From: Brion Vibber Date: Mon, 4 Oct 2010 19:54:36 +0000 (-0700) Subject: Merge branch '0.9.x' of gitorious.org:statusnet/mainline into 1.0.x X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=59119482ca34540bd7f0a2a1aa994de1d5328ea2;hp=-c;p=quix0rs-gnu-social.git Merge branch '0.9.x' of gitorious.org:statusnet/mainline into 1.0.x Conflicts: actions/hostmeta.php actions/imsettings.php classes/User.php lib/adminpanelaction.php lib/channel.php lib/default.php lib/router.php lib/util.php --- 59119482ca34540bd7f0a2a1aa994de1d5328ea2 diff --combined EVENTS.txt index 789c985ae3,2496416173..58189b9c3f --- a/EVENTS.txt +++ b/EVENTS.txt @@@ -258,10 -258,28 +258,28 @@@ EndShowExportData: just after showing t - $action: action object being shown StartShowNoticeItem: just before showing the notice item - - $action: action object being shown + - $item: The NoticeListItem object being shown EndShowNoticeItem: just after showing the notice item - - $action: action object being shown + - $item: the NoticeListItem object being shown + + StartShowNoticeInfo: just before showing notice info + - $item: The NoticeListItem object being shown + + EndShowNoticeInfo: just after showing notice info + - $item: The NoticeListItem object being shown + + StartShowNoticeOptions: just before showing notice options like fave, repeat, etc. + - $item: the NoticeListItem object being shown + + EndShowNoticeOptions: just after showing notice options like fave, repeat, etc. + - $item: the NoticeListItem object being shown + + StartShowFaveForm: just before showing the fave form + - $item: the NoticeListItem object being shown + + EndShowFaveForm: just after showing the fave form + - $item: the NoticeListItem object being shown StartShowPageNotice: just before showing the page notice (instructions or error) - $action: action object being shown @@@ -551,12 -569,6 +569,12 @@@ EndPublicXRDS: End XRDS output (right b - $action: the current action - &$xrdsoutputter - XRDSOutputter object to write to +StartHostMetaLinks: Start /.well-known/host-meta links +- &links: array containing the links elements to be written + +EndHostMetaLinks: End /.well-known/host-meta links +- &links: array containing the links elements to be written + StartCheckPassword: Check a username/password - $nickname: The nickname to check - $password: The password to check @@@ -722,24 -734,6 +740,24 @@@ StartShowContentLicense: Showing the de EndShowContentLicense: Showing the default license for content - $action: the current action +GetImTransports: Get IM transports that are available +- &$transports: append your transport to this array like so: $transports[transportName]=array('display'=>display) + +NormalizeImScreenname: Normalize an IM screenname +- $transport: transport the screenname is on +- &$screenname: screenname to be normalized + +ValidateImScreenname: Validate an IM screenname +- $transport: transport the screenname is on +- $screenname: screenname to be validated +- $valid: is the screenname valid? + +SendImConfirmationCode: Send a confirmation code to confirm a user owns an IM screenname +- $transport: transport the screenname exists on +- $screenname: screenname being confirmed +- $code: confirmation code for confirmation URL +- $user: user requesting the confirmation + StartUserRegister: When a new user is being registered - &$profile: new profile data (no ID) - &$user: new user account (no ID or URI) @@@ -780,6 -774,22 +798,22 @@@ EndDisfavorNotice: After saving a notic - $profile: profile of the person faving (can be remote!) - $notice: notice being faved + StartFavorNoticeForm: starting the data in the form for favoring a notice + - $FavorForm: the favor form being shown + - $notice: notice being favored + + EndFavorNoticeForm: Ending the data in the form for favoring a notice + - $FavorForm: the favor form being shown + - $notice: notice being favored + + StartDisFavorNoticeForm: starting the data in the form for disfavoring a notice + - $DisfavorForm: the disfavor form being shown + - $notice: notice being difavored + + EndDisFavorNoticeForm: Ending the data in the form for disfavoring a notice + - $DisfavorForm: the disfavor form being shown + - $notice: notice being disfavored + StartFindMentions: start finding mentions in a block of text - $sender: sender profile - $text: plain text version of the notice @@@ -1110,3 -1120,19 +1144,19 @@@ StartDeleteOwnNotice: when a user start EndDeleteOwnNotice: when a user has deleted their own notice - $user: the user doing the delete - $notice: the notice being deleted + + StartShowFeedLinkList: before showing the feed list in the sidebar + - $action: action being executed + - $feeds: list of feeds to show + + EndShowFeedLinkList: after showing the feed list in the sidebar + - $action: action being executed + - $feeds: list of feeds shown + + StartShowFeedLink: before showing an individual feed item + - $action: action being executed + - $feed: feed to show + + EndShowFeedLink: after showing an individual feed + - $action: action being executed + - $feed: feed to show diff --combined actions/apiaccountupdatedeliverydevice.php index e25b9a954d,2d903cb460..e732e23560 --- a/actions/apiaccountupdatedeliverydevice.php +++ b/actions/apiaccountupdatedeliverydevice.php @@@ -83,6 -83,7 +83,7 @@@ class ApiAccountUpdateDeliveryDeviceAct if ($_SERVER['REQUEST_METHOD'] != 'POST') { $this->clientError( + // TRANS: Client error message. POST is a HTTP command. It should not be translated. _('This method requires a POST.'), 400, $this->format ); @@@ -120,16 -121,10 +121,16 @@@ if (strtolower($this->device) == 'sms') { $this->user->smsnotify = true; } elseif (strtolower($this->device) == 'im') { - $this->user->jabbernotify = true; + //TODO IM is pluginized now, so what should we do? + //Enable notifications for all IM plugins? + //For now, don't do anything + //$this->user->jabbernotify = true; } elseif (strtolower($this->device == 'none')) { $this->user->smsnotify = false; - $this->user->jabbernotify = false; + //TODO IM is pluginized now, so what should we do? + //Disable notifications for all IM plugins? + //For now, don't do anything + //$this->user->jabbernotify = false; } $result = $this->user->update($original); diff --combined actions/shownotice.php index 77ba2ce9fd,005335e3b4..1161de8636 --- a/actions/shownotice.php +++ b/actions/shownotice.php @@@ -151,6 -151,7 +151,7 @@@ class ShownoticeAction extends OwnerDes strtotime($this->avatar->modified) : 0; return 'W/"' . implode(':', array($this->arg('action'), + common_user_cache_hash(), common_language(), $this->notice->id, strtotime($this->notice->created), @@@ -277,6 -278,12 +278,6 @@@ 'content' => $id->toString())); } - if ($user->jabbermicroid && $user->jabber && $this->notice->uri) { - $id = new Microid('xmpp:', $user->jabber, - $this->notice->uri); - $this->element('meta', array('name' => 'microid', - 'content' => $id->toString())); - } $this->element('link',array('rel'=>'alternate', 'type'=>'application/json+oembed', 'href'=>common_local_url( @@@ -291,6 -298,16 +292,16 @@@ array(), array('format'=>'xml','url'=>$this->notice->uri)), 'title'=>'oEmbed'),null); + + // Extras to aid in sharing notices to Facebook + $avatar = $this->profile->getAvatar(AVATAR_PROFILE_SIZE); + $avatarUrl = ($avatar) ? + $avatar->displayUrl() : + Avatar::defaultImage($avatar_size); + $this->element('meta', array('property' => 'og:image', + 'content' => $avatarUrl)); + $this->element('meta', array('property' => 'og:description', + 'content' => $this->notice->content)); } } @@@ -307,10 -324,14 +318,14 @@@ class SingleNoticeItem extends NoticeLi function show() { $this->showStart(); - $this->showNotice(); - $this->showNoticeAttachments(); - $this->showNoticeInfo(); - $this->showNoticeOptions(); + if (Event::handle('StartShowNoticeItem', array($this))) { + $this->showNotice(); + $this->showNoticeAttachments(); + $this->showNoticeInfo(); + $this->showNoticeOptions(); + Event::handle('EndShowNoticeItem', array($this)); + } + $this->showEnd(); } diff --combined classes/File_redirection.php index 6a86197d93,68fed77e8b..92f0125a40 --- a/classes/File_redirection.php +++ b/classes/File_redirection.php @@@ -176,52 -176,22 +176,52 @@@ class File_redirection extends Memcache * @param string $long_url * @return string */ - function makeShort($long_url) { + function makeShort($long_url) + { $canon = File_redirection::_canonUrl($long_url); $short_url = File_redirection::_userMakeShort($canon); // Did we get one? Is it shorter? - if (!empty($short_url) && mb_strlen($short_url) < mb_strlen($long_url)) { + + if (!empty($short_url)) { + return $short_url; + } else { + return $long_url; + } + } + + /** + * Shorten a URL with the current user's configured shortening + * options, if applicable. + * + * If it cannot be shortened or the "short" URL is longer than the + * original, the original is returned. + * + * If the referenced item has not been seen before, embedding data + * may be saved. + * + * @param string $long_url + * @return string + */ + + function forceShort($long_url) + { + $canon = File_redirection::_canonUrl($long_url); + + $short_url = File_redirection::_userMakeShort($canon, true); + + // Did we get one? Is it shorter? + if (!empty($short_url)) { return $short_url; } else { return $long_url; } } - function _userMakeShort($long_url) { - $short_url = common_shorten_url($long_url); + function _userMakeShort($long_url, $force = false) { + $short_url = common_shorten_url($long_url, $force); if (!empty($short_url) && $short_url != $long_url) { $short_url = (string)$short_url; // store it @@@ -311,4 -281,3 +311,3 @@@ $file_redir->insert(); } } - diff --combined classes/Memcached_DataObject.php index 6feb59c341,ccfd886a1d..8ffb46cc52 --- a/classes/Memcached_DataObject.php +++ b/classes/Memcached_DataObject.php @@@ -124,7 -124,7 +124,7 @@@ class Memcached_DataObject extends Safe } static function memcache() { - return common_memcache(); + return Cache::instance(); } static function cacheKey($cls, $k, $v) { @@@ -134,7 -134,7 +134,7 @@@ str_replace("\n", " ", $e->getTraceAsString())); } $vstr = self::valueString($v); - return common_cache_key(strtolower($cls).':'.$k.':'.$vstr); + return Cache::key(strtolower($cls).':'.$k.':'.$vstr); } static function getcached($cls, $k, $v) { @@@ -189,11 -189,11 +189,11 @@@ str_replace("\n", " ", $e->getTraceAsString())); return false; } else { - $keys = $this->_allCacheKeys(); + $keys = $this->_allCacheKeys(); - foreach ($keys as $key) { - $c->set($key, $this); - } + foreach ($keys as $key) { + $c->set($key, $this); + } } } @@@ -302,8 -302,8 +302,8 @@@ $inst->query($qry); return $inst; } - $key_part = common_keyize($cls).':'.md5($qry); - $ckey = common_cache_key($key_part); + $key_part = Cache::keyize($cls).':'.md5($qry); + $ckey = Cache::key($key_part); $stored = $c->get($ckey); if ($stored !== false) { @@@ -550,7 -550,7 +550,7 @@@ $keyPart = vsprintf($format, $args); - $cacheKey = common_cache_key($keyPart); + $cacheKey = Cache::key($keyPart); return $c->delete($cacheKey); } @@@ -592,7 -592,7 +592,7 @@@ return false; } - $cacheKey = common_cache_key($keyPart); + $cacheKey = Cache::key($keyPart); return $c->get($cacheKey); } @@@ -605,7 -605,7 +605,7 @@@ return false; } - $cacheKey = common_cache_key($keyPart); + $cacheKey = Cache::key($keyPart); return $c->set($cacheKey, $value, $flag, $expiry); } @@@ -637,4 -637,3 +637,3 @@@ return $vstr; } } - diff --combined classes/Notice.php index 4c6efd3eb7,e268544b50..a8ec0529f2 --- a/classes/Notice.php +++ b/classes/Notice.php @@@ -593,7 -593,7 +593,7 @@@ class Notice extends Memcached_DataObje function getStreamByIds($ids) { - $cache = common_memcache(); + $cache = Cache::instance(); if (!empty($cache)) { $notices = array(); @@@ -745,6 -745,7 +745,7 @@@ 1, 1 ); + if ($conversation->N > 0) { return true; } @@@ -753,15 -754,22 +754,22 @@@ } /** - * @param $groups array of Group *objects* - * @param $recipients array of profile *ids* + * Pull up a full list of local recipients who will be getting + * this notice in their inbox. Results will be cached, so don't + * change the input data wily-nilly! + * + * @param array $groups optional list of Group objects; + * if left empty, will be loaded from group_inbox records + * @param array $recipient optional list of reply profile ids + * if left empty, will be loaded from reply records + * @return array associating recipient user IDs with an inbox source constant */ function whoGets($groups=null, $recipients=null) { $c = self::memcache(); if (!empty($c)) { - $ni = $c->get(common_cache_key('notice:who_gets:'.$this->id)); + $ni = $c->get(Cache::key('notice:who_gets:'.$this->id)); if ($ni !== false) { return $ni; } @@@ -787,33 -795,33 +795,33 @@@ $ni[$id] = NOTICE_INBOX_SOURCE_SUB; } - $profile = $this->getProfile(); - foreach ($groups as $group) { $users = $group->getUserMembers(); foreach ($users as $id) { if (!array_key_exists($id, $ni)) { - $user = User::staticGet('id', $id); - if (!$user->hasBlocked($profile)) { - $ni[$id] = NOTICE_INBOX_SOURCE_GROUP; - } + $ni[$id] = NOTICE_INBOX_SOURCE_GROUP; } } } foreach ($recipients as $recipient) { - if (!array_key_exists($recipient, $ni)) { - $recipientUser = User::staticGet('id', $recipient); - if (!empty($recipientUser)) { - $ni[$recipient] = NOTICE_INBOX_SOURCE_REPLY; - } + $ni[$recipient] = NOTICE_INBOX_SOURCE_REPLY; + } + } + + // Exclude any deleted, non-local, or blocking recipients. + $profile = $this->getProfile(); + foreach ($ni as $id => $source) { + $user = User::staticGet('id', $id); + if (empty($user) || $user->hasBlocked($profile)) { + unset($ni[$id]); } } if (!empty($c)) { // XXX: pack this data better - $c->set(common_cache_key('notice:who_gets:'.$this->id), $ni); + $c->set(Cache::key('notice:who_gets:'.$this->id), $ni); } return $ni; @@@ -1212,6 -1220,64 +1220,64 @@@ return $groups; } + function asActivity() + { + $profile = $this->getProfile(); + + $act = new Activity(); + + $act->actor = ActivityObject::fromProfile($profile); + $act->verb = ActivityVerb::POST; + $act->objects[] = ActivityObject::fromNotice($this); + + $act->time = strtotime($this->created); + $act->link = $this->bestUrl(); + + $act->content = common_xml_safe_str($this->rendered); + $act->id = $this->uri; + $act->title = common_xml_safe_str($this->content); + + $ctx = new ActivityContext(); + + if (!empty($this->reply_to)) { + $reply = Notice::staticGet('id', $this->reply_to); + if (!empty($reply)) { + $ctx->replyToID = $reply->uri; + $ctx->replyToUrl = $reply->bestUrl(); + } + } + + $ctx->location = $this->getLocation(); + + $conv = null; + + if (!empty($this->conversation)) { + $conv = Conversation::staticGet('id', $this->conversation); + if (!empty($conv)) { + $ctx->conversation = $conv->uri; + } + } + + $reply_ids = $this->getReplies(); + + foreach ($reply_ids as $id) { + $profile = Profile::staticGet('id', $id); + if (!empty($profile)) { + $ctx->attention[] = $profile->getUri(); + } + } + + $groups = $this->getGroups(); + + foreach ($groups as $group) { + $ctx->attention[] = $group->uri; + } + + $act->context = $ctx; + + return $act; + } + // This has gotten way too long. Needs to be sliced up into functional bits // or ideally exported to a utility class. @@@ -1240,13 -1306,10 +1306,10 @@@ } if (Event::handle('StartActivitySource', array(&$this, &$xs))) { - if ($source) { - $atom_feed = $profile->getAtomFeed(); if (!empty($atom_feed)) { - $xs->elementStart('source'); // XXX: we should store the actual feed ID @@@ -1587,7 -1650,7 +1650,7 @@@ function stream($fn, $args, $cachekey, $offset=0, $limit=20, $since_id=0, $max_id=0) { - $cache = common_memcache(); + $cache = Cache::instance(); if (empty($cache) || $since_id != 0 || $max_id != 0 || @@@ -1597,7 -1660,7 +1660,7 @@@ $max_id))); } - $idkey = common_cache_key($cachekey); + $idkey = Cache::key($cachekey); $idstr = $cache->get($idkey); @@@ -1779,17 -1842,17 +1842,17 @@@ function repeatStream($limit=100) { - $cache = common_memcache(); + $cache = Cache::instance(); if (empty($cache)) { $ids = $this->_repeatStreamDirect($limit); } else { - $idstr = $cache->get(common_cache_key('notice:repeats:'.$this->id)); + $idstr = $cache->get(Cache::key('notice:repeats:'.$this->id)); if ($idstr !== false) { $ids = explode(',', $idstr); } else { $ids = $this->_repeatStreamDirect(100); - $cache->set(common_cache_key('notice:repeats:'.$this->id), implode(',', $ids)); + $cache->set(Cache::key('notice:repeats:'.$this->id), implode(',', $ids)); } if ($limit < 100) { // We do a max of 100, so slice down to limit @@@ -1834,7 -1897,6 +1897,6 @@@ $options = array(); if (!empty($location_id) && !empty($location_ns)) { - $options['location_id'] = $location_id; $options['location_ns'] = $location_ns; @@@ -1846,7 -1908,6 +1908,6 @@@ } } else if (!empty($lat) && !empty($lon)) { - $options['lat'] = $lat; $options['lon'] = $lon; @@@ -1857,7 -1918,6 +1918,6 @@@ $options['location_ns'] = $location->location_ns; } } else if (!empty($profile)) { - if (isset($profile->lat) && isset($profile->lon)) { $options['lat'] = $profile->lat; $options['lon'] = $profile->lon; @@@ -1943,10 -2003,10 +2003,10 @@@ if ($tag->find()) { while ($tag->fetch()) { - self::blow('profile:notice_ids_tagged:%d:%s', $this->profile_id, common_keyize($tag->tag)); - self::blow('profile:notice_ids_tagged:%d:%s;last', $this->profile_id, common_keyize($tag->tag)); - self::blow('notice_tag:notice_ids:%s', common_keyize($tag->tag)); - self::blow('notice_tag:notice_ids:%s;last', common_keyize($tag->tag)); + self::blow('profile:notice_ids_tagged:%d:%s', $this->profile_id, Cache::keyize($tag->tag)); + self::blow('profile:notice_ids_tagged:%d:%s;last', $this->profile_id, Cache::keyize($tag->tag)); + self::blow('notice_tag:notice_ids:%s', Cache::keyize($tag->tag)); + self::blow('notice_tag:notice_ids:%s;last', Cache::keyize($tag->tag)); $tag->delete(); } } @@@ -1974,6 -2034,7 +2034,7 @@@ { // We always insert for the author so they don't // have to wait + Event::handle('StartNoticeDistribute', array($this)); $user = User::staticGet('id', $this->profile_id); if (!empty($user)) { diff --combined classes/Profile.php index 230b3aa3a0,3844077e62..1d130c44ad --- a/classes/Profile.php +++ b/classes/Profile.php @@@ -103,7 -103,6 +103,6 @@@ class Profile extends Memcached_DataObj foreach (array(AVATAR_PROFILE_SIZE, AVATAR_STREAM_SIZE, AVATAR_MINI_SIZE) as $size) { # We don't do a scaled one if original is our scaled size if (!($avatar->width == $size && $avatar->height == $size)) { - $scaled_filename = $imagefile->resize($size); //$scaled = DB_DataObject::factory('avatar'); @@@ -429,10 -428,10 +428,10 @@@ function subscriptionCount() { - $c = common_memcache(); + $c = Cache::instance(); if (!empty($c)) { - $cnt = $c->get(common_cache_key('profile:subscription_count:'.$this->id)); + $cnt = $c->get(Cache::key('profile:subscription_count:'.$this->id)); if (is_integer($cnt)) { return (int) $cnt; } @@@ -446,7 -445,7 +445,7 @@@ $cnt = ($cnt > 0) ? $cnt - 1 : $cnt; if (!empty($c)) { - $c->set(common_cache_key('profile:subscription_count:'.$this->id), $cnt); + $c->set(Cache::key('profile:subscription_count:'.$this->id), $cnt); } return $cnt; @@@ -454,9 -453,9 +453,9 @@@ function subscriberCount() { - $c = common_memcache(); + $c = Cache::instance(); if (!empty($c)) { - $cnt = $c->get(common_cache_key('profile:subscriber_count:'.$this->id)); + $cnt = $c->get(Cache::key('profile:subscriber_count:'.$this->id)); if (is_integer($cnt)) { return (int) $cnt; } @@@ -468,17 -467,52 +467,52 @@@ $cnt = (int) $sub->count('distinct subscriber'); if (!empty($c)) { - $c->set(common_cache_key('profile:subscriber_count:'.$this->id), $cnt); + $c->set(Cache::key('profile:subscriber_count:'.$this->id), $cnt); } return $cnt; } + function hasFave($notice) + { - $cache = common_memcache(); ++ $cache = Cache::instance(); + + // XXX: Kind of a hack. + + if (!empty($cache)) { + // This is the stream of favorite notices, in rev chron + // order. This forces it into cache. + + $ids = Fave::stream($this->id, 0, NOTICE_CACHE_WINDOW); + + // If it's in the list, then it's a fave + + if (in_array($notice->id, $ids)) { + return true; + } + + // If we're not past the end of the cache window, + // then the cache has all available faves, so this one + // is not a fave. + + if (count($ids) < NOTICE_CACHE_WINDOW) { + return false; + } + + // Otherwise, cache doesn't have all faves; + // fall through to the default + } + + $fave = Fave::pkeyGet(array('user_id' => $this->id, + 'notice_id' => $notice->id)); + return ((is_null($fave)) ? false : true); + } + function faveCount() { - $c = common_memcache(); + $c = Cache::instance(); if (!empty($c)) { - $cnt = $c->get(common_cache_key('profile:fave_count:'.$this->id)); + $cnt = $c->get(Cache::key('profile:fave_count:'.$this->id)); if (is_integer($cnt)) { return (int) $cnt; } @@@ -489,7 -523,7 +523,7 @@@ $cnt = (int) $faves->count('distinct notice_id'); if (!empty($c)) { - $c->set(common_cache_key('profile:fave_count:'.$this->id), $cnt); + $c->set(Cache::key('profile:fave_count:'.$this->id), $cnt); } return $cnt; @@@ -497,10 -531,10 +531,10 @@@ function noticeCount() { - $c = common_memcache(); + $c = Cache::instance(); if (!empty($c)) { - $cnt = $c->get(common_cache_key('profile:notice_count:'.$this->id)); + $cnt = $c->get(Cache::key('profile:notice_count:'.$this->id)); if (is_integer($cnt)) { return (int) $cnt; } @@@ -511,41 -545,55 +545,55 @@@ $cnt = (int) $notices->count('distinct id'); if (!empty($c)) { - $c->set(common_cache_key('profile:notice_count:'.$this->id), $cnt); + $c->set(Cache::key('profile:notice_count:'.$this->id), $cnt); } return $cnt; } + function blowFavesCache() + { + $cache = common_memcache(); + if ($cache) { + // Faves don't happen chronologically, so we need to blow + // ;last cache, too + $cache->delete(common_cache_key('fave:ids_by_user:'.$this->id)); + $cache->delete(common_cache_key('fave:ids_by_user:'.$this->id.';last')); + $cache->delete(common_cache_key('fave:ids_by_user_own:'.$this->id)); + $cache->delete(common_cache_key('fave:ids_by_user_own:'.$this->id.';last')); + } + $this->blowFaveCount(); + } + function blowSubscriberCount() { - $c = common_memcache(); + $c = Cache::instance(); if (!empty($c)) { - $c->delete(common_cache_key('profile:subscriber_count:'.$this->id)); + $c->delete(Cache::key('profile:subscriber_count:'.$this->id)); } } function blowSubscriptionCount() { - $c = common_memcache(); + $c = Cache::instance(); if (!empty($c)) { - $c->delete(common_cache_key('profile:subscription_count:'.$this->id)); + $c->delete(Cache::key('profile:subscription_count:'.$this->id)); } } function blowFaveCount() { - $c = common_memcache(); + $c = Cache::instance(); if (!empty($c)) { - $c->delete(common_cache_key('profile:fave_count:'.$this->id)); + $c->delete(Cache::key('profile:fave_count:'.$this->id)); } } function blowNoticeCount() { - $c = common_memcache(); + $c = Cache::instance(); if (!empty($c)) { - $c->delete(common_cache_key('profile:notice_count:'.$this->id)); + $c->delete(Cache::key('profile:notice_count:'.$this->id)); } } @@@ -790,13 -838,14 +838,14 @@@ * @param $right string Name of the right, usually a constant in class Right * @return boolean whether the user has the right in question */ - function hasRight($right) { $result = false; + if ($this->hasRole(Profile_role::DELETED)) { return false; } + if (Event::handle('UserRightsCheck', array($this, $right, &$result))) { switch ($right) { diff --combined classes/User.php index c15ddc9dc5,e784fd9e9a..259df7e2c3 --- a/classes/User.php +++ b/classes/User.php @@@ -48,6 -48,11 +48,6 @@@ class User extends Memcached_DataObjec public $language; // varchar(50) public $timezone; // varchar(50) public $emailpost; // tinyint(1) default_1 - public $jabber; // varchar(255) unique_key - public $jabbernotify; // tinyint(1) - public $jabberreplies; // tinyint(1) - public $jabbermicroid; // tinyint(1) default_1 - public $updatefrompresence; // tinyint(1) public $sms; // varchar(64) unique_key public $carrier; // int(4) public $smsnotify; // tinyint(1) @@@ -88,7 -93,7 +88,7 @@@ { $this->_connect(); $parts = array(); - foreach (array('nickname', 'email', 'jabber', 'incomingemail', 'sms', 'carrier', 'smsemail', 'language', 'timezone') as $k) { + foreach (array('nickname', 'email', 'incomingemail', 'sms', 'carrier', 'smsemail', 'language', 'timezone') as $k) { if (strcmp($this->$k, $orig->$k) != 0) { $parts[] = $k . ' = ' . $this->_quote($this->$k); } @@@ -250,6 -255,19 +250,19 @@@ $user->inboxed = 1; + // Set default-on options here, otherwise they'll be disabled + // initially for sites using caching, since the initial encache + // doesn't know about the defaults in the database. + $user->emailnotifysub = 1; + $user->emailnotifyfav = 1; + $user->emailnotifynudge = 1; + $user->emailnotifymsg = 1; + $user->emailnotifyattn = 1; + $user->emailmicroid = 1; + $user->emailpost = 1; + $user->jabbermicroid = 1; + $user->viewdesigns = 1; + $user->created = common_sql_now(); if (Event::handle('StartUserRegister', array(&$user, &$profile))) { @@@ -264,7 -282,13 +277,13 @@@ } $user->id = $id; - $user->uri = common_user_uri($user); + + if (!empty($uri)) { + $user->uri = $uri; + } else { + $user->uri = common_user_uri($user); + } + if (!empty($password)) { // may not have a password for OpenID users $user->password = common_munge_password($password, $id); } @@@ -388,37 -412,8 +407,8 @@@ function hasFave($notice) { - $cache = Cache::instance(); - - // XXX: Kind of a hack. - - if ($cache) { - // This is the stream of favorite notices, in rev chron - // order. This forces it into cache. - - $ids = Fave::stream($this->id, 0, NOTICE_CACHE_WINDOW); - - // If it's in the list, then it's a fave - - if (in_array($notice->id, $ids)) { - return true; - } - - // If we're not past the end of the cache window, - // then the cache has all available faves, so this one - // is not a fave. - - if (count($ids) < NOTICE_CACHE_WINDOW) { - return false; - } - - // Otherwise, cache doesn't have all faves; - // fall through to the default - } - - $fave = Fave::pkeyGet(array('user_id' => $this->id, - 'notice_id' => $notice->id)); - return ((is_null($fave)) ? false : true); + $profile = $this->getProfile(); + return $profile->hasFave($notice); } function mutuallySubscribed($other) @@@ -487,17 -482,8 +477,8 @@@ function blowFavesCache() { - $cache = Cache::instance(); - if ($cache) { - // Faves don't happen chronologically, so we need to blow - // ;last cache, too - $cache->delete(Cache::key('fave:ids_by_user:'.$this->id)); - $cache->delete(Cache::key('fave:ids_by_user:'.$this->id.';last')); - $cache->delete(Cache::key('fave:ids_by_user_own:'.$this->id)); - $cache->delete(Cache::key('fave:ids_by_user_own:'.$this->id.';last')); - } $profile = $this->getProfile(); - $profile->blowFaveCount(); + $profile->blowFavesCache(); } function getSelfTags() @@@ -547,6 -533,9 +528,9 @@@ if (Subscription::exists($other, $self)) { Subscription::cancel($other, $self); } + if (Subscription::exists($self, $other)) { + Subscription::cancel($self, $other); + } $block->query('COMMIT'); diff --combined lib/adminpanelaction.php index 9e0b2d041b,fae9f4fa57..8dd16e9d0c --- a/lib/adminpanelaction.php +++ b/lib/adminpanelaction.php @@@ -44,7 -44,6 +44,6 @@@ if (!defined('STATUSNET')) * * @todo Find some commonalities with SettingsAction and combine */ - class AdminPanelAction extends Action { var $success = true; @@@ -61,7 -60,6 +60,6 @@@ * * @return boolean success flag */ - function prepare($args) { parent::prepare($args); @@@ -124,7 -122,6 +122,6 @@@ * * @return void */ - function handle($args) { if ($_SERVER['REQUEST_METHOD'] == 'POST') { @@@ -155,7 -152,6 +152,6 @@@ * @return void * @see AdminPanelNav */ - function showLocalNav() { $nav = new AdminPanelNav($this); @@@ -169,7 -165,6 +165,6 @@@ * * @return void. */ - function showContent() { $this->showForm(); @@@ -199,7 -194,6 +194,6 @@@ * * @return void */ - function showPageNotice() { if ($this->msg) { @@@ -222,7 -216,6 +216,6 @@@ * * @return void */ - function showForm() { // TRANS: Client error message. @@@ -239,7 -232,6 +232,6 @@@ * * @return void */ - function getInstructions() { return ''; @@@ -252,7 -244,6 +244,6 @@@ * * @return void */ - function saveSettings() { // TRANS: Client error message @@@ -267,7 -258,6 +258,6 @@@ * * @return mixed $result false if something didn't work */ - function deleteSetting($section, $setting) { $config = new Config(); @@@ -314,7 -304,6 +304,6 @@@ * * @see Widget */ - class AdminPanelNav extends Widget { var $action = null; @@@ -324,7 -313,6 +313,6 @@@ * * @param Action $action current action, used for output */ - function __construct($action=null) { parent::__construct($action); @@@ -336,7 -324,6 +324,6 @@@ * * @return void */ - function show() { $action_name = $this->action->trimmed('action'); @@@ -409,17 -396,16 +396,24 @@@ $menu_title, $action_name == 'snapshotadminpanel', 'nav_snapshot_admin_panel'); } + if (AdminPanelAction::canAdmin('license')) { + // TRANS: Menu item title/tooltip + $menu_title = _('Set site license'); + // TRANS: Menu item for site administration + $this->out->menuItem(common_local_url('licenseadminpanel'), _('License'), + $menu_title, $action_name == 'licenseadminpanel', 'nav_license_admin_panel'); + } + + if (AdminPanelAction::canAdmin('plugins')) { + // TRANS: Menu item title/tooltip + $menu_title = _('Plugins configuration'); + // TRANS: Menu item for site administration + $this->out->menuItem(common_local_url('pluginsadminpanel'), _('Plugins'), + $menu_title, $action_name == 'pluginsadminpanel', 'nav_design_admin_panel'); + } + Event::handle('EndAdminPanelNav', array($this)); } $this->action->elementEnd('ul'); } - } diff --combined lib/apiaction.php index b4252db95a,0ebf88282a..d8249055a4 --- a/lib/apiaction.php +++ b/lib/apiaction.php @@@ -98,8 -98,6 +98,8 @@@ if (!defined('STATUSNET')) exit(1); } +class ApiValidationException extends Exception { } + /** * Contains most of the Twitter-compatible API output functions. * @@@ -114,7 -112,6 +114,6 @@@ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ - class ApiAction extends Action { const READ_ONLY = 1; @@@ -141,7 -138,6 +140,6 @@@ * * @return boolean false if user doesn't exist */ - function prepare($args) { StatusNet::setApi(true); // reduce exception reports to aid in debugging @@@ -174,7 -170,6 +172,6 @@@ * * @return void */ - function handle($args) { header('Access-Control-Allow-Origin: *'); @@@ -467,7 -462,6 +464,6 @@@ $entry = array(); if (Event::handle('StartRssEntryArray', array($notice, &$entry))) { - $profile = $notice->getProfile(); // We trim() to avoid extraneous whitespace in the output @@@ -557,7 -551,6 +553,6 @@@ $notifications = false; if ($source->isSubscribed($target)) { - $sub = Subscription::pkeyGet(array('subscriber' => $source->id, 'subscribed' => $target->id)); @@@ -743,7 -736,6 +738,6 @@@ function showXmlTimeline($notice) { - $this->initDocument('xml'); $this->elementStart('statuses', array('type' => 'array', 'xmlns:statusnet' => 'http://status.net/schema/api/1/')); @@@ -768,7 -760,6 +762,6 @@@ function showRssTimeline($notice, $title, $link, $subtitle, $suplink = null, $logo = null, $self = null) { - $this->initDocument('rss'); $this->element('title', null, $title); @@@ -824,7 -815,6 +817,6 @@@ function showAtomTimeline($notice, $title, $id, $link, $subtitle=null, $suplink=null, $selfuri=null, $logo=null) { - $this->initDocument('atom'); $this->element('title', null, $title); @@@ -864,12 -854,10 +856,10 @@@ } $this->endDocument('atom'); - } function showRssGroups($group, $title, $link, $subtitle) { - $this->initDocument('rss'); $this->element('title', null, $title); @@@ -1017,7 -1005,6 +1007,6 @@@ function showAtomGroups($group, $title, $id, $link, $subtitle=null, $selfuri=null) { - $this->initDocument('atom'); $this->element('title', null, common_xml_safe_str($title)); @@@ -1048,7 -1035,6 +1037,6 @@@ function showJsonTimeline($notice) { - $this->initDocument('json'); $statuses = array(); @@@ -1074,7 -1060,6 +1062,6 @@@ function showJsonGroups($group) { - $this->initDocument('json'); $groups = array(); @@@ -1120,7 -1105,6 +1107,6 @@@ function showTwitterXmlUsers($user) { - $this->initDocument('xml'); $this->elementStart('users', array('type' => 'array', 'xmlns:statusnet' => 'http://status.net/schema/api/1/')); @@@ -1143,7 -1127,6 +1129,6 @@@ function showJsonUsers($user) { - $this->initDocument('json'); $users = array(); @@@ -1228,7 -1211,6 +1213,6 @@@ $this->endXML(); break; case 'json': - // Check for JSONP callback if (isset($this->callback)) { print ')'; @@@ -1374,7 -1356,6 +1358,6 @@@ function getTargetUser($id) { if (empty($id)) { - // Twitter supports these other ways of passing the user ID if (is_numeric($this->arg('id'))) { return User::staticGet($this->arg('id')); @@@ -1485,7 -1466,6 +1468,6 @@@ */ function arg($key, $def=null) { - // XXX: Do even more input validation/scrubbing? if (array_key_exists($key, $this->args)) { @@@ -1552,5 -1532,4 +1534,4 @@@ return $uri; } - } diff --combined lib/cache.php index 17cc5f0472,ea0ff769d1..3d78c79adb --- a/lib/cache.php +++ b/lib/cache.php @@@ -41,7 -41,6 +41,6 @@@ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 * @link http://status.net/ */ - class Cache { var $_items = array(); @@@ -56,7 -55,6 +55,6 @@@ * * @return Cache cache object */ - static function instance() { if (is_null(self::$_inst)) { @@@ -77,13 -75,12 +75,12 @@@ * * @return string full key */ - static function key($extra) { $base_key = common_config('cache', 'base'); if (empty($base_key)) { - $base_key = common_keyize(common_config('site', 'name')); + $base_key = self::keyize(common_config('site', 'name')); } return 'statusnet:' . $base_key . ':' . $extra; @@@ -98,7 -95,6 +95,6 @@@ * * @return string keyized string */ - static function keyize($str) { $str = strtolower($str); @@@ -115,7 -111,6 +111,6 @@@ * * @return string retrieved value or null if unfound */ - function get($key) { $value = false; @@@ -140,7 -135,6 +135,6 @@@ * * @return boolean success flag */ - function set($key, $value, $flag=null, $expiry=null) { $success = false; @@@ -192,7 -186,6 +186,6 @@@ * * @return boolean success flag */ - function delete($key) { $success = false; @@@ -214,7 -207,6 +207,6 @@@ * * @return boolean success flag */ - function reconnect() { $success = false; diff --combined lib/channel.php index 5b38a4b6ae,fbc2e8697c..ae9b2d214f --- a/lib/channel.php +++ b/lib/channel.php @@@ -19,6 -19,9 +19,9 @@@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } + /** + * @todo Needs documentation. + */ class Channel { function on($user) @@@ -66,6 -69,62 +69,6 @@@ class CLIChannel extends Channe } } -class XMPPChannel extends Channel -{ - var $conn = null; - - function source() - { - return 'xmpp'; - } - - function __construct($conn) - { - $this->conn = $conn; - } - - function on($user) - { - return $this->set_notify($user, 1); - } - - function off($user) - { - return $this->set_notify($user, 0); - } - - function output($user, $text) - { - $text = '['.common_config('site', 'name') . '] ' . $text; - jabber_send_message($user->jabber, $text); - } - - function error($user, $text) - { - $text = '['.common_config('site', 'name') . '] ' . $text; - jabber_send_message($user->jabber, $text); - } - - function set_notify(&$user, $notify) - { - $orig = clone($user); - $user->jabbernotify = $notify; - $result = $user->update($orig); - if (!$result) { - $last_error = &PEAR::getStaticProperty('DB_DataObject','lastError'); - common_log(LOG_ERR, - 'Could not set notify flag to ' . $notify . - ' for user ' . common_log_objstring($user) . - ': ' . $last_error->message); - return false; - } else { - common_log(LOG_INFO, - 'User ' . $user->nickname . ' set notify flag to ' . $notify); - return true; - } - } -} - class WebChannel extends Channel { var $out = null; @@@ -97,6 -156,7 +100,7 @@@ # depending on what command was run $this->out->startHTML(); $this->out->elementStart('head'); + // TRANS: Title for command results. $this->out->element('title', null, _('Command results')); $this->out->elementEnd('head'); $this->out->elementStart('body'); @@@ -117,6 -177,7 +121,7 @@@ class AjaxWebChannel extends WebChanne { $this->out->startHTML('text/xml;charset=utf-8'); $this->out->elementStart('head'); + // TRANS: Title for command results. $this->out->element('title', null, _('Command results')); $this->out->elementEnd('head'); $this->out->elementStart('body'); @@@ -129,7 -190,8 +134,8 @@@ { $this->out->startHTML('text/xml;charset=utf-8'); $this->out->elementStart('head'); - $this->out->element('title', null, _('Ajax Error')); + // TRANS: Title for command results. + $this->out->element('title', null, _('AJAX error')); $this->out->elementEnd('head'); $this->out->elementStart('body'); $this->out->element('p', array('id' => 'error'), $text); @@@ -140,7 -202,6 +146,6 @@@ class MailChannel extends Channel { - var $addr = null; function source() @@@ -155,20 -216,20 +160,20 @@@ function on($user) { - return $this->set_notify($user, 1); + return $this->setNotify($user, 1); } function off($user) { - return $this->set_notify($user, 0); + return $this->setNotify($user, 0); } function output($user, $text) { - $headers['From'] = $user->incomingemail; $headers['To'] = $this->addr; + // TRANS: E-mail subject when a command has completed. $headers['Subject'] = _('Command complete'); return mail_send(array($this->addr), $headers, $text); @@@ -176,16 -237,16 +181,16 @@@ function error($user, $text) { - $headers['From'] = $user->incomingemail; $headers['To'] = $this->addr; + // TRANS: E-mail subject when a command has failed. $headers['Subject'] = _('Command failed'); return mail_send(array($this->addr), $headers, $text); } - function set_notify($user, $value) + function setNotify($user, $value) { $orig = clone($user); $user->smsnotify = $value; diff --combined lib/command.php index 90a321ad32,658262a090..efe917fb11 --- a/lib/command.php +++ b/lib/command.php @@@ -23,7 -23,6 +23,6 @@@ require_once(INSTALLDIR.'/lib/channel.p class Command { - var $user = null; function __construct($user=null) @@@ -49,7 -48,6 +48,6 @@@ } } - /** * Override this with the meat! * @@@ -313,7 -311,6 +311,6 @@@ class FavCommand extends Comman // TRANS: Text shown when a notice has been marked as favourite successfully. $channel->output($this->user, _('Notice marked as fave.')); } - } class JoinCommand extends Command @@@ -361,8 -358,8 +358,8 @@@ $cur->nickname, $group->nickname)); } - } + class DropCommand extends Command { var $other = null; @@@ -409,7 -406,6 +406,6 @@@ $cur->nickname, $group->nickname)); } - } class WhoisCommand extends Command @@@ -471,6 -467,7 +467,7 @@@ class MessageCommand extends Comman throw $e; } // TRANS: Command exception text shown when trying to send a direct message to a remote user (a user not registered at the current server). + // TRANS: %s is a remote profile. throw new CommandException(sprintf(_('%s is a remote profile; you can only send direct messages to users on the same server.'), $this->other)); } @@@ -611,7 -608,6 +608,6 @@@ class ReplyCommand extends Comman class GetCommand extends Command { - var $other = null; function __construct($user, $other) @@@ -638,7 -634,6 +634,6 @@@ class SubCommand extends Command { - var $other = null; function __construct($user, $other) @@@ -678,7 -673,6 +673,6 @@@ class UnsubCommand extends Command { - var $other = null; function __construct($user, $other) @@@ -712,6 -706,7 +706,7 @@@ class OffCommand extends Command { var $other = null; + function __construct($user, $other=null) { parent::__construct($user); @@@ -719,7 -714,7 +714,7 @@@ } function handle($channel) { - if ($other) { + if ($this->other) { // TRANS: Error text shown when issuing the command "off" with a setting which has not yet been implemented. $channel->error($this->user, _("Command not yet implemented.")); } else { @@@ -745,7 -740,7 +740,7 @@@ class OnCommand extends Comman function handle($channel) { - if ($other) { + if ($this->other) { // TRANS: Error text shown when issuing the command "on" with a setting which has not yet been implemented. $channel->error($this->user, _("Command not yet implemented.")); } else { @@@ -831,7 -826,7 +826,7 @@@ class SubscriptionsCommand extends Comm $out=_('You are not subscribed to anyone.'); }else{ // TRANS: Text shown after requesting other users a user is subscribed to. - // TRANS: This message support plural forms. This message is followed by a + // TRANS: This message supports plural forms. This message is followed by a // TRANS: hard coded space and a comma separated list of subscribed users. $out = ngettext('You are subscribed to this person:', 'You are subscribed to these people:', @@@ -858,7 -853,7 +853,7 @@@ class SubscribersCommand extends Comman $out=_('No one is subscribed to you.'); }else{ // TRANS: Text shown after requesting other users that are subscribed to a user (followers). - // TRANS: This message support plural forms. This message is followed by a + // TRANS: This message supports plural forms. This message is followed by a // TRANS: hard coded space and a comma separated list of subscribing users. $out = ngettext('This person is subscribed to you:', 'These people are subscribed to you:', @@@ -885,7 -880,7 +880,7 @@@ class GroupsCommand extends Comman $out=_('You are not a member of any groups.'); }else{ // TRANS: Text shown after requesting groups a user is subscribed to. - // TRANS: This message support plural forms. This message is followed by a + // TRANS: This message supports plural forms. This message is followed by a // TRANS: hard coded space and a comma separated list of subscribed groups. $out = ngettext('You are a member of this group:', 'You are a member of these groups:', @@@ -900,8 -895,8 +895,8 @@@ class HelpCommand extends Comman { function handle($channel) { - // TRANS: Help text for commands. $channel->output($this->user, + // TRANS: Help text for commands. Do not translate the command names themselves; they are fixed strings. _("Commands:\n". "on - turn on notifications\n". "off - turn off notifications\n". diff --combined lib/common.php index 0a0f5c6317,236f2d68a7..2a11ab722d --- a/lib/common.php +++ b/lib/common.php @@@ -71,7 -71,6 +71,7 @@@ if (!function_exists('dl')) # global configuration object require_once('PEAR.php'); +require_once('PEAR/Exception.php'); require_once('DB/DataObject.php'); require_once('DB/DataObject/Cast.php'); # for dates @@@ -128,33 -127,22 +128,39 @@@ require_once INSTALLDIR.'/lib/subs.php' require_once INSTALLDIR.'/lib/clientexception.php'; require_once INSTALLDIR.'/lib/serverexception.php'; + +//set PEAR error handling to use regular PHP exceptions +function PEAR_ErrorToPEAR_Exception($err) +{ + //DB_DataObject throws error when an empty set would be returned + //That behavior is weird, and not how the rest of StatusNet works. + //So just ignore those errors. + if ($err->getCode() == DB_DATAOBJECT_ERROR_NODATA) { + return; + } + if ($err->getCode()) { + throw new PEAR_Exception($err->getMessage(), $err->getCode()); + } + throw new PEAR_Exception($err->getMessage()); +} +PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'PEAR_ErrorToPEAR_Exception'); + try { StatusNet::init(@$server, @$path, @$conffile); } catch (NoConfigException $e) { // XXX: Throw a conniption if database not installed // XXX: Find a way to use htmlwriter for this instead of handcoded markup + // TRANS: Error message displayed when no configuration file was found for a StatusNet installation. echo '

'. _('No configuration file found. ') .'

'; - echo '

'. _('I looked for configuration files in the following places: ') .'
'; - echo implode($e->configFiles, '
'); + // TRANS: Error message displayed when no configuration file was found for a StatusNet installation. + // TRANS: Is followed by a list of directories (separated by HTML breaks). + echo '

'. _('I looked for configuration files in the following places: ') .'
'; + echo implode($e->configFiles, '
'); + // TRANS: Error message displayed when no configuration file was found for a StatusNet installation. echo '

'. _('You may wish to run the installer to fix this.') .'

'; + // @todo FIXME Link should be in a para? + // TRANS: Error message displayed when no configuration file was found for a StatusNet installation. + // TRANS: The text is link text that leads to the installer page. echo ''. _('Go to the installer.') .''; exit; } - diff --combined lib/connectsettingsaction.php index 5d62fc56b3,bb2e86176a..c2e759f0f3 --- a/lib/connectsettingsaction.php +++ b/lib/connectsettingsaction.php @@@ -44,7 -44,6 +44,6 @@@ require_once INSTALLDIR.'/lib/settingsa * * @see Widget */ - class ConnectSettingsAction extends SettingsAction { /** @@@ -54,7 -53,6 +53,6 @@@ * * @return void */ - function showLocalNav() { $menu = new ConnectSettingsNav($this); @@@ -73,7 -71,6 +71,6 @@@ * * @see HTMLOutputter */ - class ConnectSettingsNav extends Widget { var $action = null; @@@ -83,7 -80,6 +80,6 @@@ * * @param Action $action current action, used for output */ - function __construct($action=null) { parent::__construct($action); @@@ -95,7 -91,6 +91,6 @@@ * * @return void */ - function show() { $action_name = $this->action->trimmed('action'); @@@ -105,21 -100,25 +100,27 @@@ # action => array('prompt', 'title') $menu = array(); - if (common_config('xmpp', 'enabled')) { + $transports = array(); + Event::handle('GetImTransports', array(&$transports)); + if ($transports) { $menu['imsettings'] = - array(_('IM'), + // TRANS: Menu item for Instant Messaging settings. + array(_m('MENU','IM'), + // TRANS: Tooltip for Instant Messaging menu item. _('Updates by instant messenger (IM)')); } if (common_config('sms', 'enabled')) { $menu['smssettings'] = - array(_('SMS'), + // TRANS: Menu item for Short Message Service settings. + array(_m('MENU','SMS'), + // TRANS: Tooltip for Short Message Service menu item. _('Updates by SMS')); } - + $menu['oauthconnectionssettings'] = array( - _('Connections'), + // TRANS: Menu item for OAth connection settings. + _m('MENU','Connections'), + // TRANS: Tooltip for connected applications (Connections through OAth) menu item. _('Authorized connected applications') ); @@@ -135,6 -134,4 +136,4 @@@ $this->action->elementEnd('ul'); } - } - diff --combined lib/default.php index 76e4e44cf2,45e35e83d3..79b54fc3bd --- a/lib/default.php +++ b/lib/default.php @@@ -297,12 -297,11 +297,13 @@@ $default 'OStatus' => null, 'WikiHashtags' => null, 'RSSCloud' => null, + 'ClientSideShorten' => null, 'OpenID' => null), + 'locale_path' => false, // Set to a path to use *instead of* each plugin's own locale subdirectories ), + 'pluginlist' => array(), 'admin' => - array('panels' => array('design', 'site', 'user', 'paths', 'access', 'sessions', 'sitenotice', 'plugins')), - array('panels' => array('design', 'site', 'user', 'paths', 'access', 'sessions', 'sitenotice', 'license')), ++ array('panels' => array('design', 'site', 'user', 'paths', 'access', 'sessions', 'sitenotice', 'license', 'plugins')), 'singleuser' => array('enabled' => false, 'nickname' => null), @@@ -316,10 -315,6 +317,10 @@@ array('subscribers' => true, 'members' => true, 'peopletag' => true), + 'url' => + array('shortener' => 'ur1.ca', + 'maxlength' => 25, + 'maxnoticelength' => -1), 'http' => // HTTP client settings when contacting other sites array('ssl_cafile' => false, // To enable SSL cert validation, point to a CA bundle (eg '/usr/lib/ssl/certs/ca-certificates.crt') 'curl' => false, // Use CURL backend for HTTP fetches if available. (If not, PHP's socket streams will be used.) diff --combined lib/htmloutputter.php index 7eccd6cc0e,44b0296046..9780dc4243 --- a/lib/htmloutputter.php +++ b/lib/htmloutputter.php @@@ -177,7 -177,7 +177,7 @@@ class HTMLOutputter extends XMLOutputte $attrs = array('name' => $id, 'type' => 'text', 'id' => $id); - if ($value) { + if (!is_null($value)) { // value can be 0 or '' $attrs['value'] = $value; } $this->element('input', $attrs); @@@ -325,6 -325,7 +325,7 @@@ * @param string $label text of the button * @param string $cls class of the button, default 'submit' * @param string $name name, if different than ID + * @param string $title title text for the submit button * * @return void * diff --combined lib/router.php index 86dd116c8d,00b2993734..3bbb4a044e --- a/lib/router.php +++ b/lib/router.php @@@ -151,8 -151,6 +151,8 @@@ class Route $m->connect('main/xrds', array('action' => 'publicxrds')); + $m->connect('.well-known/host-meta', + array('action' => 'hostmeta')); // these take a code @@@ -490,19 -488,19 +490,19 @@@ // Social graph $m->connect('api/friends/ids/:id.:format', - array('action' => 'apiuserfriends', + array('action' => 'ApiUserFriends', 'ids_only' => true)); $m->connect('api/followers/ids/:id.:format', - array('action' => 'apiuserfollowers', + array('action' => 'ApiUserFollowers', 'ids_only' => true)); $m->connect('api/friends/ids.:format', - array('action' => 'apiuserfriends', + array('action' => 'ApiUserFriends', 'ids_only' => true)); $m->connect('api/followers/ids.:format', - array('action' => 'apiuserfollowers', + array('action' => 'ApiUserFollowers', 'ids_only' => true)); // account @@@ -657,12 -655,6 +657,12 @@@ $m->connect('api/statusnet/groups/create.:format', array('action' => 'ApiGroupCreate', 'format' => '(xml|json)')); + + $m->connect('api/statusnet/groups/update/:id.:format', + array('action' => 'ApiGroupProfileUpdate', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json)')); + // Tags $m->connect('api/statusnet/tags/timeline/:tag.:format', array('action' => 'ApiTimelineTag', @@@ -680,13 -672,13 +680,13 @@@ $m->connect('api/trends.json', array('action' => 'ApiTrends')); $m->connect('api/oauth/request_token', - array('action' => 'apioauthrequesttoken')); + array('action' => 'ApiOauthRequestToken')); $m->connect('api/oauth/access_token', - array('action' => 'apioauthaccesstoken')); + array('action' => 'ApiOauthAccessToken')); $m->connect('api/oauth/authorize', - array('action' => 'apioauthauthorize')); + array('action' => 'ApiOauthAuthorize')); // Admin @@@ -698,13 -690,8 +698,14 @@@ $m->connect('admin/sessions', array('action' => 'sessionsadminpanel')); $m->connect('admin/sitenotice', array('action' => 'sitenoticeadminpanel')); $m->connect('admin/snapshot', array('action' => 'snapshotadminpanel')); + $m->connect('admin/license', array('action' => 'licenseadminpanel')); - + $m->connect('admin/plugins', array('action' => 'pluginsadminpanel')); + $m->connect('admin/plugins/enable/:plugin', + array('action' => 'pluginenable'), + array('plugin' => '[A-Za-z0-9_]+')); + $m->connect('admin/plugins/disable/:plugin', + array('action' => 'plugindisable'), + array('plugin' => '[A-Za-z0-9_]+')); $m->connect('getfile/:filename', array('action' => 'getfile'), diff --combined lib/util.php index 20c9144d47,dc853f657b..b20ed8225f --- a/lib/util.php +++ b/lib/util.php @@@ -19,15 -19,18 +19,18 @@@ /* XXX: break up into separate modules (HTTP, user, files) */ - // Show a server error - + /** + * Show a server error. + */ function common_server_error($msg, $code=500) { $err = new ServerErrorAction($msg, $code); $err->showPage(); } - // Show a user error + /** + * Show a user error. + */ function common_user_error($msg, $code=400) { $err = new ClientErrorAction($msg, $code); @@@ -142,6 -145,7 +145,6 @@@ function common_switch_locale($language textdomain("statusnet"); } - function common_timezone() { if (common_logged_in()) { @@@ -154,38 -158,22 +157,38 @@@ return common_config('site', 'timezone'); } +function common_valid_language($lang) +{ + if ($lang) { + // Validate -- we don't want to end up with a bogus code + // left over from some old junk. + foreach (common_config('site', 'languages') as $code => $info) { + if ($info['lang'] == $lang) { + return true; + } + } + } + return false; +} + function common_language() { + // Allow ?uselang=xx override, very useful for debugging + // and helping translators check usage and context. + if (isset($_GET['uselang'])) { + $uselang = strval($_GET['uselang']); + if (common_valid_language($uselang)) { + return $uselang; + } + } + // If there is a user logged in and they've set a language preference // then return that one... if (_have_config() && common_logged_in()) { $user = common_current_user(); - $user_language = $user->language; - - if ($user->language) { - // Validate -- we don't want to end up with a bogus code - // left over from some old junk. - foreach (common_config('site', 'languages') as $code => $info) { - if ($info['lang'] == $user_language) { - return $user_language; - } - } + + if (common_valid_language($user->language)) { + return $user->language; } } @@@ -203,8 -191,10 +206,10 @@@ // Finally, if none of the above worked, use the site's default... return common_config('site', 'language'); } - // salted, hashed passwords are stored in the DB + /** + * Salted, hashed passwords are stored in the DB. + */ function common_munge_password($password, $id) { if (is_object($id) || is_object($password)) { @@@ -215,8 -205,9 +220,9 @@@ return md5($password . $id); } - // check if a username exists and has matching password - + /** + * Check if a username exists and has matching password. + */ function common_check_user($nickname, $password) { // empty nickname always unacceptable @@@ -243,7 -234,9 +249,9 @@@ return $authenticatedUser; } - // is the current user logged in? + /** + * Is the current user logged in? + */ function common_logged_in() { return (!is_null(common_current_user())); @@@ -289,12 -282,10 +297,10 @@@ function common_ensure_session( // 3) null to clear // Initialize to false; set to null if none found - $_cur = false; function common_set_user($user) { - global $_cur; if (is_null($user) && common_have_session()) { @@@ -380,7 -371,6 +386,6 @@@ function common_rememberme($user=null function common_remembered_user() { - $user = null; $packed = isset($_COOKIE[REMEMBERME]) ? $_COOKIE[REMEMBERME] : null; @@@ -442,14 -432,17 +447,17 @@@ return $user; } - // must be called with a valid user! - + /** + * must be called with a valid user! + */ function common_forgetme() { common_set_cookie(REMEMBERME, '', 0); } - // who is the current user? + /** + * Who is the current user? + */ function common_current_user() { global $_cur; @@@ -485,10 -478,11 +493,11 @@@ return $_cur; } - // Logins that are 'remembered' aren't 'real' -- they're subject to - // cookie-stealing. So, we don't let them do certain things. New reg, - // OpenID, and password logins _are_ real. - + /** + * Logins that are 'remembered' aren't 'real' -- they're subject to + * cookie-stealing. So, we don't let them do certain things. New reg, + * OpenID, and password logins _are_ real. + */ function common_real_login($real=true) { common_ensure_session(); @@@ -500,6 -494,29 +509,29 @@@ function common_is_real_login( return common_logged_in() && $_SESSION['real_login']; } + /** + * Get a hash portion for HTTP caching Etags and such including + * info on the current user's session. If login/logout state changes, + * or we've changed accounts, or we've renamed the current user, + * we'll get a new hash value. + * + * This should not be considered secure information. + * + * @param User $user (optional; uses common_current_user() if left out) + * @return string + */ + function common_user_cache_hash($user=false) + { + if ($user === false) { + $user = common_current_user(); + } + if ($user) { + return crc32($user->id . ':' . $user->nickname); + } else { + return '0'; + } + } + // get canonical version of nickname for comparison function common_canonical_nickname($nickname) { @@@ -591,9 -608,7 +623,7 @@@ function common_find_mentions($text, $n } if (Event::handle('StartFindMentions', array($sender, $text, &$mentions))) { - // Get the context of the original notice, if any - $originalAuthor = null; $originalNotice = null; $originalMentions = array(); @@@ -629,7 -644,6 +659,6 @@@ $matches = array_merge($tmatches[1], $atmatches[1]); foreach ($matches as $match) { - $nickname = common_canonical_nickname($match[0]); // Try to get a profile for this nickname. @@@ -637,19 -651,15 +666,15 @@@ // sender context. if (!empty($originalAuthor) && $originalAuthor->nickname == $nickname) { - $mentioned = $originalAuthor; - } else if (!empty($originalMentions) && array_key_exists($nickname, $originalMentions)) { - $mentioned = $originalMentions[$nickname]; } else { $mentioned = common_relative_profile($sender, $nickname); } if (!empty($mentioned)) { - $user = User::staticGet('id', $mentioned->id); if ($user) { @@@ -891,21 -901,9 +916,21 @@@ function common_linkify($url) function common_shorten_links($text, $always = false) { - $maxLength = Notice::maxContent(); - if (!$always && ($maxLength == 0 || mb_strlen($text) <= $maxLength)) return $text; - return common_replace_urls_callback($text, array('File_redirection', 'makeShort')); + common_debug("common_shorten_links() called"); + + $user = common_current_user(); + + $maxLength = User_urlshortener_prefs::maxNoticeLength($user); + + common_debug("maxLength = $maxLength"); + + if ($always || mb_strlen($text) > $maxLength) { + common_debug("Forcing shortening"); + return common_replace_urls_callback($text, array('File_redirection', 'forceShort')); + } else { + common_debug("Not forcing shortening"); + return common_replace_urls_callback($text, array('File_redirection', 'makeShort')); + } } function common_xml_safe_str($str) @@@ -1130,30 -1128,30 +1155,30 @@@ function common_date_string($dt // TRANS: Used in notices to indicate when the notice was made compared to now. return _('about a minute ago'); } else if ($diff < 3300) { - // XXX: should support plural. + $minutes = round($diff/60); // TRANS: Used in notices to indicate when the notice was made compared to now. - return sprintf(_('about %d minutes ago'), round($diff/60)); + return sprintf( ngettext('about one minute ago', 'about %d minutes ago', $minutes), $minutes); } else if ($diff < 5400) { // TRANS: Used in notices to indicate when the notice was made compared to now. return _('about an hour ago'); } else if ($diff < 22 * 3600) { - // XXX: should support plural. + $hours = round($diff/3600); // TRANS: Used in notices to indicate when the notice was made compared to now. - return sprintf(_('about %d hours ago'), round($diff/3600)); + return sprintf( ngettext('about one hour ago', 'about %d hours ago', $hours), $hours); } else if ($diff < 37 * 3600) { // TRANS: Used in notices to indicate when the notice was made compared to now. return _('about a day ago'); } else if ($diff < 24 * 24 * 3600) { - // XXX: should support plural. + $days = round($diff/(24*3600)); // TRANS: Used in notices to indicate when the notice was made compared to now. - return sprintf(_('about %d days ago'), round($diff/(24*3600))); + return sprintf( ngettext('about one day ago', 'about %d days ago', $days), $days); } else if ($diff < 46 * 24 * 3600) { // TRANS: Used in notices to indicate when the notice was made compared to now. return _('about a month ago'); } else if ($diff < 330 * 24 * 3600) { - // XXX: should support plural. + $months = round($diff/(30*24*3600)); // TRANS: Used in notices to indicate when the notice was made compared to now. - return sprintf(_('about %d months ago'), round($diff/(30*24*3600))); + return sprintf( ngettext('about one month ago', 'about %d months ago',$months), $months); } else if ($diff < 480 * 24 * 3600) { // TRANS: Used in notices to indicate when the notice was made compared to now. return _('about a year ago'); @@@ -1251,8 -1249,14 +1276,8 @@@ function common_redirect($url, $code=30 exit; } -function common_broadcast_notice($notice, $remote=false) -{ - // DO NOTHING! -} +// Stick the notice on the queue -/** - * Stick the notice on the queue. - */ function common_enqueue_notice($notice) { static $localTransports = array('omb', @@@ -1266,9 -1270,18 +1291,9 @@@ $transports[] = 'plugin'; } - $xmpp = common_config('xmpp', 'enabled'); - - if ($xmpp) { - $transports[] = 'jabber'; - } - // We can skip these for gatewayed notices. if ($notice->isLocal()) { $transports = array_merge($transports, $localTransports); - if ($xmpp) { - $transports[] = 'public'; - } } if (Event::handle('StartEnqueueNotice', array($notice, &$transports))) { @@@ -1305,8 -1318,9 +1330,9 @@@ function common_profile_url($nickname null, null, false); } - // Should make up a reasonable root URL - + /** + * Should make up a reasonable root URL + */ function common_root_url($ssl=false) { $url = common_path('', $ssl, false); @@@ -1317,9 -1331,10 +1343,10 @@@ return $url; } - // returns $bytes bytes of random data as a hexadecimal string - // "good" here is a goal and not a guarantee - + /** + * returns $bytes bytes of random data as a hexadecimal string + * "good" here is a goal and not a guarantee + */ function common_good_rand($bytes) { // XXX: use random.org...? @@@ -1465,7 -1480,12 +1492,12 @@@ function common_log_db_error(&$object, { $objstr = common_log_objstring($object); $last_error = &PEAR::getStaticProperty('DB_DataObject','lastError'); - common_log(LOG_ERR, $last_error->message . '(' . $verb . ' on ' . $objstr . ')', $filename); + if (is_object($last_error)) { + $msg = $last_error->message; + } else { + $msg = 'Unknown error (' . var_export($last_error, true) . ')'; + } + common_log(LOG_ERR, $msg . '(' . $verb . ' on ' . $objstr . ')', $filename); } function common_log_objstring(&$object) @@@ -1793,6 -1813,21 +1825,6 @@@ function common_session_token( return $_SESSION['token']; } -function common_cache_key($extra) -{ - return Cache::key($extra); -} - -function common_keyize($str) -{ - return Cache::keyize($str); -} - -function common_memcache() -{ - return Cache::instance(); -} - function common_license_terms($uri) { if(preg_match('/creativecommons.org\/licenses\/([^\/]+)/', $uri, $matches)) { @@@ -1823,7 -1858,6 +1855,6 @@@ function common_compatible_license($fro */ function common_database_tablename($tablename) { - if(common_config('db','quote_identifiers')) { $tablename = '"'. $tablename .'"'; } @@@ -1834,42 -1868,30 +1865,42 @@@ /** * Shorten a URL with the current user's configured shortening service, * or ur1.ca if configured, or not at all if no shortening is set up. - * Length is not considered. * - * @param string $long_url + * @param string $long_url original URL + * @param boolean $force Force shortening (used when notice is too long) + * * @return string may return the original URL if shortening failed * * @fixme provide a way to specify a particular shortener * @fixme provide a way to specify to use a given user's shortening preferences */ -function common_shorten_url($long_url) + +function common_shorten_url($long_url, $force = false) { + common_debug("Shortening URL '$long_url' (force = $force)"); + $long_url = trim($long_url); + $user = common_current_user(); - if (empty($user)) { - // common current user does not find a user when called from the XMPP daemon - // therefore we'll set one here fix, so that XMPP given URLs may be shortened - $shortenerName = 'ur1.ca'; - } else { - $shortenerName = $user->urlshorteningservice; + + $maxUrlLength = User_urlshortener_prefs::maxUrlLength($user); + common_debug("maxUrlLength = $maxUrlLength"); + + // $force forces shortening even if it's not strictly needed + + if (mb_strlen($long_url) < $maxUrlLength && !$force) { + common_debug("Skipped shortening URL."); + return $long_url; } - if(Event::handle('StartShortenUrl', array($long_url,$shortenerName,&$shortenedUrl))){ + $shortenerName = User_urlshortener_prefs::urlShorteningService($user); + + common_debug("Shortener name = '$shortenerName'"); + + if (Event::handle('StartShortenUrl', array($long_url, $shortenerName, &$shortenedUrl))) { //URL wasn't shortened, so return the long url return $long_url; - }else{ + } else { //URL was shortened, so return the result return trim($shortenedUrl); } diff --combined lib/xrd.php index 145cd64cb4,0000000000..c8cffed9cd mode 100644,000000..100644 --- a/lib/xrd.php +++ b/lib/xrd.php @@@ -1,171 -1,0 +1,172 @@@ +. + * + * @package StatusNet + * @author James Walker + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + - +class XRD +{ + const XML_NS = 'http://www.w3.org/2000/xmlns/'; + + const XRD_NS = 'http://docs.oasis-open.org/ns/xri/xrd-1.0'; + + const HOST_META_NS = 'http://host-meta.net/xrd/1.0'; + + public $expires; + + public $subject; + + public $host; + + public $alias = array(); + + public $types = array(); + + public $links = array(); + + public static function parse($xml) + { + $xrd = new XRD(); + + $dom = new DOMDocument(); + + // Don't spew XML warnings to output + $old = error_reporting(); + error_reporting($old & ~E_WARNING); + $ok = $dom->loadXML($xml); + error_reporting($old); + + if (!$ok) { - throw new Exception("Invalid XML."); ++ // TRANS: Exception. ++ throw new Exception(_m('Invalid XML.')); + } + $xrd_element = $dom->getElementsByTagName('XRD')->item(0); + if (!$xrd_element) { - throw new Exception("Invalid XML, missing XRD root."); ++ // TRANS: Exception. ++ throw new Exception(_m('Invalid XML, missing XRD root.')); + } + + // Check for host-meta host + $host = $xrd_element->getElementsByTagName('Host')->item(0); + if ($host) { + $xrd->host = $host->nodeValue; + } + + // Loop through other elements + foreach ($xrd_element->childNodes as $node) { + if (!($node instanceof DOMElement)) { + continue; + } + switch ($node->tagName) { + case 'Expires': + $xrd->expires = $node->nodeValue; + break; + case 'Subject': + $xrd->subject = $node->nodeValue; + break; + + case 'Alias': + $xrd->alias[] = $node->nodeValue; + break; + + case 'Link': + $xrd->links[] = $xrd->parseLink($node); + break; + + case 'Type': + $xrd->types[] = $xrd->parseType($node); + break; + + } + } + return $xrd; + } + + public function toXML() + { + $xs = new XMLStringer(); + + $xs->startXML(); + $xs->elementStart('XRD', array('xmlns' => XRD::XRD_NS)); + + if ($this->host) { + $xs->element('hm:Host', array('xmlns:hm' => XRD::HOST_META_NS), $this->host); + } + + if ($this->expires) { + $xs->element('Expires', null, $this->expires); + } + + if ($this->subject) { + $xs->element('Subject', null, $this->subject); + } + + foreach ($this->alias as $alias) { + $xs->element('Alias', null, $alias); + } + + foreach ($this->links as $link) { + $titles = array(); + if (isset($link['title'])) { + $titles = $link['title']; + unset($link['title']); + } + $xs->elementStart('Link', $link); + foreach ($titles as $title) { + $xs->element('Title', null, $title); + } + $xs->elementEnd('Link'); + } + + $xs->elementEnd('XRD'); + + return $xs->getString(); + } + + function parseType($element) + { + return array(); + } + + function parseLink($element) + { + $link = array(); + $link['rel'] = $element->getAttribute('rel'); + $link['type'] = $element->getAttribute('type'); + $link['href'] = $element->getAttribute('href'); + $link['template'] = $element->getAttribute('template'); + foreach ($element->childNodes as $node) { + if ($node instanceof DOMElement) { + switch($node->tagName) { + case 'Title': + $link['title'][] = $node->nodeValue; + } + } + } + + return $link; + } +} diff --combined plugins/BitlyUrl/BitlyUrlPlugin.php index 38c8216361,10d99b3588..e1c8d3462e --- a/plugins/BitlyUrl/BitlyUrlPlugin.php +++ b/plugins/BitlyUrl/BitlyUrlPlugin.php @@@ -31,6 -31,8 +31,6 @@@ if (!defined('STATUSNET')) exit(1); } -require_once INSTALLDIR.'/plugins/UrlShortener/UrlShortenerPlugin.php'; - class BitlyUrlPlugin extends UrlShortenerPlugin { public $serviceUrl; @@@ -38,7 -40,7 +38,7 @@@ function onInitializePlugin(){ parent::onInitializePlugin(); if(!isset($this->serviceUrl)){ - throw new Exception("must specify a serviceUrl"); + throw new Exception(_m("You must specify a serviceUrl.")); } } @@@ -61,4 -63,3 +61,3 @@@ return true; } } - diff --combined plugins/CasAuthentication/caslogin.php index be69673810,846774e7c6..3301ce5824 --- a/plugins/CasAuthentication/caslogin.php +++ b/plugins/CasAuthentication/caslogin.php @@@ -28,7 -28,7 +28,7 @@@ class CasloginAction extends Actio $this->clientError(_m('Already logged in.')); } else { global $casSettings; - phpCAS::client(CAS_VERSION_2_0,$casSettings['server'],$casSettings['port'],$casSettings['path']); + phpCAS::client(CAS_VERSION_2_0,$casSettings['server'],$casSettings['port'],$casSettings['path'],false); phpCAS::setNoCasServerValidation(); phpCAS::handleLogoutRequests(); phpCAS::forceAuthentication(); @@@ -36,13 -36,13 +36,13 @@@ $casTempPassword = common_good_rand(16); $user = common_check_user(phpCAS::getUser(), $casTempPassword); if (!$user) { - $this->serverError(_('Incorrect username or password.')); + $this->serverError(_m('Incorrect username or password.')); return; } // success! if (!common_set_user($user)) { - $this->serverError(_('Error setting user. You are probably not authorized.')); + $this->serverError(_m('Error setting user. You are probably not authorized.')); return; } @@@ -69,7 -69,6 +69,6 @@@ } common_redirect($url, 303); - } } } diff --combined plugins/ClientSideShorten/ClientSideShortenPlugin.php index 3da08e05da,27a3a56f72..65e27a0374 --- a/plugins/ClientSideShorten/ClientSideShortenPlugin.php +++ b/plugins/ClientSideShorten/ClientSideShortenPlugin.php @@@ -51,10 -51,8 +51,10 @@@ class ClientSideShortenPlugin extends P } function onEndShowScripts($action){ - $action->inlineScript('var Notice_maxContent = ' . Notice::maxContent()); if (common_logged_in()) { + $user = common_current_user(); + $action->inlineScript('var maxNoticeLength = ' . User_urlshortener_prefs::maxNoticeLength($user)); + $action->inlineScript('var maxUrlLength = ' . User_urlshortener_prefs::maxUrlLength($user)); $action->script('plugins/ClientSideShorten/shorten.js'); } } @@@ -73,9 -71,7 +73,7 @@@ 'author' => 'Craig Andrews', 'homepage' => 'http://status.net/wiki/Plugin:ClientSideShorten', 'rawdescription' => - _m('ClientSideShorten causes the web interface\'s notice form to automatically shorten urls as they entered, and before the notice is submitted.')); + _m('ClientSideShorten causes the web interface\'s notice form to automatically shorten URLs as they entered, and before the notice is submitted.')); return true; } - } - diff --combined plugins/Geonames/GeonamesPlugin.php index 0000000000,310641ce60..d88014bb80 mode 000000,100644..100644 --- a/plugins/Geonames/GeonamesPlugin.php +++ b/plugins/Geonames/GeonamesPlugin.php @@@ -1,0 -1,503 +1,503 @@@ + . + * + * @category Action + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + + if (!defined('STATUSNET')) { + exit(1); + } + + /** + * Plugin to convert string locations to Geonames IDs and vice versa + * + * This handles most of the events that Location class emits. It uses + * the geonames.org Web service to convert names like 'Montreal, Quebec, Canada' + * into IDs and lat/lon pairs. + * + * @category Plugin + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + * @seeAlso Location + */ + class GeonamesPlugin extends Plugin + { + const LOCATION_NS = 1; + + public $host = 'ws.geonames.org'; + public $username = null; + public $token = null; + public $expiry = 7776000; // 90-day expiry + public $timeout = 2; // Web service timeout in seconds. + public $timeoutWindow = 60; // Further lookups in this process will be disabled for N seconds after a timeout. + public $cachePrefix = null; // Optional shared memcache prefix override + // to share lookups between local instances. + + protected $lastTimeout = null; // timestamp of last web service timeout + + /** + * convert a name into a Location object + * + * @param string $name Name to convert + * @param string $language ISO code for anguage the name is in + * @param Location &$location Location object (may be null) + * + * @return boolean whether to continue (results in $location) + */ + function onLocationFromName($name, $language, &$location) + { + $loc = $this->getCache(array('name' => $name, + 'language' => $language)); + + if ($loc !== false) { + $location = $loc; + return false; + } + + try { + $geonames = $this->getGeonames('search', + array('maxRows' => 1, + 'q' => $name, + 'lang' => $language, + 'type' => 'xml')); + } catch (Exception $e) { + $this->log(LOG_WARNING, "Error for $name: " . $e->getMessage()); + return true; + } + + if (count($geonames) == 0) { + // no results + $this->setCache(array('name' => $name, + 'language' => $language), + null); + return true; + } + + $n = $geonames[0]; + + $location = new Location(); + + $location->lat = $this->canonical($n->lat); + $location->lon = $this->canonical($n->lng); + $location->names[$language] = (string)$n->name; + $location->location_id = (string)$n->geonameId; + $location->location_ns = self::LOCATION_NS; + + $this->setCache(array('name' => $name, + 'language' => $language), + $location); + + // handled, don't continue processing! + return false; + } + + /** + * convert an id into a Location object + * + * @param string $id Name to convert + * @param string $ns Name to convert + * @param string $language ISO code for language for results + * @param Location &$location Location object (may be null) + * + * @return boolean whether to continue (results in $location) + */ + function onLocationFromId($id, $ns, $language, &$location) + { + if ($ns != self::LOCATION_NS) { + // It's not one of our IDs... keep processing + return true; + } + + $loc = $this->getCache(array('id' => $id)); + + if ($loc !== false) { + $location = $loc; + return false; + } + + try { + $geonames = $this->getGeonames('hierarchy', + array('geonameId' => $id, + 'lang' => $language)); + } catch (Exception $e) { + $this->log(LOG_WARNING, "Error for ID $id: " . $e->getMessage()); + return false; + } + + $parts = array(); + + foreach ($geonames as $level) { + if (in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) { + $parts[] = (string)$level->name; + } + } + + $last = $geonames[count($geonames)-1]; + + if (!in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) { + $parts[] = (string)$last->name; + } + + $location = new Location(); + + $location->location_id = (string)$last->geonameId; + $location->location_ns = self::LOCATION_NS; + $location->lat = $this->canonical($last->lat); + $location->lon = $this->canonical($last->lng); + + $location->names[$language] = implode(', ', array_reverse($parts)); + + $this->setCache(array('id' => (string)$last->geonameId), + $location); + + // We're responsible for this namespace; nobody else + // can resolve it + + return false; + } + + /** + * convert a lat/lon pair into a Location object + * + * Given a lat/lon, we try to find a Location that's around + * it or nearby. We prefer populated places (cities, towns, villages). + * + * @param string $lat Latitude + * @param string $lon Longitude + * @param string $language ISO code for language for results + * @param Location &$location Location object (may be null) + * + * @return boolean whether to continue (results in $location) + */ + function onLocationFromLatLon($lat, $lon, $language, &$location) + { + // Make sure they're canonical + + $lat = $this->canonical($lat); + $lon = $this->canonical($lon); + + $loc = $this->getCache(array('lat' => $lat, + 'lon' => $lon)); + + if ($loc !== false) { + $location = $loc; + return false; + } + + try { + $geonames = $this->getGeonames('findNearbyPlaceName', + array('lat' => $lat, + 'lng' => $lon, + 'lang' => $language)); + } catch (Exception $e) { + $this->log(LOG_WARNING, "Error for coords $lat, $lon: " . $e->getMessage()); + return true; + } + + if (count($geonames) == 0) { + // no results + $this->setCache(array('lat' => $lat, + 'lon' => $lon), + null); + return true; + } + + $n = $geonames[0]; + + $parts = array(); + + $location = new Location(); + + $parts[] = (string)$n->name; + + if (!empty($n->adminName1)) { + $parts[] = (string)$n->adminName1; + } + + if (!empty($n->countryName)) { + $parts[] = (string)$n->countryName; + } + + $location->location_id = (string)$n->geonameId; + $location->location_ns = self::LOCATION_NS; + $location->lat = $this->canonical($n->lat); + $location->lon = $this->canonical($n->lng); + + $location->names[$language] = implode(', ', $parts); + + $this->setCache(array('lat' => $lat, + 'lon' => $lon), + $location); + + // Success! We handled it, so no further processing + + return false; + } + + /** + * Human-readable name for a location + * + * Given a location, we try to retrieve a human-readable name + * in the target language. + * + * @param Location $location Location to get the name for + * @param string $language ISO code for language to find name in + * @param string &$name Place to put the name + * + * @return boolean whether to continue + */ + function onLocationNameLanguage($location, $language, &$name) + { + if ($location->location_ns != self::LOCATION_NS) { + // It's not one of our IDs... keep processing + return true; + } + + $id = $location->location_id; + + $n = $this->getCache(array('id' => $id, + 'language' => $language)); + + if ($n !== false) { + $name = $n; + return false; + } + + try { + $geonames = $this->getGeonames('hierarchy', + array('geonameId' => $id, + 'lang' => $language)); + } catch (Exception $e) { + $this->log(LOG_WARNING, "Error for ID $id: " . $e->getMessage()); + return false; + } + + if (count($geonames) == 0) { + $this->setCache(array('id' => $id, + 'language' => $language), + null); + return false; + } + + $parts = array(); + + foreach ($geonames as $level) { + if (in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) { + $parts[] = (string)$level->name; + } + } + + $last = $geonames[count($geonames)-1]; + + if (!in_array($level->fcode, array('PCLI', 'ADM1', 'PPL'))) { + $parts[] = (string)$last->name; + } + + if (count($parts)) { + $name = implode(', ', array_reverse($parts)); + $this->setCache(array('id' => $id, + 'language' => $language), + $name); + } + + return false; + } + + /** + * Human-readable URL for a location + * + * Given a location, we try to retrieve a geonames.org URL. + * + * @param Location $location Location to get the url for + * @param string &$url Place to put the url + * + * @return boolean whether to continue + */ + function onLocationUrl($location, &$url) + { + if ($location->location_ns != self::LOCATION_NS) { + // It's not one of our IDs... keep processing + return true; + } + + $url = 'http://www.geonames.org/' . $location->location_id; + + // it's been filled, so don't process further. + return false; + } + + /** + * Machine-readable name for a location + * + * Given a location, we try to retrieve a geonames.org URL. + * + * @param Location $location Location to get the url for + * @param string &$url Place to put the url + * + * @return boolean whether to continue + */ + function onLocationRdfUrl($location, &$url) + { + if ($location->location_ns != self::LOCATION_NS) { + // It's not one of our IDs... keep processing + return true; + } + + $url = 'http://sws.geonames.org/' . $location->location_id . '/'; + + // it's been filled, so don't process further. + return false; + } + + function getCache($attrs) + { - $c = common_memcache(); ++ $c = Cache::instance(); + + if (empty($c)) { + return null; + } + + $key = $this->cacheKey($attrs); + + $value = $c->get($key); + + return $value; + } + + function setCache($attrs, $loc) + { - $c = common_memcache(); ++ $c = Cache::instance(); + + if (empty($c)) { + return null; + } + + $key = $this->cacheKey($attrs); + + $result = $c->set($key, $loc, 0, time() + $this->expiry); + + return $result; + } + + function cacheKey($attrs) + { + $key = 'geonames:' . + implode(',', array_keys($attrs)) . ':'. - common_keyize(implode(',', array_values($attrs))); ++ Cache::keyize(implode(',', array_values($attrs))); + if ($this->cachePrefix) { + return $this->cachePrefix . ':' . $key; + } else { - return common_cache_key($key); ++ return Cache::key($key); + } + } + + function wsUrl($method, $params) + { + if (!empty($this->username)) { + $params['username'] = $this->username; + } + + if (!empty($this->token)) { + $params['token'] = $this->token; + } + + $str = http_build_query($params, null, '&'); + + return 'http://'.$this->host.'/'.$method.'?'.$str; + } + + function getGeonames($method, $params) + { + if ($this->lastTimeout && (time() - $this->lastTimeout < $this->timeoutWindow)) { + throw new Exception("skipping due to recent web service timeout"); + } + + $client = HTTPClient::start(); + $client->setConfig('connect_timeout', $this->timeout); + $client->setConfig('timeout', $this->timeout); + + try { + $result = $client->get($this->wsUrl($method, $params)); + } catch (Exception $e) { + common_log(LOG_ERR, __METHOD__ . ": " . $e->getMessage()); + $this->lastTimeout = time(); + throw $e; + } + + if (!$result->isOk()) { + throw new Exception("HTTP error code " . $result->getStatus()); + } + + $body = $result->getBody(); + + if (empty($body)) { + throw new Exception("Empty HTTP body in response"); + } + + // This will throw an exception if the XML is mal-formed + + $document = new SimpleXMLElement($body); + + // No children, usually no results + + $children = $document->children(); + + if (count($children) == 0) { + return array(); + } + + if (isset($document->status)) { + throw new Exception("Error #".$document->status['value']." ('".$document->status['message']."')"); + } + + // Array of elements, >0 elements + + return $document->geoname; + } + + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'Geonames', + 'version' => STATUSNET_VERSION, + 'author' => 'Evan Prodromou', + 'homepage' => 'http://status.net/wiki/Plugin:Geonames', + 'rawdescription' => + _m('Uses Geonames service to get human-readable '. + 'names for locations based on user-provided lat/long pairs.')); + return true; + } + + function canonical($coord) + { + $coord = rtrim($coord, "0"); + $coord = rtrim($coord, "."); + + return $coord; + } + } diff --combined plugins/Imap/imapmanager.php index cfc08c1ee1,e2f8c6d543..c49c060921 --- a/plugins/Imap/imapmanager.php +++ b/plugins/Imap/imapmanager.php @@@ -44,7 -44,7 +44,7 @@@ class ImapManager extends IoManage */ public static function get() { - throw new Exception('ImapManager should be created using it\'s constructor, not the static get method'); + throw new Exception(_m('ImapManager should be created using its constructor, not the using the static get method.')); } /** @@@ -59,14 -59,12 +59,14 @@@ } /** - * Tell the i/o master we need one instance for each supporting site - * being handled in this process. + * Tell the i/o master we need one instance globally. + * Since this is a plugin manager, the plugin class itself will + * create one instance per site. This prevents the IoMaster from + * making more instances. */ public static function multiSite() { - return IoManager::INSTANCE_PER_SITE; + return IoManager::GLOBAL_SINGLE_ONLY; } /** @@@ -94,12 -92,12 +94,12 @@@ { return $this->check_mailbox() > 0; } - + function pollInterval() { return $this->plugin->poll_frequency; } - + protected function connect() { $this->conn = imap_open($this->plugin->mailbox, $this->plugin->user, $this->plugin->password); diff --combined plugins/LdapCommon/LdapCommon.php index 09ff54bad9,159b2d265a..579fe4b64b --- a/plugins/LdapCommon/LdapCommon.php +++ b/plugins/LdapCommon/LdapCommon.php @@@ -60,13 -60,13 +60,13 @@@ class LdapCommo $this->ldap_config = $this->get_ldap_config(); if(!isset($this->host)){ - throw new Exception("must specify a host"); + throw new Exception(_m("A host must be specified.")); } if(!isset($this->basedn)){ - throw new Exception("must specify a basedn"); + throw new Exception(_m('"basedn" must be specified.')); } if(!isset($this->attributes['username'])){ - throw new Exception("username attribute must be set."); + throw new Exception(_m('The username attribute must be set.')); } } @@@ -126,11 -126,11 +126,11 @@@ } throw new Exception('Could not connect to LDAP server: '.$err->getMessage()); } - $c = common_memcache(); + $c = Cache::instance(); if (!empty($c)) { $cacheObj = new MemcacheSchemaCache( array('c'=>$c, - 'cacheKey' => common_cache_key('ldap_schema:' . $config_id))); + 'cacheKey' => Cache::key('ldap_schema:' . $config_id))); $ldap->registerSchemaCache($cacheObj); } self::$ldap_connections[$config_id] = $ldap; @@@ -179,7 -179,7 +179,7 @@@ $ldap = $this->get_ldap_connection($config); $entry = $this->get_user($username,array(),$ldap); - + $newCryptedPassword = $this->hashPassword($newpassword, $this->password_encoding); if ($newCryptedPassword===false) { return false; @@@ -260,15 -260,14 +260,14 @@@ * @return string The hashed password. * */ - - function hashPassword( $passwordClear, $encodageType ) + function hashPassword( $passwordClear, $encodageType ) { $encodageType = strtolower( $encodageType ); switch( $encodageType ) { - case 'crypt': - $cryptedPassword = '{CRYPT}' . crypt($passwordClear,$this->randomSalt(2)); + case 'crypt': + $cryptedPassword = '{CRYPT}' . crypt($passwordClear,$this->randomSalt(2)); break; - + case 'ext_des': // extended des crypt. see OpenBSD crypt man page. if ( ! defined( 'CRYPT_EXT_DES' ) || CRYPT_EXT_DES == 0 ) {return FALSE;} //Your system crypt library does not support extended DES encryption. @@@ -351,8 -350,7 +350,7 @@@ * @param int $length The length of the salt string to generate. * @return string The generated salt string. */ - - function randomSalt( $length ) + function randomSalt( $length ) { $possible = '0123456789'. 'abcdefghijklmnopqrstuvwxyz'. @@@ -366,10 -364,8 +364,8 @@@ return $str; } - } class LdapInvalidCredentialsException extends Exception { - } diff --combined plugins/LilUrl/LilUrlPlugin.php index 06ea49ff57,bd98026fe8..b63cc8a556 --- a/plugins/LilUrl/LilUrlPlugin.php +++ b/plugins/LilUrl/LilUrlPlugin.php @@@ -31,6 -31,8 +31,6 @@@ if (!defined('STATUSNET')) exit(1); } -require_once INSTALLDIR.'/plugins/UrlShortener/UrlShortenerPlugin.php'; - class LilUrlPlugin extends UrlShortenerPlugin { public $serviceUrl; @@@ -38,7 -40,7 +38,7 @@@ function onInitializePlugin(){ parent::onInitializePlugin(); if(!isset($this->serviceUrl)){ - throw new Exception("must specify a serviceUrl"); + throw new Exception(_m('A serviceUrl must be specified.')); } } @@@ -69,4 -71,3 +69,3 @@@ return true; } } - diff --combined plugins/Minify/MinifyPlugin.php index afe6edf5fd,b375311652..cfed0779ba --- a/plugins/Minify/MinifyPlugin.php +++ b/plugins/Minify/MinifyPlugin.php @@@ -51,7 -51,6 +51,6 @@@ class MinifyPlugin extends Plugi * * @return boolean hook return */ - function onStartInitializeRouter($m) { $m->connect('main/min', @@@ -119,9 -118,9 +118,9 @@@ function onStartInlineScriptElement($action,&$code,&$type) { if($this->minifyInlineJs && $type=='text/javascript'){ - $c = common_memcache(); + $c = Cache::instance(); if (!empty($c)) { - $cacheKey = common_cache_key(self::cacheKey . ':' . crc32($code)); + $cacheKey = Cache::key(self::cacheKey . ':' . crc32($code)); $out = $c->get($cacheKey); } if(empty($out)) { @@@ -139,9 -138,9 +138,9 @@@ function onStartStyleElement($action,&$code,&$type,&$media) { if($this->minifyInlineCss && $type=='text/css'){ - $c = common_memcache(); + $c = Cache::instance(); if (!empty($c)) { - $cacheKey = common_cache_key(self::cacheKey . ':' . crc32($code)); + $cacheKey = Cache::key(self::cacheKey . ':' . crc32($code)); $out = $c->get($cacheKey); } if(empty($out)) { @@@ -177,8 -176,7 +176,7 @@@ 'author' => 'Craig Andrews', 'homepage' => 'http://status.net/wiki/Plugin:Minify', 'rawdescription' => - _m('The Minify plugin minifies your CSS and Javascript, removing whitespace and comments.')); + _m('The Minify plugin minifies StatusNet\'s CSS and JavaScript, removing whitespace and comments.')); return true; } } - diff --combined plugins/Minify/minify.php index bac1df8e2d,9a59c4223c..e012a40272 --- a/plugins/Minify/minify.php +++ b/plugins/Minify/minify.php @@@ -46,11 -46,11 +46,11 @@@ class MinifyAction extends Actio if(file_exists($this->file)) { return true; } else { - $this->clientError(_('f parameter is not a valid path'),404); + $this->clientError(_m('The parameter "f" is not a valid path.'),404); return false; } }else{ - $this->clientError(_('f parameter is required'),500); + $this->clientError(_m('The parameter "f" is required but missing.'),500); return false; } } @@@ -74,9 -74,9 +74,9 @@@ { parent::handle($args); - $c = common_memcache(); + $c = Cache::instance(); if (!empty($c)) { - $cacheKey = common_cache_key(MinifyPlugin::cacheKey . ':' . $this->file . '?v=' . empty($this->v)?'':$this->v); + $cacheKey = Cache::key(MinifyPlugin::cacheKey . ':' . $this->file . '?v=' . empty($this->v)?'':$this->v); $out = $c->get($cacheKey); } if(empty($out)) { @@@ -108,10 -108,9 +108,9 @@@ header('Content-Type: ' . self::TYPE_CSS); break; default: - $this->clientError(_('File type not supported'),500); + $this->clientError(_m('File type not supported.'),500); return false; } return $out; } } - diff --combined plugins/OStatus/OStatusPlugin.php index dd15099542,dcf1b36078..601243b425 --- a/plugins/OStatus/OStatusPlugin.php +++ b/plugins/OStatus/OStatusPlugin.php @@@ -50,6 -50,8 +50,6 @@@ class OStatusPlugin extends Plugi function onRouterInitialized($m) { // Discovery actions - $m->connect('.well-known/host-meta', - array('action' => 'hostmeta')); $m->connect('main/xrd', array('action' => 'userxrd')); $m->connect('main/ownerxrd', @@@ -221,6 -223,7 +221,7 @@@ array('nickname' => $profile->nickname)); $output->element('a', array('href' => $url, 'class' => 'entity_remote_subscribe'), + // TRANS: Link description for link to subscribe to a remote user. _m('Subscribe')); $output->elementEnd('li'); @@@ -239,6 -242,7 +240,7 @@@ array('group' => $group->nickname)); $output->element('a', array('href' => $url, 'class' => 'entity_remote_subscribe'), + // TRANS: Link description for link to join a remote group. _m('Join')); } @@@ -451,6 -455,7 +453,7 @@@ } $url = $notice->url; + // TRANSLATE: %s is a domain. $title = sprintf(_m("Sent from %s via OStatus"), $domain); return false; } @@@ -522,6 -527,7 +525,7 @@@ } if (!$oprofile->subscribe()) { + // TRANS: Exception. throw new Exception(_m('Could not set up remote subscription.')); } } @@@ -551,25 -557,10 +555,10 @@@ return true; } - $act = new Activity(); - - $act->verb = ActivityVerb::FOLLOW; - - $act->id = TagURI::mint('follow:%d:%d:%s', - $subscriber->id, - $other->id, - common_date_iso8601(time())); - - $act->time = time(); - $act->title = _("Follow"); - // TRANS: Success message for subscribe to user attempt through OStatus. - // TRANS: %1$s is the subscriber name, %2$s is the subscribed user's name. - $act->content = sprintf(_("%1$s is now following %2$s."), - $subscriber->getBestName(), - $other->getBestName()); + $sub = Subscription::pkeyGet(array('subscriber' => $subscriber->id, + 'subscribed' => $other->id)); - $act->actor = ActivityObject::fromProfile($subscriber); - $act->object = ActivityObject::fromProfile($other); + $act = $sub->asActivity(); $oprofile->notifyActivity($act, $subscriber); @@@ -611,10 -602,10 +600,10 @@@ common_date_iso8601(time())); $act->time = time(); - $act->title = _("Unfollow"); + $act->title = _m('Unfollow'); // TRANS: Success message for unsubscribe from user attempt through OStatus. // TRANS: %1$s is the unsubscriber's name, %2$s is the unsubscribed user's name. - $act->content = sprintf(_("%1$s stopped following %2$s."), + $act->content = sprintf(_m('%1$s stopped following %2$s.'), $profile->getBestName(), $other->getBestName()); @@@ -645,6 -636,9 +634,9 @@@ throw new Exception(_m('Could not set up remote group membership.')); } + // NOTE: we don't use Group_member::asActivity() since that record + // has not yet been created. + $member = Profile::staticGet($user->id); $act = new Activity(); @@@ -669,6 -663,7 +661,7 @@@ return true; } else { $oprofile->garbageCollect(); + // TRANS: Exception. throw new Exception(_m("Failed joining remote group.")); } } @@@ -727,7 -722,6 +720,6 @@@ * @param Notice $notice being favored * @return hook return value */ - function onEndFavorNotice(Profile $profile, Notice $notice) { $user = User::staticGet('id', $profile->id); @@@ -742,24 -736,15 +734,15 @@@ return true; } - $act = new Activity(); + $fav = Fave::pkeyGet(array('user_id' => $user->id, + 'notice_id' => $notice->id)); - $act->verb = ActivityVerb::FAVORITE; - $act->id = TagURI::mint('favor:%d:%d:%s', - $profile->id, - $notice->id, - common_date_iso8601(time())); + if (empty($fav)) { + // That's weird. + return true; + } - $act->time = time(); - $act->title = _("Favor"); - // TRANS: Success message for adding a favorite notice through OStatus. - // TRANS: %1$s is the favoring user's name, %2$s is URI to the favored notice. - $act->content = sprintf(_("%1$s marked notice %2$s as a favorite."), - $profile->getBestName(), - $notice->uri); - - $act->actor = ActivityObject::fromProfile($profile); - $act->object = ActivityObject::fromNotice($notice); + $act = $fav->asActivity(); $oprofile->notifyActivity($act, $profile); @@@ -797,10 -782,10 +780,10 @@@ $notice->id, common_date_iso8601(time())); $act->time = time(); - $act->title = _("Disfavor"); + $act->title = _m('Disfavor'); // TRANS: Success message for remove a favorite notice through OStatus. // TRANS: %1$s is the unfavoring user's name, %2$s is URI to the no longer favored notice. - $act->content = sprintf(_("%1$s marked notice %2$s as no longer a favorite."), + $act->content = sprintf(_m('%1$s marked notice %2$s as no longer a favorite.'), $profile->getBestName(), $notice->uri); @@@ -874,8 -859,9 +857,9 @@@ $action->elementStart('p', array('id' => 'entity_remote_subscribe', 'class' => 'entity_subscribe')); $action->element('a', array('href' => common_local_url($target), - 'class' => 'entity_remote_subscribe') - , _m('Remote')); // @todo: i18n: Add translator hint for this text. + 'class' => 'entity_remote_subscribe'), + // TRANS: Link text for link to remote subscribe. + _m('Remote')); $action->elementEnd('p'); $action->elementEnd('div'); } @@@ -914,6 -900,7 +898,7 @@@ $profile->id, common_date_iso8601(time())); $act->time = time(); + // TRANS: Title for activity. $act->title = _m("Profile update"); // TRANS: Ping text for remote profile update through OStatus. // TRANS: %s is user that updated their profile. @@@ -946,7 -933,8 +931,8 @@@ array('nickname' => $profileUser->nickname)); $output->element('a', array('href' => $url, 'class' => 'entity_remote_subscribe'), - _m('Subscribe')); // @todo: i18n: Add context. + // TRANS: Link text for a user to subscribe to an OStatus user. + _m('Subscribe')); $output->elementEnd('li'); } } @@@ -960,15 -948,15 +946,15 @@@ 'version' => STATUSNET_VERSION, 'author' => 'Evan Prodromou, James Walker, Brion Vibber, Zach Copley', 'homepage' => 'http://status.net/wiki/Plugin:OStatus', - 'rawdescription' => - _m('Follow people across social networks that implement '. - 'OStatus.')); // @todo i18n: Add translator hint. + // TRANS: Plugin description. + 'rawdescription' => _m('Follow people across social networks that implement '. + 'OStatus.')); return true; } /** - * Utility function to check if the given URL is a canonical group profile + * Utility function to check if the given URI is a canonical group profile * page, and if so return the ID number. * * @param string $url @@@ -976,11 -964,22 +962,22 @@@ */ public static function localGroupFromUrl($url) { - $template = common_local_url('groupbyid', array('id' => '31337')); - $template = preg_quote($template, '/'); - $template = str_replace('31337', '(\d+)', $template); - if (preg_match("/$template/", $url, $matches)) { - return intval($matches[1]); + $group = User_group::staticGet('uri', $url); + if ($group) { + $local = Local_group::staticGet('id', $group->id); + if ($local) { + return $group->id; + } + } else { + // To find local groups which haven't had their uri fields filled out... + // If the domain has changed since a subscriber got the URI, it'll + // be broken. + $template = common_local_url('groupbyid', array('id' => '31337')); + $template = preg_quote($template, '/'); + $template = str_replace('31337', '(\d+)', $template); + if (preg_match("/$template/", $url, $matches)) { + return intval($matches[1]); + } } return false; } @@@ -1010,12 -1009,4 +1007,12 @@@ return true; } + + function onStartHostMetaLinks(&$links) { + $url = common_local_url('userxrd'); + $url.= '?uri={uri}'; + $links[] = array('rel' => Discovery::LRDD_REL, + 'template' => $url, + 'title' => array('Resource Descriptor')); + } } diff --combined plugins/OpenID/OpenIDPlugin.php index e58440fc10,a033a50109..9c32074520 --- a/plugins/OpenID/OpenIDPlugin.php +++ b/plugins/OpenID/OpenIDPlugin.php @@@ -102,9 -102,14 +102,14 @@@ class OpenIDPlugin extends Plugi function onStartConnectPath(&$path, &$defaults, &$rules, &$result) { if (common_config('site', 'openidonly')) { - static $block = array('main/login', - 'main/register', - 'main/recoverpassword', + // Note that we should not remove the login and register + // actions. Lots of auth-related things link to them, + // such as when visiting a private site without a session + // or revalidating a remembered login for admin work. + // + // We take those two over with redirects to ourselves + // over in onArgsInitialize(). + static $block = array('main/recoverpassword', 'settings/password'); if (in_array($path, $block)) { @@@ -630,28 -635,6 +635,28 @@@ return true; } + /** + * Add OpenID information to the Account Management Control Document + * Event supplied by the Account Manager plugin + * + * @param array &$amcd Array that expresses the AMCD + * + * @return boolean hook value + */ + + function onEndAccountManagementControlDocument(&$amcd) + { + $amcd['auth-methods']['openid'] = array( + 'connect' => array( + 'method' => 'POST', + 'path' => common_local_url('openidlogin'), + 'params' => array( + 'identity' => 'openid_url' + ) + ) + ); + } + /** * Add our version information to output * diff --combined plugins/Recaptcha/RecaptchaPlugin.php index b7a0e92c7f,0c46a33e0b..08557cbd84 --- a/plugins/Recaptcha/RecaptchaPlugin.php +++ b/plugins/Recaptcha/RecaptchaPlugin.php @@@ -2,7 -2,7 +2,7 @@@ /** * StatusNet, the distributed open-source microblogging tool * - * Plugin to show reCaptcha when a user registers + * Plugin to show reCaptcha when a user registers * * PHP version 5 * @@@ -41,8 -41,7 +41,8 @@@ class RecaptchaPlugin extends Plugi var $failed; var $ssl; - function onInitializePlugin(){ + function onInitializePlugin() + { if(!isset($this->private_key)) { common_log(LOG_ERR, 'Recaptcha: Must specify private_key in config.php'); } @@@ -51,8 -50,7 +51,8 @@@ } } - function checkssl(){ + function checkssl() + { if(common_config('site', 'ssl') === 'sometimes' || common_config('site', 'ssl') === 'always') { return true; } @@@ -63,14 -61,14 +63,14 @@@ function onEndRegistrationFormData($action) { $action->elementStart('li'); - $action->raw(''); + $action->raw(''); // AJAX API will fill this div out. // We're calling that instead of the regular one so we stay compatible // with application/xml+xhtml output as for mobile. $action->element('div', array('id' => 'recaptcha')); $action->elementEnd('li'); - + $action->recaptchaPluginNeedsOutput = true; return true; } @@@ -85,7 -83,7 +85,7 @@@ $url = "http://api.recaptcha.net/js/recaptcha_ajax.js"; } $action->script($url); - + // And when we're ready, fill out the captcha! $key = json_encode($this->public_key); $action->inlinescript("\$(function(){Recaptcha.create($key, 'recaptcha');});"); @@@ -102,7 -100,7 +102,7 @@@ if (!$resp->is_valid) { if($this->display_errors) { - $action->showForm ("(reCAPTCHA error: " . $resp->error . ")"); + $action->showForm(sprintf(_("(reCAPTCHA error: %s)", $resp->error))); } $action->showForm(_m("Captcha does not match!")); return false; @@@ -120,4 -118,4 +120,4 @@@ 'captcha to the registration page.')); return true; } -} +}