- $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
- $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
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)
- $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
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
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
);
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);
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),
'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(
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));
}
}
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();
}
* @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
$file_redir->insert();
}
}
-
}
static function memcache() {
- return common_memcache();
+ return Cache::instance();
}
static function cacheKey($cls, $k, $v) {
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) {
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);
+ }
}
}
$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) {
$keyPart = vsprintf($format, $args);
- $cacheKey = common_cache_key($keyPart);
+ $cacheKey = Cache::key($keyPart);
return $c->delete($cacheKey);
}
return false;
}
- $cacheKey = common_cache_key($keyPart);
+ $cacheKey = Cache::key($keyPart);
return $c->get($cacheKey);
}
return false;
}
- $cacheKey = common_cache_key($keyPart);
+ $cacheKey = Cache::key($keyPart);
return $c->set($cacheKey, $value, $flag, $expiry);
}
return $vstr;
}
}
-
function getStreamByIds($ids)
{
- $cache = common_memcache();
+ $cache = Cache::instance();
if (!empty($cache)) {
$notices = array();
1,
1
);
+
if ($conversation->N > 0) {
return true;
}
}
/**
- * @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;
}
$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;
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.
}
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
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 ||
$max_id)));
}
- $idkey = common_cache_key($cachekey);
+ $idkey = Cache::key($cachekey);
$idstr = $cache->get($idkey);
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
$options = array();
if (!empty($location_id) && !empty($location_ns)) {
-
$options['location_id'] = $location_id;
$options['location_ns'] = $location_ns;
}
} else if (!empty($lat) && !empty($lon)) {
-
$options['lat'] = $lat;
$options['lon'] = $lon;
$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;
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();
}
}
{
// 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)) {
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');
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;
}
$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;
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;
}
$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;
}
- $cache = common_memcache();
+ function hasFave($notice)
+ {
++ $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;
}
$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;
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;
}
$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));
}
}
* @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)
{
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)
{
$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);
}
$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))) {
}
$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);
}
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)
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()
if (Subscription::exists($other, $self)) {
Subscription::cancel($other, $self);
}
+ if (Subscription::exists($self, $other)) {
+ Subscription::cancel($self, $other);
+ }
$block->query('COMMIT');
*
* @todo Find some commonalities with SettingsAction and combine
*/
-
class AdminPanelAction extends Action
{
var $success = true;
*
* @return boolean success flag
*/
-
function prepare($args)
{
parent::prepare($args);
*
* @return void
*/
-
function handle($args)
{
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
* @return void
* @see AdminPanelNav
*/
-
function showLocalNav()
{
$nav = new AdminPanelNav($this);
*
* @return void.
*/
-
function showContent()
{
$this->showForm();
*
* @return void
*/
-
function showPageNotice()
{
if ($this->msg) {
*
* @return void
*/
-
function showForm()
{
// TRANS: Client error message.
*
* @return void
*/
-
function getInstructions()
{
return '';
*
* @return void
*/
-
function saveSettings()
{
// TRANS: Client error message
*
* @return mixed $result false if something didn't work
*/
-
function deleteSetting($section, $setting)
{
$config = new Config();
*
* @see Widget
*/
-
class AdminPanelNav extends Widget
{
var $action = null;
*
* @param Action $action current action, used for output
*/
-
function __construct($action=null)
{
parent::__construct($action);
*
* @return void
*/
-
function show()
{
$action_name = $this->action->trimmed('action');
$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');
}
-
}
exit(1);
}
+class ApiValidationException extends Exception { }
+
/**
* Contains most of the Twitter-compatible API output functions.
*
* @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;
*
* @return boolean false if user doesn't exist
*/
-
function prepare($args)
{
StatusNet::setApi(true); // reduce exception reports to aid in debugging
*
* @return void
*/
-
function handle($args)
{
header('Access-Control-Allow-Origin: *');
$entry = array();
if (Event::handle('StartRssEntryArray', array($notice, &$entry))) {
-
$profile = $notice->getProfile();
// We trim() to avoid extraneous whitespace in the output
$notifications = false;
if ($source->isSubscribed($target)) {
-
$sub = Subscription::pkeyGet(array('subscriber' =>
$source->id, 'subscribed' => $target->id));
function showXmlTimeline($notice)
{
-
$this->initDocument('xml');
$this->elementStart('statuses', array('type' => 'array',
'xmlns:statusnet' => 'http://status.net/schema/api/1/'));
function showRssTimeline($notice, $title, $link, $subtitle, $suplink = null, $logo = null, $self = null)
{
-
$this->initDocument('rss');
$this->element('title', null, $title);
function showAtomTimeline($notice, $title, $id, $link, $subtitle=null, $suplink=null, $selfuri=null, $logo=null)
{
-
$this->initDocument('atom');
$this->element('title', null, $title);
}
$this->endDocument('atom');
-
}
function showRssGroups($group, $title, $link, $subtitle)
{
-
$this->initDocument('rss');
$this->element('title', null, $title);
function showAtomGroups($group, $title, $id, $link, $subtitle=null, $selfuri=null)
{
-
$this->initDocument('atom');
$this->element('title', null, common_xml_safe_str($title));
function showJsonTimeline($notice)
{
-
$this->initDocument('json');
$statuses = array();
function showJsonGroups($group)
{
-
$this->initDocument('json');
$groups = array();
function showTwitterXmlUsers($user)
{
-
$this->initDocument('xml');
$this->elementStart('users', array('type' => 'array',
'xmlns:statusnet' => 'http://status.net/schema/api/1/'));
function showJsonUsers($user)
{
-
$this->initDocument('json');
$users = array();
$this->endXML();
break;
case 'json':
-
// Check for JSONP callback
if (isset($this->callback)) {
print ')';
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'));
*/
function arg($key, $def=null)
{
-
// XXX: Do even more input validation/scrubbing?
if (array_key_exists($key, $this->args)) {
return $uri;
}
-
}
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
-
class Cache
{
var $_items = array();
*
* @return Cache cache object
*/
-
static function instance()
{
if (is_null(self::$_inst)) {
*
* @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;
*
* @return string keyized string
*/
-
static function keyize($str)
{
$str = strtolower($str);
*
* @return string retrieved value or null if unfound
*/
-
function get($key)
{
$value = false;
*
* @return boolean success flag
*/
-
function set($key, $value, $flag=null, $expiry=null)
{
$success = false;
*
* @return boolean success flag
*/
-
function delete($key)
{
$success = false;
*
* @return boolean success flag
*/
-
function reconnect()
{
$success = false;
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+ /**
+ * @todo Needs documentation.
+ */
class Channel
{
function on($user)
}
}
-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;
# 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');
{
$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');
{
$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);
class MailChannel extends Channel
{
-
var $addr = null;
function source()
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);
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;
class Command
{
-
var $user = null;
function __construct($user=null)
}
}
-
/**
* Override this with the meat!
*
// TRANS: Text shown when a notice has been marked as favourite successfully.
$channel->output($this->user, _('Notice marked as fave.'));
}
-
}
class JoinCommand extends Command
$cur->nickname,
$group->nickname));
}
-
}
+
class DropCommand extends Command
{
var $other = null;
$cur->nickname,
$group->nickname));
}
-
}
class WhoisCommand extends Command
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));
}
class GetCommand extends Command
{
-
var $other = null;
function __construct($user, $other)
class SubCommand extends Command
{
-
var $other = null;
function __construct($user, $other)
class UnsubCommand extends Command
{
-
var $other = null;
function __construct($user, $other)
class OffCommand extends Command
{
var $other = null;
+
function __construct($user, $other=null)
{
parent::__construct($user);
}
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 {
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 {
$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:',
$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:',
$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:',
{
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".
# 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
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 '<p>'. _('No configuration file found. ') .'</p>';
- echo '<p>'. _('I looked for configuration files in the following places: ') .'<br/> ';
- echo implode($e->configFiles, '<br/>');
+ // 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 '<p>'. _('I looked for configuration files in the following places: ') .'<br /> ';
+ echo implode($e->configFiles, '<br />');
+ // TRANS: Error message displayed when no configuration file was found for a StatusNet installation.
echo '<p>'. _('You may wish to run the installer to fix this.') .'</p>';
+ // @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 '<a href="install.php">'. _('Go to the installer.') .'</a>';
exit;
}
-
*
* @see Widget
*/
-
class ConnectSettingsAction extends SettingsAction
{
/**
*
* @return void
*/
-
function showLocalNav()
{
$menu = new ConnectSettingsNav($this);
*
* @see HTMLOutputter
*/
-
class ConnectSettingsNav extends Widget
{
var $action = null;
*
* @param Action $action current action, used for output
*/
-
function __construct($action=null)
{
parent::__construct($action);
*
* @return void
*/
-
function show()
{
$action_name = $this->action->trimmed('action');
# 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')
);
$this->action->elementEnd('ul');
}
-
}
-
'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),
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.)
$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);
* @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
*
$m->connect('main/xrds',
array('action' => 'publicxrds'));
+ $m->connect('.well-known/host-meta',
+ array('action' => 'hostmeta'));
// these take a code
// 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
$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',
$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
$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'),
/* 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);
textdomain("statusnet");
}
-
function common_timezone()
{
if (common_logged_in()) {
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;
}
}
// 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)) {
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
return $authenticatedUser;
}
- // is the current user logged in?
+ /**
+ * Is the current user logged in?
+ */
function common_logged_in()
{
return (!is_null(common_current_user()));
// 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()) {
function common_remembered_user()
{
-
$user = null;
$packed = isset($_COOKIE[REMEMBERME]) ? $_COOKIE[REMEMBERME] : null;
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;
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();
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)
{
}
if (Event::handle('StartFindMentions', array($sender, $text, &$mentions))) {
-
// Get the context of the original notice, if any
-
$originalAuthor = null;
$originalNotice = null;
$originalMentions = array();
$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.
// 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) {
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)
// 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');
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',
$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))) {
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);
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...?
{
$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)
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)) {
*/
function common_database_tablename($tablename)
{
-
if(common_config('db','quote_identifiers')) {
$tablename = '"'. $tablename .'"';
}
/**
* 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);
}
--- /dev/null
-
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * A sample module to show best practices for StatusNet plugins
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package StatusNet
+ * @author James Walker <james@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
- throw new Exception("Invalid XML.");
+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, missing XRD root.");
++ // TRANS: Exception.
++ throw new Exception(_m('Invalid XML.'));
+ }
+ $xrd_element = $dom->getElementsByTagName('XRD')->item(0);
+ if (!$xrd_element) {
++ // 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;
+ }
+}
exit(1);
}
-require_once INSTALLDIR.'/plugins/UrlShortener/UrlShortenerPlugin.php';
-
class BitlyUrlPlugin extends UrlShortenerPlugin
{
public $serviceUrl;
function onInitializePlugin(){
parent::onInitializePlugin();
if(!isset($this->serviceUrl)){
- throw new Exception("must specify a serviceUrl");
+ throw new Exception(_m("You must specify a serviceUrl."));
}
}
return true;
}
}
-
$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();
$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;
}
}
common_redirect($url, 303);
-
}
}
}
}
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');
}
}
'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;
}
-
}
-
--- /dev/null
- $c = common_memcache();
+ <?php
+ /**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Plugin to convert string locations to Geonames IDs and vice versa
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Action
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @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 <evan@status.net>
+ * @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)
+ {
- common_keyize(implode(',', array_values($attrs)));
++ $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)) . ':'.
- return common_cache_key($key);
++ Cache::keyize(implode(',', array_values($attrs)));
+ if ($this->cachePrefix) {
+ return $this->cachePrefix . ':' . $key;
+ } else {
++ 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 <a href="http://geonames.org/">Geonames</a> 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;
+ }
+ }
*/
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.'));
}
/**
}
/**
- * 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;
}
/**
{
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);
$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.'));
}
}
}
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;
$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;
* @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.
* @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'.
return $str;
}
-
}
class LdapInvalidCredentialsException extends Exception
{
-
}
exit(1);
}
-require_once INSTALLDIR.'/plugins/UrlShortener/UrlShortenerPlugin.php';
-
class LilUrlPlugin extends UrlShortenerPlugin
{
public $serviceUrl;
function onInitializePlugin(){
parent::onInitializePlugin();
if(!isset($this->serviceUrl)){
- throw new Exception("must specify a serviceUrl");
+ throw new Exception(_m('A serviceUrl must be specified.'));
}
}
return true;
}
}
-
*
* @return boolean hook return
*/
-
function onStartInitializeRouter($m)
{
$m->connect('main/min',
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)) {
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)) {
'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;
}
}
-
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;
}
}
{
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)) {
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;
}
}
-
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',
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');
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'));
}
}
$url = $notice->url;
+ // TRANSLATE: %s is a domain.
$title = sprintf(_m("Sent from %s via OStatus"), $domain);
return false;
}
}
if (!$oprofile->subscribe()) {
+ // TRANS: Exception.
throw new Exception(_m('Could not set up remote subscription.'));
}
}
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);
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());
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();
return true;
} else {
$oprofile->garbageCollect();
+ // TRANS: Exception.
throw new Exception(_m("Failed joining remote group."));
}
}
* @param Notice $notice being favored
* @return hook return value
*/
-
function onEndFavorNotice(Profile $profile, Notice $notice)
{
$user = User::staticGet('id', $profile->id);
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);
$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);
$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');
}
$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.
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');
}
}
'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 '.
- '<a href="http://ostatus.org/">OStatus</a>.')); // @todo i18n: Add translator hint.
+ // TRANS: Plugin description.
+ 'rawdescription' => _m('Follow people across social networks that implement '.
+ '<a href="http://ostatus.org/">OStatus</a>.'));
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
*/
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;
}
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'));
+ }
}
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)) {
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
*
/**
* 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
*
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');
}
}
}
- function checkssl(){
+ function checkssl()
+ {
if(common_config('site', 'ssl') === 'sometimes' || common_config('site', 'ssl') === 'always') {
return true;
}
function onEndRegistrationFormData($action)
{
$action->elementStart('li');
- $action->raw('<label for="recaptcha">Captcha</label>');
+ $action->raw('<label for="recaptcha">'._m('Captcha').'</label>');
// 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;
}
$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');});");
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;
'captcha to the registration page.'));
return true;
}
-}
+}