From d634f9cf17f683c38150d64d29b47975b5aeac70 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Tue, 10 Aug 2010 17:03:47 -0700 Subject: Notice::asActivity --- classes/Notice.php | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) (limited to 'classes/Notice.php') diff --git a/classes/Notice.php b/classes/Notice.php index f1b012465..9a6f180e5 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -1212,6 +1212,64 @@ class Notice extends Memcached_DataObject return $groups; } + function asActivity() + { + $profile = $this->getProfile(); + + $act = new Activity(); + + $act->actor = Activity::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_string($this->rendered); + $act->id = $this->uri; + $act->title = common_xml_safe_string($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->uri; + } + } + + $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. -- cgit v1.2.3-54-g00ecf From 9f4891568f458a8135d59820b47b91e3f89ab93b Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 13 Sep 2010 16:27:02 -0400 Subject: bugs in function calls in Notice::asActivity --- classes/Notice.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'classes/Notice.php') diff --git a/classes/Notice.php b/classes/Notice.php index 9a6f180e5..0539ca3b1 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -1218,16 +1218,16 @@ class Notice extends Memcached_DataObject $act = new Activity(); - $act->actor = Activity::fromProfile($profile); + $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_string($this->rendered); + $act->content = common_xml_safe_str($this->rendered); $act->id = $this->uri; - $act->title = common_xml_safe_string($this->content); + $act->title = common_xml_safe_str($this->content); $ctx = new ActivityContext(); -- cgit v1.2.3-54-g00ecf From 521daf5562051d0cc319c35a0dceaae3e0fd49b7 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 20 Sep 2010 15:57:46 -0700 Subject: Ticket #2327: fixing block to remove the blocking user's subscription to the blockee if present; also cleaning up inbox delivery to apply the block checks more consistently, instead of just to group posts. --- classes/Notice.php | 33 ++++++++++++++++++++------------- classes/User.php | 3 +++ 2 files changed, 23 insertions(+), 13 deletions(-) (limited to 'classes/Notice.php') diff --git a/classes/Notice.php b/classes/Notice.php index f1b012465..13b322828 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -753,8 +753,15 @@ class Notice extends Memcached_DataObject } /** - * @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) { @@ -787,27 +794,27 @@ class Notice extends Memcached_DataObject $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]); } } diff --git a/classes/User.php b/classes/User.php index 8033229c4..4c25ee3d9 100644 --- a/classes/User.php +++ b/classes/User.php @@ -552,6 +552,9 @@ class User extends Memcached_DataObject if (Subscription::exists($other, $self)) { Subscription::cancel($other, $self); } + if (Subscription::exists($self, $other)) { + Subscription::cancel($self, $other); + } $block->query('COMMIT'); -- cgit v1.2.3-54-g00ecf From 556a2a8fd8ce278f5c0b5ced9e762c6f9bac8659 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Tue, 21 Sep 2010 06:21:47 -0400 Subject: use Profile::getUri() to get a profile's URI --- classes/Notice.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'classes/Notice.php') diff --git a/classes/Notice.php b/classes/Notice.php index 0539ca3b1..04dcd24cd 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -1255,7 +1255,7 @@ class Notice extends Memcached_DataObject foreach ($reply_ids as $id) { $profile = Profile::staticGet('id', $id); if (!empty($profile)) { - $ctx->attention[] = $profile->uri; + $ctx->attention[] = $profile->getUri(); } } -- cgit v1.2.3-54-g00ecf From b03ece26eb1589c5200094f4d87455ca46db780b Mon Sep 17 00:00:00 2001 From: Siebrand Mazeland Date: Tue, 28 Sep 2010 23:21:09 +0200 Subject: * i18n/L10n and translator documentation updates. * whitespace and indentation updates --- classes/Avatar.php | 5 +++-- classes/Consumer.php | 1 - classes/Conversation.php | 2 -- classes/Fave.php | 5 ++++- classes/File.php | 1 - classes/File_oembed.php | 1 - classes/File_redirection.php | 1 - classes/File_thumbnail.php | 1 - classes/File_to_post.php | 1 - classes/Foreign_link.php | 2 -- classes/Foreign_service.php | 4 ++-- classes/Foreign_subscription.php | 2 +- classes/Foreign_user.php | 1 - classes/Group_block.php | 1 - classes/Group_inbox.php | 2 +- classes/Group_member.php | 9 +++++++-- classes/Inbox.php | 9 ++++----- classes/Invitation.php | 2 +- classes/Memcached_DataObject.php | 9 ++++----- classes/Message.php | 1 - classes/Nonce.php | 1 - classes/Notice.php | 7 +------ classes/Notice_source.php | 2 +- classes/Oauth_application.php | 2 -- classes/Oauth_application_user.php | 1 - 25 files changed, 29 insertions(+), 44 deletions(-) (limited to 'classes/Notice.php') diff --git a/classes/Avatar.php b/classes/Avatar.php index dbe2cd813..6edc81768 100644 --- a/classes/Avatar.php +++ b/classes/Avatar.php @@ -42,8 +42,9 @@ class Avatar extends Memcached_DataObject return Memcached_DataObject::pkeyGet('Avatar', $kv); } - // where should the avatar go for this user? - + /** + * Where should the avatar go for this user? + */ static function filename($id, $extension, $size=null, $extra=null) { if ($size) { diff --git a/classes/Consumer.php b/classes/Consumer.php index ce399f278..c1090b85a 100644 --- a/classes/Consumer.php +++ b/classes/Consumer.php @@ -65,5 +65,4 @@ class Consumer extends Memcached_DataObject $nonce->consumer_key = $this->consumer_key; $nonce->delete(); } - } diff --git a/classes/Conversation.php b/classes/Conversation.php index f540004ef..aab55723f 100755 --- a/classes/Conversation.php +++ b/classes/Conversation.php @@ -74,6 +74,4 @@ class Conversation extends Memcached_DataObject return $conv; } - } - diff --git a/classes/Fave.php b/classes/Fave.php index f21f1b529..059b339cd 100644 --- a/classes/Fave.php +++ b/classes/Fave.php @@ -144,8 +144,11 @@ class Fave extends Memcached_DataObject common_date_iso8601($this->modified)); $act->time = strtotime($this->modified); + // TRANS: Activity title when marking a notice as favorite. $act->title = _("Favor"); - $act->content = sprintf(_("%s marked notice %s as a favorite."), + // TRANS: Ntofication given when a user marks a notice as favorite. + // TRANS: %1$s is a user nickname or full name, %2$s is a notice URI. + $act->content = sprintf(_("%1$s marked notice %2$s as a favorite."), $profile->getBestName(), $notice->uri); diff --git a/classes/File.php b/classes/File.php index 407fd3211..d457968b5 100644 --- a/classes/File.php +++ b/classes/File.php @@ -29,7 +29,6 @@ require_once INSTALLDIR.'/classes/File_to_post.php'; /** * Table Definition for file */ - class File extends Memcached_DataObject { ###START_AUTOCODE diff --git a/classes/File_oembed.php b/classes/File_oembed.php index 041b44740..4813d5dda 100644 --- a/classes/File_oembed.php +++ b/classes/File_oembed.php @@ -131,4 +131,3 @@ class File_oembed extends Memcached_DataObject } } } - diff --git a/classes/File_redirection.php b/classes/File_redirection.php index 51b8be3b0..68fed77e8 100644 --- a/classes/File_redirection.php +++ b/classes/File_redirection.php @@ -281,4 +281,3 @@ class File_redirection extends Memcached_DataObject $file_redir->insert(); } } - diff --git a/classes/File_thumbnail.php b/classes/File_thumbnail.php index f8b70356c..edae8ac21 100644 --- a/classes/File_thumbnail.php +++ b/classes/File_thumbnail.php @@ -57,4 +57,3 @@ class File_thumbnail extends Memcached_DataObject $tn->insert(); } } - diff --git a/classes/File_to_post.php b/classes/File_to_post.php index 72a42b088..530921adc 100644 --- a/classes/File_to_post.php +++ b/classes/File_to_post.php @@ -67,4 +67,3 @@ class File_to_post extends Memcached_DataObject return Memcached_DataObject::pkeyGet('File_to_post', $kv); } } - diff --git a/classes/Foreign_link.php b/classes/Foreign_link.php index e47b2e309..60db51595 100644 --- a/classes/Foreign_link.php +++ b/classes/Foreign_link.php @@ -44,7 +44,6 @@ class Foreign_link extends Memcached_DataObject $result = $flink->find(true); return empty($result) ? null : $flink; - } static function getByForeignID($foreign_id, $service) @@ -129,5 +128,4 @@ class Foreign_link extends Memcached_DataObject return false; } } - } diff --git a/classes/Foreign_service.php b/classes/Foreign_service.php index ef614dbd6..dd74fd2ca 100644 --- a/classes/Foreign_service.php +++ b/classes/Foreign_service.php @@ -4,7 +4,7 @@ */ require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; -class Foreign_service extends Memcached_DataObject +class Foreign_service extends Memcached_DataObject { ###START_AUTOCODE /* the code below is auto generated do not remove the above tag */ @@ -12,7 +12,7 @@ class Foreign_service extends Memcached_DataObject public $__table = 'foreign_service'; // table name public $id; // int(4) primary_key not_null public $name; // varchar(32) unique_key not_null - public $description; // varchar(255) + public $description; // varchar(255) public $created; // datetime() not_null public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP diff --git a/classes/Foreign_subscription.php b/classes/Foreign_subscription.php index d50860621..ec2631238 100644 --- a/classes/Foreign_subscription.php +++ b/classes/Foreign_subscription.php @@ -4,7 +4,7 @@ */ require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; -class Foreign_subscription extends Memcached_DataObject +class Foreign_subscription extends Memcached_DataObject { ###START_AUTOCODE /* the code below is auto generated do not remove the above tag */ diff --git a/classes/Foreign_user.php b/classes/Foreign_user.php index e98a16064..8e6e0b33e 100644 --- a/classes/Foreign_user.php +++ b/classes/Foreign_user.php @@ -83,5 +83,4 @@ class Foreign_user extends Memcached_DataObject } return $result; } - } diff --git a/classes/Group_block.php b/classes/Group_block.php index 9f4d59295..ffc57a496 100644 --- a/classes/Group_block.php +++ b/classes/Group_block.php @@ -111,5 +111,4 @@ class Group_block extends Memcached_DataObject return true; } - } diff --git a/classes/Group_inbox.php b/classes/Group_inbox.php index 2a0787e38..8f5c65e59 100644 --- a/classes/Group_inbox.php +++ b/classes/Group_inbox.php @@ -1,8 +1,8 @@ profile_id); if (empty($member)) { - throw new Exception("Profile ID {$this->profile_id} invalid."); + // TRANS: Exception thrown providing an invalid profile ID. + // TRANS: %s is the invalid profile ID. + throw new Exception(sprintf(_("Profile ID %s is invalid."),$this->profile_id)); } return $member; @@ -82,7 +84,9 @@ class Group_member extends Memcached_DataObject $group = User_group::staticGet('id', $this->group_id); if (empty($group)) { - throw new Exception("Group ID {$this->group_id} invalid."); + // TRANS: Exception thrown providing an invalid group ID. + // TRANS: %s is the invalid group ID. + throw new Exception(sprintf(_("Group ID %s is invalid."),$this->group_id)); } return $group; @@ -105,6 +109,7 @@ class Group_member extends Memcached_DataObject $act->objects[] = ActivityObject::fromGroup($group); $act->time = strtotime($this->created); + // TRANS: Activity title. $act->title = _("Join"); // TRANS: Success message for subscribe to group attempt through OStatus. diff --git a/classes/Inbox.php b/classes/Inbox.php index 430419ba5..a1ab6215f 100644 --- a/classes/Inbox.php +++ b/classes/Inbox.php @@ -55,7 +55,6 @@ class Inbox extends Memcached_DataObject /** * Create a new inbox from existing Notice_inbox stuff */ - static function initialize($user_id) { $inbox = Inbox::fromNoticeInbox($user_id); @@ -115,10 +114,10 @@ class Inbox extends Memcached_DataObject */ static function insertNotice($user_id, $notice_id) { - // Going straight to the DB rather than trusting our caching - // during an update. Note: not using DB_DataObject::staticGet, - // which is unsafe to use directly (in-process caching causes - // memory leaks, which accumulate in queue processes). + // Going straight to the DB rather than trusting our caching + // during an update. Note: not using DB_DataObject::staticGet, + // which is unsafe to use directly (in-process caching causes + // memory leaks, which accumulate in queue processes). $inbox = new Inbox(); if (!$inbox->get('user_id', $user_id)) { $inbox = Inbox::initialize($user_id); diff --git a/classes/Invitation.php b/classes/Invitation.php index 8a36fd8df..0e87c1629 100644 --- a/classes/Invitation.php +++ b/classes/Invitation.php @@ -4,7 +4,7 @@ */ require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; -class Invitation extends Memcached_DataObject +class Invitation extends Memcached_DataObject { ###START_AUTOCODE /* the code below is auto generated do not remove the above tag */ diff --git a/classes/Memcached_DataObject.php b/classes/Memcached_DataObject.php index 0f1ed0489..ccfd886a1 100644 --- a/classes/Memcached_DataObject.php +++ b/classes/Memcached_DataObject.php @@ -189,11 +189,11 @@ class Memcached_DataObject extends Safe_DataObject 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); + } } } @@ -637,4 +637,3 @@ class Memcached_DataObject extends Safe_DataObject return $vstr; } } - diff --git a/classes/Message.php b/classes/Message.php index fa0c5b318..353dc01f9 100644 --- a/classes/Message.php +++ b/classes/Message.php @@ -38,7 +38,6 @@ class Message extends Memcached_DataObject } static function saveNew($from, $to, $content, $source) { - $sender = Profile::staticGet('id', $from); if (!$sender->hasRight(Right::NEWMESSAGE)) { diff --git a/classes/Nonce.php b/classes/Nonce.php index 2f8ab00b5..93191bd40 100644 --- a/classes/Nonce.php +++ b/classes/Nonce.php @@ -36,5 +36,4 @@ class Nonce extends Memcached_DataObject { return array('consumer_key,token' => 'token:consumer_key,token'); } - } diff --git a/classes/Notice.php b/classes/Notice.php index 4f23e3500..79626f889 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -745,6 +745,7 @@ class Notice extends Memcached_DataObject 1, 1 ); + if ($conversation->N > 0) { return true; } @@ -1305,13 +1306,10 @@ class Notice extends Memcached_DataObject } 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 @@ -1899,7 +1897,6 @@ class Notice extends Memcached_DataObject $options = array(); if (!empty($location_id) && !empty($location_ns)) { - $options['location_id'] = $location_id; $options['location_ns'] = $location_ns; @@ -1911,7 +1908,6 @@ class Notice extends Memcached_DataObject } } else if (!empty($lat) && !empty($lon)) { - $options['lat'] = $lat; $options['lon'] = $lon; @@ -1922,7 +1918,6 @@ class Notice extends Memcached_DataObject $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; diff --git a/classes/Notice_source.php b/classes/Notice_source.php index e7568bbca..43893ebe1 100644 --- a/classes/Notice_source.php +++ b/classes/Notice_source.php @@ -4,7 +4,7 @@ */ require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; -class Notice_source extends Memcached_DataObject +class Notice_source extends Memcached_DataObject { ###START_AUTOCODE /* the code below is auto generated do not remove the above tag */ diff --git a/classes/Oauth_application.php b/classes/Oauth_application.php index 748b64220..e81706104 100644 --- a/classes/Oauth_application.php +++ b/classes/Oauth_application.php @@ -110,7 +110,6 @@ class Oauth_application extends Memcached_DataObject * * @return void */ - function uploadLogo() { if ($_FILES['app_icon']['error'] == @@ -153,5 +152,4 @@ class Oauth_application extends Memcached_DataObject $oauser->application_id = $this->id; $oauser->delete(); } - } diff --git a/classes/Oauth_application_user.php b/classes/Oauth_application_user.php index 57986281f..3d4238d64 100644 --- a/classes/Oauth_application_user.php +++ b/classes/Oauth_application_user.php @@ -40,5 +40,4 @@ class Oauth_application_user extends Memcached_DataObject return empty($result) ? null : $oau; } - } -- cgit v1.2.3-54-g00ecf From 55a080ea4e662ae719e8956c93389f3c689bb73a Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 30 Sep 2010 16:25:15 -0700 Subject: ForceGroup plugin: optionally force new users to join a particular group or set of groups on registration; and/or to force posts by members of particular groups to be posted into those groups even if not explicitly mentioned. The posting feature requires a couple quick hook additions in core. --- classes/Notice.php | 1 + lib/distribqueuehandler.php | 8 +++- plugins/ForceGroup/ForceGroupPlugin.php | 82 +++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 plugins/ForceGroup/ForceGroupPlugin.php (limited to 'classes/Notice.php') diff --git a/classes/Notice.php b/classes/Notice.php index 79626f889..e268544b5 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -2034,6 +2034,7 @@ class Notice extends Memcached_DataObject { // 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 --git a/lib/distribqueuehandler.php b/lib/distribqueuehandler.php index 8f4b72d5c..a7519c1d5 100644 --- a/lib/distribqueuehandler.php +++ b/lib/distribqueuehandler.php @@ -77,14 +77,20 @@ class DistribQueueHandler $this->logit($notice, $e); } + try { + Event::handle('EndNoticeDistribute', array($notice)); + } catch (Exception $e) { + $this->logit($notice, $e); + } + try { Event::handle('EndNoticeSave', array($notice)); - // Enqueue for other handlers } catch (Exception $e) { $this->logit($notice, $e); } try { + // Enqueue for other handlers common_enqueue_notice($notice); } catch (Exception $e) { $this->logit($notice, $e); diff --git a/plugins/ForceGroup/ForceGroupPlugin.php b/plugins/ForceGroup/ForceGroupPlugin.php new file mode 100644 index 000000000..e0a04fcca --- /dev/null +++ b/plugins/ForceGroup/ForceGroupPlugin.php @@ -0,0 +1,82 @@ +. + */ + +/** + * @package ForceGroupPlugin + * @maintainer Brion Vibber + */ + +if (!defined('STATUSNET')) { exit(1); } + +class ForceGroupPlugin extends Plugin +{ + /** + * Members of these groups will have all their posts mirrored into + * the group even if they don't explicitly mention it. + * + * List by local nickname. + */ + public $post = array(); + + /** + * New user registrations will automatically join these groups on + * registration. They're not prevented from leaving, however. + * + * List by local nickname. + */ + public $join = array(); + + /** + * If poster is in one of the forced groups, make sure their notice + * gets saved into that group even if not explicitly mentioned. + * + * @param Notice $notice + * @return boolean event hook return + */ + function onStartNoticeDistribute($notice) + { + $profile = $notice->getProfile(); + foreach ($this->post as $nickname) { + $group = User_group::getForNickname($nickname); + if ($group && $profile->isMember($group)) { + $notice->addToGroupInbox($group); + } + } + return true; + } + + function onEndUserRegister($profile, $user) + { + $profile = $user->getProfile(); + foreach ($this->join as $nickname) { + $group = User_group::getForNickname($nickname); + if ($group && !$profile->isMember($group)) { + try { + if (Event::handle('StartJoinGroup', array($group, $user))) { + Group_member::join($group->id, $user->id); + Event::handle('EndJoinGroup', array($group, $user)); + } + } catch (Exception $e) { + throw new ServerException(sprintf(_('Could not join user %1$s to group %2$s.'), + $user->nickname, $group->nickname)); + } + } + } + } +} -- cgit v1.2.3-54-g00ecf