From: Mikael Nordfeldth Date: Sat, 27 Aug 2016 13:24:25 +0000 (+0200) Subject: TagCloud turned into plugin (performance issues on large installs) X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=a32bfe7d87ae513ae5762db5cde628c87333b5aa;p=quix0rs-gnu-social.git TagCloud turned into plugin (performance issues on large installs) --- diff --git a/actions/all.php b/actions/all.php index 19413076b5..84e4dd5305 100644 --- a/actions/all.php +++ b/actions/all.php @@ -170,12 +170,6 @@ class AllAction extends ShowstreamAction } $ibs->show(); } - // XXX: make this a little more convenient - - if (!common_config('performance', 'high')) { - $pop = new InboxTagCloudSection($this, $this->target); - $pop->show(); - } } } diff --git a/actions/attachment.php b/actions/attachment.php index 3ec837a511..3f2ae5c1ce 100644 --- a/actions/attachment.php +++ b/actions/attachment.php @@ -132,9 +132,5 @@ class AttachmentAction extends ManagedAction function showSections() { $ns = new AttachmentNoticeSection($this); $ns->show(); - if (!common_config('performance', 'high')) { - $atcs = new AttachmentTagCloudSection($this); - $atcs->show(); - } } } diff --git a/actions/public.php b/actions/public.php index 000f82cb93..a2958e8806 100644 --- a/actions/public.php +++ b/actions/public.php @@ -86,12 +86,6 @@ class PublicAction extends SitestreamAction $ibs->show(); } - $p = Profile::current(); - - if (!common_config('performance', 'high')) { - $cloud = new PublicTagCloudSection($this); - $cloud->show(); - } $feat = new FeaturedUsersSection($this); $feat->show(); } diff --git a/actions/publictagcloud.php b/actions/publictagcloud.php deleted file mode 100644 index e557b75fd0..0000000000 --- a/actions/publictagcloud.php +++ /dev/null @@ -1,179 +0,0 @@ -. - * - * @category Public - * @package StatusNet - * @author Mike Cochrane - * @author Evan Prodromou - * @copyright 2008 Mike Cochrane - * @copyright 2008-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') && !defined('LACONICA')) { exit(1); } - -define('TAGS_PER_PAGE', 100); - -/** - * Public tag cloud for notices - * - * @category Personal - * @package StatusNet - * @author Mike Cochrane - * @author Evan Prodromou - * @copyright 2008 Mike Cochrane - * @copyright 2008-2009 StatusNet, Inc. - * @link http://status.net/ - */ -class PublictagcloudAction extends Action -{ - function isReadOnly($args) - { - return true; - } - - function title() - { - // TRANS: Title for public tag cloud. - return _('Public tag cloud'); - } - - function showPageNotice() - { - $this->element('p', 'instructions', - // TRANS: Instructions (more used like an explanation/header). - // TRANS: %s is the StatusNet sitename. - sprintf(_('These are most popular recent tags on %s'), - common_config('site', 'name'))); - } - - function showEmptyList() - { - // TRANS: This message contains a Markdown URL. The link description is between - // TRANS: square brackets, and the link between parentheses. Do not separate "](" - // TRANS: and do not change the URL part. - $message = _('No one has posted a notice with a [hashtag](%%doc.tags%%) yet.') . ' '; - - if (common_logged_in()) { - // TRANS: Message shown to a logged in user for the public tag cloud - // TRANS: while no tags exist yet. "One" refers to the non-existing hashtag. - $message .= _('Be the first to post one!'); - } - else { - // TRANS: Message shown to a anonymous user for the public tag cloud - // TRANS: while no tags exist yet. "One" refers to the non-existing hashtag. - // TRANS: This message contains a Markdown URL. The link description is between - // TRANS: square brackets, and the link between parentheses. Do not separate "](" - // TRANS: and do not change the URL part. - $message .= _('Why not [register an account](%%action.register%%) and be the first to post one!'); - } - - $this->elementStart('div', 'guide'); - $this->raw(common_markup_to_html($message)); - $this->elementEnd('div'); - } - - function handle() - { - parent::handle(); - $this->showPage(); - } - - function showContent() - { - // This should probably be cached rather than recalculated - $tags = new Notice_tag(); - - #Need to clear the selection and then only re-add the field - #we are grouping by, otherwise it's not a valid 'group by' - #even though MySQL seems to let it slide... - $tags->selectAdd(); - $tags->selectAdd('tag'); - - #Add the aggregated columns... - $tags->selectAdd('max(notice_id) as last_notice_id'); - $calc = common_sql_weight('created', common_config('tag', 'dropoff')); - $cutoff = sprintf("notice_tag.created > '%s'", - common_sql_date(time() - common_config('tag', 'cutoff'))); - $tags->selectAdd($calc . ' as weight'); - $tags->whereAdd($cutoff); - $tags->groupBy('tag'); - $tags->orderBy('weight DESC'); - - $tags->limit(TAGS_PER_PAGE); - - $cnt = $tags->find(); - - if ($cnt > 0) { - $this->elementStart('div', array('id' => 'tagcloud', - 'class' => 'section')); - - $tw = array(); - $sum = 0; - while ($tags->fetch()) { - $tw[$tags->tag] = $tags->weight; - $sum += $tags->weight; - } - - ksort($tw); - - $this->elementStart('ul', 'tags xoxo tag-cloud'); - foreach ($tw as $tag => $weight) { - if ($sum) { - $weightedSum = $weight/$sum; - } else { - $weightedSum = 0.5; - } - $this->showTag($tag, $weight, $weightedSum); - } - $this->elementEnd('ul'); - - $this->elementEnd('div'); - } else { - $this->showEmptyList(); - } - } - - function showTag($tag, $weight, $relative) - { - if ($relative > 0.1) { - $rel = 'tag-cloud-7'; - } else if ($relative > 0.05) { - $rel = 'tag-cloud-6'; - } else if ($relative > 0.02) { - $rel = 'tag-cloud-5'; - } else if ($relative > 0.01) { - $rel = 'tag-cloud-4'; - } else if ($relative > 0.005) { - $rel = 'tag-cloud-3'; - } else if ($relative > 0.002) { - $rel = 'tag-cloud-2'; - } else { - $rel = 'tag-cloud-1'; - } - - $this->elementStart('li', $rel); - $this->element('a', array('href' => common_local_url('tag', array('tag' => $tag))), - $tag); - $this->elementEnd('li'); - } -} diff --git a/actions/showstream.php b/actions/showstream.php index 97b21b1286..1e70ecd3ac 100644 --- a/actions/showstream.php +++ b/actions/showstream.php @@ -251,15 +251,6 @@ class ShowstreamAction extends NoticestreamAction $this->elementEnd('div'); } - function showSections() - { - parent::showSections(); - if (!common_config('performance', 'high')) { - $cloud = new PersonalTagCloudSection($this->target, $this); - $cloud->show(); - } - } - function noticeFormOptions() { $options = parent::noticeFormOptions(); diff --git a/lib/attachmenttagcloudsection.php b/lib/attachmenttagcloudsection.php deleted file mode 100644 index 2c8fc5ee1a..0000000000 --- a/lib/attachmenttagcloudsection.php +++ /dev/null @@ -1,82 +0,0 @@ -. - * - * @category Widget - * @package StatusNet - * @author Evan Prodromou - * @copyright 2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET') && !defined('LACONICA')) { - exit(1); -} - -/** - * Attachment tag cloud section - * - * @category Widget - * @package StatusNet - * @author Evan Prodromou - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ -class AttachmentTagCloudSection extends TagCloudSection -{ - function title() - { - // TRANS: Title. - return _('Tags for this attachment'); - } - - function showTag($tag, $weight, $relative) - { - if ($relative > 0.5) { - $rel = 'tag-cloud-7'; - } else if ($relative > 0.4) { - $rel = 'tag-cloud-6'; - } else if ($relative > 0.3) { - $rel = 'tag-cloud-5'; - } else if ($relative > 0.2) { - $rel = 'tag-cloud-4'; - } else if ($relative > 0.1) { - $rel = 'tag-cloud-3'; - } else if ($relative > 0.05) { - $rel = 'tag-cloud-2'; - } else { - $rel = 'tag-cloud-1'; - } - - $this->out->elementStart('li', $rel); - $this->out->element('a', array('href' => $this->tagUrl($tag)), - $tag); - $this->out->elementEnd('li'); - } - - function getTags() - { - $notice_tag = new Notice_tag; - $query = 'select tag,count(tag) as weight from notice_tag join file_to_post on (notice_tag.notice_id=post_id) join notice on notice_id = notice.id where file_id=' . $notice_tag->escape($this->out->attachment->id) . ' group by tag order by weight desc'; - $notice_tag->query($query); - return $notice_tag; - } -} diff --git a/lib/groupaction.php b/lib/groupaction.php index 0886460737..31a0b8bc34 100644 --- a/lib/groupaction.php +++ b/lib/groupaction.php @@ -112,11 +112,6 @@ class GroupAction extends Action } $this->showAdmins(); - - if (!common_config('performance', 'high')) { - $cloud = new GroupTagCloudSection($this, $this->group); - $cloud->show(); - } } /** diff --git a/lib/grouptagcloudsection.php b/lib/grouptagcloudsection.php deleted file mode 100644 index 5ed14560ce..0000000000 --- a/lib/grouptagcloudsection.php +++ /dev/null @@ -1,105 +0,0 @@ -. - * - * @category Widget - * @package StatusNet - * @author Evan Prodromou - * @copyright 2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET') && !defined('LACONICA')) { - exit(1); -} - -/** - * Group tag cloud section - * - * @category Widget - * @package StatusNet - * @author Evan Prodromou - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ -class GroupTagCloudSection extends TagCloudSection -{ - var $group = null; - - function __construct($out=null, $group=null) - { - parent::__construct($out); - $this->group = $group; - } - - function title() - { - // TRANS: Title for group tag cloud section. - // TRANS: %s is a group name. - return _('Tags'); - } - - function getTags() - { - $weightexpr = common_sql_weight('notice_tag.created', common_config('tag', 'dropoff')); - // @fixme should we use the cutoff too? Doesn't help with indexing per-group. - - $names = $this->group->getAliases(); - - $names = array_merge(array($this->group->nickname), $names); - - // XXX This is dumb. - - $quoted = array(); - - foreach ($names as $name) { - $quoted[] = "'$name'"; - } - - $namestring = implode(',', $quoted); - - $qry = 'SELECT notice_tag.tag, '. - $weightexpr . ' as weight ' . - 'FROM notice_tag JOIN notice ' . - 'ON notice_tag.notice_id = notice.id ' . - 'JOIN group_inbox on group_inbox.notice_id = notice.id ' . - 'WHERE group_inbox.group_id = %d ' . - 'AND notice_tag.tag not in (%s) '. - 'GROUP BY notice_tag.tag ' . - 'ORDER BY weight DESC '; - - $limit = TAGS_PER_SECTION; - $offset = 0; - - if (common_config('db','type') == 'pgsql') { - $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; - } else { - $qry .= ' LIMIT ' . $offset . ', ' . $limit; - } - - $tag = Memcached_DataObject::cachedQuery('Notice_tag', - sprintf($qry, - $this->group->id, - $namestring), - 3600); - return $tag; - } -} diff --git a/lib/inboxtagcloudsection.php b/lib/inboxtagcloudsection.php deleted file mode 100644 index 4268ee4854..0000000000 --- a/lib/inboxtagcloudsection.php +++ /dev/null @@ -1,107 +0,0 @@ -. - * - * @category Widget - * @package StatusNet - * @author Evan Prodromou - * @copyright 2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('GNUSOCIAL')) { exit(1); } - -/** - * Personal tag cloud section - * - * @category Widget - * @package StatusNet - * @author Evan Prodromou - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ -class InboxTagCloudSection extends TagCloudSection -{ - const MAX_NOTICES = 1024; // legacy value for "Inbox" table size when that existed - - protected $target = null; - - function __construct($out=null, Profile $target) - { - parent::__construct($out); - $this->target = $target; - } - - function title() - { - // TRANS: Title for inbox tag cloud section. - return _m('TITLE', 'Trends'); - } - - function getTags() - { - // FIXME: Get the Profile::current() value some other way - // to avoid confusion between background stuff and session. - $stream = new InboxNoticeStream($this->target, Profile::current()); - - $ids = $stream->getNoticeIds(0, self::MAX_NOTICES, null, null); - - if (empty($ids)) { - $tag = array(); - } else { - $weightexpr = common_sql_weight('notice_tag.created', common_config('tag', 'dropoff')); - // @fixme should we use the cutoff too? Doesn't help with indexing per-user. - - $qry = 'SELECT notice_tag.tag, '. - $weightexpr . ' as weight ' . - 'FROM notice_tag JOIN notice ' . - 'ON notice_tag.notice_id = notice.id ' . - 'WHERE notice.id in (' . implode(',', $ids) . ')'. - 'GROUP BY notice_tag.tag ' . - 'ORDER BY weight DESC '; - - $limit = TAGS_PER_SECTION; - $offset = 0; - - if (common_config('db','type') == 'pgsql') { - $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; - } else { - $qry .= ' LIMIT ' . $offset . ', ' . $limit; - } - - $t = new Notice_tag(); - - $t->query($qry); - - $tag = array(); - - while ($t->fetch()) { - $tag[] = clone($t); - } - } - - return new ArrayWrapper($tag); - } - - function showMore() - { - } -} diff --git a/lib/personaltagcloudsection.php b/lib/personaltagcloudsection.php deleted file mode 100644 index 46b4661e89..0000000000 --- a/lib/personaltagcloudsection.php +++ /dev/null @@ -1,85 +0,0 @@ -. - * - * @category Widget - * @package StatusNet - * @author Evan Prodromou - * @copyright 2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('GNUSOCIAL')) { exit(1); } - -/** - * Personal tag cloud section - * - * @category Widget - * @package StatusNet - * @author Evan Prodromou - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ -class PersonalTagCloudSection extends TagCloudSection -{ - protected $profile = null; - - function __construct(Profile $profile, HTMLOutputter $out=null) - { - parent::__construct($out); - $this->profile = $profile; - } - - function title() - { - // TRANS: Title for personal tag cloud section. - return _m('TITLE','Tags'); - } - - function getTags() - { - $weightexpr = common_sql_weight('notice_tag.created', common_config('tag', 'dropoff')); - // @fixme should we use the cutoff too? Doesn't help with indexing per-user. - - $qry = 'SELECT notice_tag.tag, '. - $weightexpr . ' as weight ' . - 'FROM notice_tag JOIN notice ' . - 'ON notice_tag.notice_id = notice.id ' . - 'WHERE notice.profile_id = %d ' . - 'GROUP BY notice_tag.tag ' . - 'ORDER BY weight DESC '; - - $limit = TAGS_PER_SECTION; - $offset = 0; - - if (common_config('db','type') == 'pgsql') { - $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; - } else { - $qry .= ' LIMIT ' . $offset . ', ' . $limit; - } - - $tag = Memcached_DataObject::cachedQuery('Notice_tag', - sprintf($qry, - $this->profile->getID()), - 3600); - return $tag; - } -} diff --git a/lib/publicgroupnav.php b/lib/publicgroupnav.php index 620a61ddd9..ef342839d8 100644 --- a/lib/publicgroupnav.php +++ b/lib/publicgroupnav.php @@ -77,13 +77,6 @@ class PublicGroupNav extends Menu // TRANS: Menu item title in search group navigation panel. _('User groups'), $this->actionName == 'groups', 'nav_groups'); - if (!common_config('performance', 'high')) { - // TRANS: Menu item in search group navigation panel. - $this->out->menuItem(common_local_url('publictagcloud'), _m('MENU','Recent tags'), - // TRANS: Menu item title in search group navigation panel. - _('Recent tags'), $this->actionName == 'publictagcloud', 'nav_recent-tags'); - } - if (count(common_config('nickname', 'featured')) > 0) { // TRANS: Menu item in search group navigation panel. $this->out->menuItem(common_local_url('featured'), _m('MENU','Featured'), diff --git a/lib/publictagcloudsection.php b/lib/publictagcloudsection.php deleted file mode 100644 index bf88091a90..0000000000 --- a/lib/publictagcloudsection.php +++ /dev/null @@ -1,117 +0,0 @@ -. - * - * @category Widget - * @package StatusNet - * @author Evan Prodromou - * @copyright 2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET') && !defined('LACONICA')) { - exit(1); -} - -/** - * Public tag cloud section - * - * @category Widget - * @package StatusNet - * @author Evan Prodromou - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ -class PublicTagCloudSection extends TagCloudSection -{ - function __construct($out=null) - { - parent::__construct($out); - } - - function title() - { - // TRANS: Title for inbox tag cloud section. - return _m('TITLE', 'Trends'); - } - - function getTags() - { - $profile = Profile::current(); - - if (empty($profile)) { - $keypart = sprintf('Notice:public_tag_cloud:null'); - } else { - $keypart = sprintf('Notice:public_tag_cloud:%d', $profile->id); - } - - $tag = Memcached_DataObject::cacheGet($keypart); - - if ($tag === false) { - - $stream = new PublicNoticeStream($profile); - - $ids = $stream->getNoticeIds(0, 500, null, null); - - if (empty($ids)) { - $tag = array(); - } else { - $weightexpr = common_sql_weight('notice_tag.created', common_config('tag', 'dropoff')); - // @fixme should we use the cutoff too? Doesn't help with indexing per-user. - - $qry = 'SELECT notice_tag.tag, '. - $weightexpr . ' as weight ' . - 'FROM notice_tag JOIN notice ' . - 'ON notice_tag.notice_id = notice.id ' . - 'WHERE notice.id in (' . implode(',', $ids) . ') '. - 'GROUP BY notice_tag.tag ' . - 'ORDER BY weight DESC '; - - $limit = TAGS_PER_SECTION; - $offset = 0; - - if (common_config('db','type') == 'pgsql') { - $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; - } else { - $qry .= ' LIMIT ' . $offset . ', ' . $limit; - } - - $t = new Notice_tag(); - - $t->query($qry); - - $tag = array(); - - while ($t->fetch()) { - $tag[] = clone($t); - } - } - - Memcached_DataObject::cacheSet($keypart, $tag, 60 * 60 * 24); - } - - return new ArrayWrapper($tag); - } - - function showMore() - { - } -} diff --git a/lib/router.php b/lib/router.php index b01c9a7677..cd464d841c 100644 --- a/lib/router.php +++ b/lib/router.php @@ -260,12 +260,6 @@ class Router array('action' => 'userbyid'), array('id' => '[0-9]+')); - if (!common_config('performance', 'high')) { - $m->connect('tags/', array('action' => 'publictagcloud')); - $m->connect('tag/', array('action' => 'publictagcloud')); - $m->connect('tags', array('action' => 'publictagcloud')); - $m->connect('tag', array('action' => 'publictagcloud')); - } $m->connect('tag/:tag/rss', array('action' => 'tagrss'), array('tag' => self::REGEX_TAG)); diff --git a/lib/subpeopletagcloudsection.php b/lib/subpeopletagcloudsection.php deleted file mode 100644 index b23a82240d..0000000000 --- a/lib/subpeopletagcloudsection.php +++ /dev/null @@ -1,77 +0,0 @@ -. - * - * @category Widget - * @package StatusNet - * @author Evan Prodromou - * @copyright 2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET') && !defined('LACONICA')) { - exit(1); -} - -/** - * Personal tag cloud section - * - * @category Widget - * @package StatusNet - * @author Evan Prodromou - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -class SubPeopleTagCloudSection extends TagCloudSection -{ - function getTags() - { - $qry = $this->query(); - $limit = TAGS_PER_SECTION; - $offset = 0; - - if (common_config('db','type') == 'pgsql') { - $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; - } else { - $qry .= ' LIMIT ' . $offset . ', ' . $limit; - } - - $profile_tag = Memcached_DataObject::cachedQuery('Profile_tag', - sprintf($qry, - $this->out->user->id)); - return $profile_tag; - } - - function tagUrl($tag) { - return common_local_url('peopletag', array('tag' => $tag)); - } - - function showTag($tag, $weight, $relative) { - $rel = 'tag-cloud-'; - $rel .= 1+intval(7 * $relative * $weight - 0.01); - - $this->out->elementStart('li', $rel); - $this->out->element('a', array('href' => $this->tagUrl($tag)), $tag); - $this->out->elementEnd('li'); - } -} - diff --git a/lib/subscriberspeopleselftagcloudsection.php b/lib/subscriberspeopleselftagcloudsection.php deleted file mode 100644 index ddbf389868..0000000000 --- a/lib/subscriberspeopleselftagcloudsection.php +++ /dev/null @@ -1,58 +0,0 @@ -. - * - * @category Widget - * @package StatusNet - * @author Evan Prodromou - * @copyright 2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET') && !defined('LACONICA')) { - exit(1); -} - -/** - * Personal tag cloud section - * - * @category Widget - * @package StatusNet - * @author Evan Prodromou - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ -class SubscribersPeopleSelfTagCloudSection extends SubPeopleTagCloudSection -{ - function title() - { - // TRANS: Title of personal tag cloud section. - return _('People Tagcloud as self-tagged'); - } - - function query() { -// return 'select tag, count(tag) as weight from subscription left join profile_tag on tagger = subscriber where subscribed=%d and subscribed != subscriber and tagger = tagged group by tag order by weight desc'; - - return 'select profile_tag.tag, count(profile_tag.tag) as weight from subscription left join (profile_tag, profile_list) on profile_list.tag = profile_tag.tag and profile_list.tagger = profile_tag.tagger and profile_tag.tagger = subscriber where subscribed=%d and subscribed != subscriber and profile_tag.tagger = tagged and profile_list.private = false and profile_tag.tag is not null group by profile_tag.tag order by weight desc'; - -// return 'select tag, count(tag) as weight from subscription left join profile_tag on tagger = subscribed where subscriber=%d and subscribed != subscriber and tagger = tagged and tag is not null group by tag order by weight desc'; - } -} diff --git a/lib/subscriberspeopletagcloudsection.php b/lib/subscriberspeopletagcloudsection.php deleted file mode 100644 index c3602a6b46..0000000000 --- a/lib/subscriberspeopletagcloudsection.php +++ /dev/null @@ -1,60 +0,0 @@ -. - * - * @category Widget - * @package StatusNet - * @author Evan Prodromou - * @copyright 2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET') && !defined('LACONICA')) { - exit(1); -} - -/** - * Personal tag cloud section - * - * @category Widget - * @package StatusNet - * @author Evan Prodromou - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ -class SubscribersPeopleTagCloudSection extends SubPeopleTagCloudSection -{ - function title() - { - // TRANS: Title of personal tag cloud section. - return _('People Tagcloud as tagged'); - } - - function tagUrl($tag) { - $nickname = $this->out->profile->nickname; - return common_local_url('subscribers', array('nickname' => $nickname, 'tag' => $tag)); - } - - function query() { -// return 'select tag, count(tag) as weight from subscription left join profile_tag on subscriber=tagged and subscribed=tagger where subscribed=%d and subscriber != subscribed group by tag order by weight desc'; - return 'select profile_tag.tag, count(profile_tag.tag) as weight from subscription left join (profile_tag, profile_list) on subscriber=profile_tag.tagged and subscribed=profile_tag.tagger and profile_tag.tagger = profile_list.tagger and profile_tag.tag = profile_list.tag where subscribed=%d and subscriber != subscribed and profile_list.private = false and profile_tag.tag is not null group by profile_tag.tag order by weight desc'; - } -} diff --git a/lib/subscriptionspeopleselftagcloudsection.php b/lib/subscriptionspeopleselftagcloudsection.php deleted file mode 100644 index 7334234fc6..0000000000 --- a/lib/subscriptionspeopleselftagcloudsection.php +++ /dev/null @@ -1,58 +0,0 @@ -. - * - * @category Widget - * @package StatusNet - * @author Evan Prodromou - * @copyright 2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET') && !defined('LACONICA')) { - exit(1); -} - -/** - * Personal tag cloud section - * - * @category Widget - * @package StatusNet - * @author Evan Prodromou - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ -class SubscriptionsPeopleSelfTagCloudSection extends SubPeopleTagCloudSection -{ - function title() - { - // TRANS: Title of personal tag cloud section. - return _('People Tagcloud as self-tagged'); - } - - function query() { -// return 'select tag, count(tag) as weight from subscription left join profile_tag on tagger = subscriber where subscribed=%d and subscriber != subscribed and tagger = tagged group by tag order by weight desc'; - - return 'select profile_tag.tag, count(profile_tag.tag) as weight from subscription left join (profile_tag, profile_list) on profile_tag.tagger = subscribed and profile_tag.tag = profile_list.tag and profile_tag.tagger = profile_tag.tagger where subscriber=%d and subscribed != subscriber and profile_tag.tagger = profile_tag.tagged and profile_list.private = false and profile_tag.tag is not null group by profile_tag.tag order by weight desc'; - -// return 'select tag, count(tag) as weight from subscription left join profile_tag on tagger = subscriber where subscribed=%d and subscribed != subscriber and tagger = tagged and tag is not null group by tag order by weight desc'; - } -} diff --git a/lib/subscriptionspeopletagcloudsection.php b/lib/subscriptionspeopletagcloudsection.php deleted file mode 100644 index e1016404c1..0000000000 --- a/lib/subscriptionspeopletagcloudsection.php +++ /dev/null @@ -1,60 +0,0 @@ -. - * - * @category Widget - * @package StatusNet - * @author Evan Prodromou - * @copyright 2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET') && !defined('LACONICA')) { - exit(1); -} - -/** - * Personal tag cloud section - * - * @category Widget - * @package StatusNet - * @author Evan Prodromou - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ -class SubscriptionsPeopleTagCloudSection extends SubPeopleTagCloudSection -{ - function title() - { - // TRANS: Title of personal tag cloud section. - return _('People Tagcloud as tagged'); - } - - function tagUrl($tag) { - $nickname = $this->out->profile->nickname; - return common_local_url('subscriptions', array('nickname' => $nickname, 'tag' => $tag)); - } - - function query() { -// return 'select tag, count(tag) as weight from subscription left join profile_tag on subscriber=tagger and subscribed=tagged where subscriber=%d and subscriber != subscribed group by tag order by weight desc'; - return 'select profile_tag.tag, count(profile_tag.tag) as weight from subscription left join (profile_tag, profile_list) on subscriber=profile_tag.tagger and subscribed=tagged and profile_tag.tag = profile_list.tag and profile_tag.tagger = profile_list.tagger where subscriber=%d and subscriber != subscribed and profile_list.private = false and profile_tag.tag is not null group by profile_tag.tag order by weight desc'; - } -} diff --git a/lib/tagcloudsection.php b/lib/tagcloudsection.php deleted file mode 100644 index 80a9042e0f..0000000000 --- a/lib/tagcloudsection.php +++ /dev/null @@ -1,128 +0,0 @@ -. - * - * @category Widget - * @package StatusNet - * @author Evan Prodromou - * @copyright 2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET') && !defined('LACONICA')) { - exit(1); -} - -define('TAGS_PER_SECTION', 10); - -/** - * Base class for sections - * - * These are the widgets that show interesting data about a person - * group, or site. - * - * @category Widget - * @package StatusNet - * @author Evan Prodromou - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ -class TagCloudSection extends Section -{ - function showContent() - { - $tags = $this->getTags(); - - if (!$tags) { - // TRANS: Content displayed in a tag cloud section if there are no tags. - $this->out->element('p', null, _m('NOTAGS','None')); - return false; - } - - $cnt = 0; - - $tw = array(); - $sum = 0; - - while ($tags->fetch() && ++$cnt <= TAGS_PER_SECTION) { - $tw[$tags->tag] = $tags->weight; - $sum += $tags->weight; - } - - if ($cnt == 0) { - // TRANS: Content displayed in a tag cloud section if there are no tags. - $this->out->element('p', null, _m('NOTAGS','None')); - return false; - } - - ksort($tw); - - $this->out->elementStart('ul', 'tags xoxo tag-cloud'); - foreach ($tw as $tag => $weight) { - $this->showTag($tag, $weight, ($sum == 0) ? 0 : $weight/$sum); - } - $this->out->elementEnd('ul'); - - return ($cnt > TAGS_PER_SECTION); - } - - function getTags() - { - return null; - } - - function showTag($tag, $weight, $relative) - { - if ($relative > 0.1) { - $rel = 'tag-cloud-7'; - } else if ($relative > 0.05) { - $rel = 'tag-cloud-6'; - } else if ($relative > 0.02) { - $rel = 'tag-cloud-5'; - } else if ($relative > 0.01) { - $rel = 'tag-cloud-4'; - } else if ($relative > 0.005) { - $rel = 'tag-cloud-3'; - } else if ($relative > 0.002) { - $rel = 'tag-cloud-2'; - } else { - $rel = 'tag-cloud-1'; - } - - $this->out->elementStart('li', $rel); - $this->out->element('a', array('href' => $this->tagUrl($tag)), - $tag); - $this->out->elementEnd('li'); - } - - function tagUrl($tag) - { - if ($this->out instanceof ShowstreamAction) { - return common_local_url('showstream', array('nickname' => $this->out->getTarget()->getNickname(), 'tag' => $tag)); - } - return common_local_url('tag', array('tag' => $tag)); - } - - function divId() - { - return 'tagcloud'; - } -} diff --git a/plugins/TagCloud/TagCloudPlugin.php b/plugins/TagCloud/TagCloudPlugin.php new file mode 100644 index 0000000000..c616be75fd --- /dev/null +++ b/plugins/TagCloud/TagCloudPlugin.php @@ -0,0 +1,70 @@ + + * @copyright 2016 Free Software Foundation, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://gnu.io/social/ + */ + +if (!defined('GNUSOCIAL')) { exit(1); } + +class TagCloudPlugin extends Plugin { + + public function onRouterInitialized(URLMapper $m) + { + $m->connect('tags/', array('action' => 'publictagcloud')); + $m->connect('tag/', array('action' => 'publictagcloud')); + $m->connect('tags', array('action' => 'publictagcloud')); + $m->connect('tag', array('action' => 'publictagcloud')); + } + + public function onEndPublicGroupNav(Menu $menu) + { + // TRANS: Menu item in search group navigation panel. + $menu->out->menuItem(common_local_url('publictagcloud'), _m('MENU','Recent tags'), + // TRANS: Menu item title in search group navigation panel. + _('Recent tags'), $menu->actionName === 'publictagcloud', 'nav_recent-tags'); + } + + public function onEndShowSections(Action $action) + { + $cloud = null; + + switch (true) { + case $action instanceof AllAction: + $cloud = new InboxTagCloudSection($action, $action->getTarget()); + break; + case $action instanceof AttachmentAction: + $cloud = new AttachmentTagCloudSection($action); + break; + case $action instanceof PublicAction: + $cloud = new PublicTagCloudSection($action); + break; + case $action instanceof ShowstreamAction: + $cloud = new PersonalTagCloudSection($action, $action->getTarget()); + break; + case $action instanceof GroupAction: + $cloud = new GroupTagCloudSection($action, $action->getGroup()); + } + + if (!is_null($cloud)) { + $cloud->show(); + } + } + + public function onPluginVersion(array &$versions) + { + $versions[] = array('name' => 'TagCloud', + 'version' => GNUSOCIAL_VERSION, + 'author' => 'Mikael Nordfeldth', + 'homepage' => 'https://gnu.io/social', + 'description' => + // TRANS: Plugin description. + _m('Adds tag clouds to stream pages')); + return true; + } +} diff --git a/plugins/TagCloud/actions/publictagcloud.php b/plugins/TagCloud/actions/publictagcloud.php new file mode 100644 index 0000000000..e557b75fd0 --- /dev/null +++ b/plugins/TagCloud/actions/publictagcloud.php @@ -0,0 +1,179 @@ +. + * + * @category Public + * @package StatusNet + * @author Mike Cochrane + * @author Evan Prodromou + * @copyright 2008 Mike Cochrane + * @copyright 2008-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') && !defined('LACONICA')) { exit(1); } + +define('TAGS_PER_PAGE', 100); + +/** + * Public tag cloud for notices + * + * @category Personal + * @package StatusNet + * @author Mike Cochrane + * @author Evan Prodromou + * @copyright 2008 Mike Cochrane + * @copyright 2008-2009 StatusNet, Inc. + * @link http://status.net/ + */ +class PublictagcloudAction extends Action +{ + function isReadOnly($args) + { + return true; + } + + function title() + { + // TRANS: Title for public tag cloud. + return _('Public tag cloud'); + } + + function showPageNotice() + { + $this->element('p', 'instructions', + // TRANS: Instructions (more used like an explanation/header). + // TRANS: %s is the StatusNet sitename. + sprintf(_('These are most popular recent tags on %s'), + common_config('site', 'name'))); + } + + function showEmptyList() + { + // TRANS: This message contains a Markdown URL. The link description is between + // TRANS: square brackets, and the link between parentheses. Do not separate "](" + // TRANS: and do not change the URL part. + $message = _('No one has posted a notice with a [hashtag](%%doc.tags%%) yet.') . ' '; + + if (common_logged_in()) { + // TRANS: Message shown to a logged in user for the public tag cloud + // TRANS: while no tags exist yet. "One" refers to the non-existing hashtag. + $message .= _('Be the first to post one!'); + } + else { + // TRANS: Message shown to a anonymous user for the public tag cloud + // TRANS: while no tags exist yet. "One" refers to the non-existing hashtag. + // TRANS: This message contains a Markdown URL. The link description is between + // TRANS: square brackets, and the link between parentheses. Do not separate "](" + // TRANS: and do not change the URL part. + $message .= _('Why not [register an account](%%action.register%%) and be the first to post one!'); + } + + $this->elementStart('div', 'guide'); + $this->raw(common_markup_to_html($message)); + $this->elementEnd('div'); + } + + function handle() + { + parent::handle(); + $this->showPage(); + } + + function showContent() + { + // This should probably be cached rather than recalculated + $tags = new Notice_tag(); + + #Need to clear the selection and then only re-add the field + #we are grouping by, otherwise it's not a valid 'group by' + #even though MySQL seems to let it slide... + $tags->selectAdd(); + $tags->selectAdd('tag'); + + #Add the aggregated columns... + $tags->selectAdd('max(notice_id) as last_notice_id'); + $calc = common_sql_weight('created', common_config('tag', 'dropoff')); + $cutoff = sprintf("notice_tag.created > '%s'", + common_sql_date(time() - common_config('tag', 'cutoff'))); + $tags->selectAdd($calc . ' as weight'); + $tags->whereAdd($cutoff); + $tags->groupBy('tag'); + $tags->orderBy('weight DESC'); + + $tags->limit(TAGS_PER_PAGE); + + $cnt = $tags->find(); + + if ($cnt > 0) { + $this->elementStart('div', array('id' => 'tagcloud', + 'class' => 'section')); + + $tw = array(); + $sum = 0; + while ($tags->fetch()) { + $tw[$tags->tag] = $tags->weight; + $sum += $tags->weight; + } + + ksort($tw); + + $this->elementStart('ul', 'tags xoxo tag-cloud'); + foreach ($tw as $tag => $weight) { + if ($sum) { + $weightedSum = $weight/$sum; + } else { + $weightedSum = 0.5; + } + $this->showTag($tag, $weight, $weightedSum); + } + $this->elementEnd('ul'); + + $this->elementEnd('div'); + } else { + $this->showEmptyList(); + } + } + + function showTag($tag, $weight, $relative) + { + if ($relative > 0.1) { + $rel = 'tag-cloud-7'; + } else if ($relative > 0.05) { + $rel = 'tag-cloud-6'; + } else if ($relative > 0.02) { + $rel = 'tag-cloud-5'; + } else if ($relative > 0.01) { + $rel = 'tag-cloud-4'; + } else if ($relative > 0.005) { + $rel = 'tag-cloud-3'; + } else if ($relative > 0.002) { + $rel = 'tag-cloud-2'; + } else { + $rel = 'tag-cloud-1'; + } + + $this->elementStart('li', $rel); + $this->element('a', array('href' => common_local_url('tag', array('tag' => $tag))), + $tag); + $this->elementEnd('li'); + } +} diff --git a/plugins/TagCloud/lib/attachmenttagcloudsection.php b/plugins/TagCloud/lib/attachmenttagcloudsection.php new file mode 100644 index 0000000000..2c8fc5ee1a --- /dev/null +++ b/plugins/TagCloud/lib/attachmenttagcloudsection.php @@ -0,0 +1,82 @@ +. + * + * @category Widget + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +/** + * Attachment tag cloud section + * + * @category Widget + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ +class AttachmentTagCloudSection extends TagCloudSection +{ + function title() + { + // TRANS: Title. + return _('Tags for this attachment'); + } + + function showTag($tag, $weight, $relative) + { + if ($relative > 0.5) { + $rel = 'tag-cloud-7'; + } else if ($relative > 0.4) { + $rel = 'tag-cloud-6'; + } else if ($relative > 0.3) { + $rel = 'tag-cloud-5'; + } else if ($relative > 0.2) { + $rel = 'tag-cloud-4'; + } else if ($relative > 0.1) { + $rel = 'tag-cloud-3'; + } else if ($relative > 0.05) { + $rel = 'tag-cloud-2'; + } else { + $rel = 'tag-cloud-1'; + } + + $this->out->elementStart('li', $rel); + $this->out->element('a', array('href' => $this->tagUrl($tag)), + $tag); + $this->out->elementEnd('li'); + } + + function getTags() + { + $notice_tag = new Notice_tag; + $query = 'select tag,count(tag) as weight from notice_tag join file_to_post on (notice_tag.notice_id=post_id) join notice on notice_id = notice.id where file_id=' . $notice_tag->escape($this->out->attachment->id) . ' group by tag order by weight desc'; + $notice_tag->query($query); + return $notice_tag; + } +} diff --git a/plugins/TagCloud/lib/grouptagcloudsection.php b/plugins/TagCloud/lib/grouptagcloudsection.php new file mode 100644 index 0000000000..5ed14560ce --- /dev/null +++ b/plugins/TagCloud/lib/grouptagcloudsection.php @@ -0,0 +1,105 @@ +. + * + * @category Widget + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +/** + * Group tag cloud section + * + * @category Widget + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ +class GroupTagCloudSection extends TagCloudSection +{ + var $group = null; + + function __construct($out=null, $group=null) + { + parent::__construct($out); + $this->group = $group; + } + + function title() + { + // TRANS: Title for group tag cloud section. + // TRANS: %s is a group name. + return _('Tags'); + } + + function getTags() + { + $weightexpr = common_sql_weight('notice_tag.created', common_config('tag', 'dropoff')); + // @fixme should we use the cutoff too? Doesn't help with indexing per-group. + + $names = $this->group->getAliases(); + + $names = array_merge(array($this->group->nickname), $names); + + // XXX This is dumb. + + $quoted = array(); + + foreach ($names as $name) { + $quoted[] = "'$name'"; + } + + $namestring = implode(',', $quoted); + + $qry = 'SELECT notice_tag.tag, '. + $weightexpr . ' as weight ' . + 'FROM notice_tag JOIN notice ' . + 'ON notice_tag.notice_id = notice.id ' . + 'JOIN group_inbox on group_inbox.notice_id = notice.id ' . + 'WHERE group_inbox.group_id = %d ' . + 'AND notice_tag.tag not in (%s) '. + 'GROUP BY notice_tag.tag ' . + 'ORDER BY weight DESC '; + + $limit = TAGS_PER_SECTION; + $offset = 0; + + if (common_config('db','type') == 'pgsql') { + $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; + } else { + $qry .= ' LIMIT ' . $offset . ', ' . $limit; + } + + $tag = Memcached_DataObject::cachedQuery('Notice_tag', + sprintf($qry, + $this->group->id, + $namestring), + 3600); + return $tag; + } +} diff --git a/plugins/TagCloud/lib/inboxtagcloudsection.php b/plugins/TagCloud/lib/inboxtagcloudsection.php new file mode 100644 index 0000000000..4268ee4854 --- /dev/null +++ b/plugins/TagCloud/lib/inboxtagcloudsection.php @@ -0,0 +1,107 @@ +. + * + * @category Widget + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('GNUSOCIAL')) { exit(1); } + +/** + * Personal tag cloud section + * + * @category Widget + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ +class InboxTagCloudSection extends TagCloudSection +{ + const MAX_NOTICES = 1024; // legacy value for "Inbox" table size when that existed + + protected $target = null; + + function __construct($out=null, Profile $target) + { + parent::__construct($out); + $this->target = $target; + } + + function title() + { + // TRANS: Title for inbox tag cloud section. + return _m('TITLE', 'Trends'); + } + + function getTags() + { + // FIXME: Get the Profile::current() value some other way + // to avoid confusion between background stuff and session. + $stream = new InboxNoticeStream($this->target, Profile::current()); + + $ids = $stream->getNoticeIds(0, self::MAX_NOTICES, null, null); + + if (empty($ids)) { + $tag = array(); + } else { + $weightexpr = common_sql_weight('notice_tag.created', common_config('tag', 'dropoff')); + // @fixme should we use the cutoff too? Doesn't help with indexing per-user. + + $qry = 'SELECT notice_tag.tag, '. + $weightexpr . ' as weight ' . + 'FROM notice_tag JOIN notice ' . + 'ON notice_tag.notice_id = notice.id ' . + 'WHERE notice.id in (' . implode(',', $ids) . ')'. + 'GROUP BY notice_tag.tag ' . + 'ORDER BY weight DESC '; + + $limit = TAGS_PER_SECTION; + $offset = 0; + + if (common_config('db','type') == 'pgsql') { + $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; + } else { + $qry .= ' LIMIT ' . $offset . ', ' . $limit; + } + + $t = new Notice_tag(); + + $t->query($qry); + + $tag = array(); + + while ($t->fetch()) { + $tag[] = clone($t); + } + } + + return new ArrayWrapper($tag); + } + + function showMore() + { + } +} diff --git a/plugins/TagCloud/lib/personaltagcloudsection.php b/plugins/TagCloud/lib/personaltagcloudsection.php new file mode 100644 index 0000000000..e46aa2d662 --- /dev/null +++ b/plugins/TagCloud/lib/personaltagcloudsection.php @@ -0,0 +1,85 @@ +. + * + * @category Widget + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('GNUSOCIAL')) { exit(1); } + +/** + * Personal tag cloud section + * + * @category Widget + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ +class PersonalTagCloudSection extends TagCloudSection +{ + protected $profile = null; + + function __construct(HTMLOutputter $out, Profile $profile) + { + parent::__construct($out); + $this->profile = $profile; + } + + function title() + { + // TRANS: Title for personal tag cloud section. + return _m('TITLE','Tags'); + } + + function getTags() + { + $weightexpr = common_sql_weight('notice_tag.created', common_config('tag', 'dropoff')); + // @fixme should we use the cutoff too? Doesn't help with indexing per-user. + + $qry = 'SELECT notice_tag.tag, '. + $weightexpr . ' as weight ' . + 'FROM notice_tag JOIN notice ' . + 'ON notice_tag.notice_id = notice.id ' . + 'WHERE notice.profile_id = %d ' . + 'GROUP BY notice_tag.tag ' . + 'ORDER BY weight DESC '; + + $limit = TAGS_PER_SECTION; + $offset = 0; + + if (common_config('db','type') == 'pgsql') { + $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; + } else { + $qry .= ' LIMIT ' . $offset . ', ' . $limit; + } + + $tag = Memcached_DataObject::cachedQuery('Notice_tag', + sprintf($qry, + $this->profile->getID()), + 3600); + return $tag; + } +} diff --git a/plugins/TagCloud/lib/publictagcloudsection.php b/plugins/TagCloud/lib/publictagcloudsection.php new file mode 100644 index 0000000000..bf88091a90 --- /dev/null +++ b/plugins/TagCloud/lib/publictagcloudsection.php @@ -0,0 +1,117 @@ +. + * + * @category Widget + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +/** + * Public tag cloud section + * + * @category Widget + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ +class PublicTagCloudSection extends TagCloudSection +{ + function __construct($out=null) + { + parent::__construct($out); + } + + function title() + { + // TRANS: Title for inbox tag cloud section. + return _m('TITLE', 'Trends'); + } + + function getTags() + { + $profile = Profile::current(); + + if (empty($profile)) { + $keypart = sprintf('Notice:public_tag_cloud:null'); + } else { + $keypart = sprintf('Notice:public_tag_cloud:%d', $profile->id); + } + + $tag = Memcached_DataObject::cacheGet($keypart); + + if ($tag === false) { + + $stream = new PublicNoticeStream($profile); + + $ids = $stream->getNoticeIds(0, 500, null, null); + + if (empty($ids)) { + $tag = array(); + } else { + $weightexpr = common_sql_weight('notice_tag.created', common_config('tag', 'dropoff')); + // @fixme should we use the cutoff too? Doesn't help with indexing per-user. + + $qry = 'SELECT notice_tag.tag, '. + $weightexpr . ' as weight ' . + 'FROM notice_tag JOIN notice ' . + 'ON notice_tag.notice_id = notice.id ' . + 'WHERE notice.id in (' . implode(',', $ids) . ') '. + 'GROUP BY notice_tag.tag ' . + 'ORDER BY weight DESC '; + + $limit = TAGS_PER_SECTION; + $offset = 0; + + if (common_config('db','type') == 'pgsql') { + $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; + } else { + $qry .= ' LIMIT ' . $offset . ', ' . $limit; + } + + $t = new Notice_tag(); + + $t->query($qry); + + $tag = array(); + + while ($t->fetch()) { + $tag[] = clone($t); + } + } + + Memcached_DataObject::cacheSet($keypart, $tag, 60 * 60 * 24); + } + + return new ArrayWrapper($tag); + } + + function showMore() + { + } +} diff --git a/plugins/TagCloud/lib/subpeopletagcloudsection.php b/plugins/TagCloud/lib/subpeopletagcloudsection.php new file mode 100644 index 0000000000..b23a82240d --- /dev/null +++ b/plugins/TagCloud/lib/subpeopletagcloudsection.php @@ -0,0 +1,77 @@ +. + * + * @category Widget + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +/** + * Personal tag cloud section + * + * @category Widget + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class SubPeopleTagCloudSection extends TagCloudSection +{ + function getTags() + { + $qry = $this->query(); + $limit = TAGS_PER_SECTION; + $offset = 0; + + if (common_config('db','type') == 'pgsql') { + $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; + } else { + $qry .= ' LIMIT ' . $offset . ', ' . $limit; + } + + $profile_tag = Memcached_DataObject::cachedQuery('Profile_tag', + sprintf($qry, + $this->out->user->id)); + return $profile_tag; + } + + function tagUrl($tag) { + return common_local_url('peopletag', array('tag' => $tag)); + } + + function showTag($tag, $weight, $relative) { + $rel = 'tag-cloud-'; + $rel .= 1+intval(7 * $relative * $weight - 0.01); + + $this->out->elementStart('li', $rel); + $this->out->element('a', array('href' => $this->tagUrl($tag)), $tag); + $this->out->elementEnd('li'); + } +} + diff --git a/plugins/TagCloud/lib/subscriberspeopleselftagcloudsection.php b/plugins/TagCloud/lib/subscriberspeopleselftagcloudsection.php new file mode 100644 index 0000000000..ddbf389868 --- /dev/null +++ b/plugins/TagCloud/lib/subscriberspeopleselftagcloudsection.php @@ -0,0 +1,58 @@ +. + * + * @category Widget + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +/** + * Personal tag cloud section + * + * @category Widget + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ +class SubscribersPeopleSelfTagCloudSection extends SubPeopleTagCloudSection +{ + function title() + { + // TRANS: Title of personal tag cloud section. + return _('People Tagcloud as self-tagged'); + } + + function query() { +// return 'select tag, count(tag) as weight from subscription left join profile_tag on tagger = subscriber where subscribed=%d and subscribed != subscriber and tagger = tagged group by tag order by weight desc'; + + return 'select profile_tag.tag, count(profile_tag.tag) as weight from subscription left join (profile_tag, profile_list) on profile_list.tag = profile_tag.tag and profile_list.tagger = profile_tag.tagger and profile_tag.tagger = subscriber where subscribed=%d and subscribed != subscriber and profile_tag.tagger = tagged and profile_list.private = false and profile_tag.tag is not null group by profile_tag.tag order by weight desc'; + +// return 'select tag, count(tag) as weight from subscription left join profile_tag on tagger = subscribed where subscriber=%d and subscribed != subscriber and tagger = tagged and tag is not null group by tag order by weight desc'; + } +} diff --git a/plugins/TagCloud/lib/subscriberspeopletagcloudsection.php b/plugins/TagCloud/lib/subscriberspeopletagcloudsection.php new file mode 100644 index 0000000000..c3602a6b46 --- /dev/null +++ b/plugins/TagCloud/lib/subscriberspeopletagcloudsection.php @@ -0,0 +1,60 @@ +. + * + * @category Widget + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +/** + * Personal tag cloud section + * + * @category Widget + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ +class SubscribersPeopleTagCloudSection extends SubPeopleTagCloudSection +{ + function title() + { + // TRANS: Title of personal tag cloud section. + return _('People Tagcloud as tagged'); + } + + function tagUrl($tag) { + $nickname = $this->out->profile->nickname; + return common_local_url('subscribers', array('nickname' => $nickname, 'tag' => $tag)); + } + + function query() { +// return 'select tag, count(tag) as weight from subscription left join profile_tag on subscriber=tagged and subscribed=tagger where subscribed=%d and subscriber != subscribed group by tag order by weight desc'; + return 'select profile_tag.tag, count(profile_tag.tag) as weight from subscription left join (profile_tag, profile_list) on subscriber=profile_tag.tagged and subscribed=profile_tag.tagger and profile_tag.tagger = profile_list.tagger and profile_tag.tag = profile_list.tag where subscribed=%d and subscriber != subscribed and profile_list.private = false and profile_tag.tag is not null group by profile_tag.tag order by weight desc'; + } +} diff --git a/plugins/TagCloud/lib/subscriptionspeopleselftagcloudsection.php b/plugins/TagCloud/lib/subscriptionspeopleselftagcloudsection.php new file mode 100644 index 0000000000..7334234fc6 --- /dev/null +++ b/plugins/TagCloud/lib/subscriptionspeopleselftagcloudsection.php @@ -0,0 +1,58 @@ +. + * + * @category Widget + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +/** + * Personal tag cloud section + * + * @category Widget + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ +class SubscriptionsPeopleSelfTagCloudSection extends SubPeopleTagCloudSection +{ + function title() + { + // TRANS: Title of personal tag cloud section. + return _('People Tagcloud as self-tagged'); + } + + function query() { +// return 'select tag, count(tag) as weight from subscription left join profile_tag on tagger = subscriber where subscribed=%d and subscriber != subscribed and tagger = tagged group by tag order by weight desc'; + + return 'select profile_tag.tag, count(profile_tag.tag) as weight from subscription left join (profile_tag, profile_list) on profile_tag.tagger = subscribed and profile_tag.tag = profile_list.tag and profile_tag.tagger = profile_tag.tagger where subscriber=%d and subscribed != subscriber and profile_tag.tagger = profile_tag.tagged and profile_list.private = false and profile_tag.tag is not null group by profile_tag.tag order by weight desc'; + +// return 'select tag, count(tag) as weight from subscription left join profile_tag on tagger = subscriber where subscribed=%d and subscribed != subscriber and tagger = tagged and tag is not null group by tag order by weight desc'; + } +} diff --git a/plugins/TagCloud/lib/subscriptionspeopletagcloudsection.php b/plugins/TagCloud/lib/subscriptionspeopletagcloudsection.php new file mode 100644 index 0000000000..e1016404c1 --- /dev/null +++ b/plugins/TagCloud/lib/subscriptionspeopletagcloudsection.php @@ -0,0 +1,60 @@ +. + * + * @category Widget + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +/** + * Personal tag cloud section + * + * @category Widget + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ +class SubscriptionsPeopleTagCloudSection extends SubPeopleTagCloudSection +{ + function title() + { + // TRANS: Title of personal tag cloud section. + return _('People Tagcloud as tagged'); + } + + function tagUrl($tag) { + $nickname = $this->out->profile->nickname; + return common_local_url('subscriptions', array('nickname' => $nickname, 'tag' => $tag)); + } + + function query() { +// return 'select tag, count(tag) as weight from subscription left join profile_tag on subscriber=tagger and subscribed=tagged where subscriber=%d and subscriber != subscribed group by tag order by weight desc'; + return 'select profile_tag.tag, count(profile_tag.tag) as weight from subscription left join (profile_tag, profile_list) on subscriber=profile_tag.tagger and subscribed=tagged and profile_tag.tag = profile_list.tag and profile_tag.tagger = profile_list.tagger where subscriber=%d and subscriber != subscribed and profile_list.private = false and profile_tag.tag is not null group by profile_tag.tag order by weight desc'; + } +} diff --git a/plugins/TagCloud/lib/tagcloudsection.php b/plugins/TagCloud/lib/tagcloudsection.php new file mode 100644 index 0000000000..80a9042e0f --- /dev/null +++ b/plugins/TagCloud/lib/tagcloudsection.php @@ -0,0 +1,128 @@ +. + * + * @category Widget + * @package StatusNet + * @author Evan Prodromou + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +define('TAGS_PER_SECTION', 10); + +/** + * Base class for sections + * + * These are the widgets that show interesting data about a person + * group, or site. + * + * @category Widget + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ +class TagCloudSection extends Section +{ + function showContent() + { + $tags = $this->getTags(); + + if (!$tags) { + // TRANS: Content displayed in a tag cloud section if there are no tags. + $this->out->element('p', null, _m('NOTAGS','None')); + return false; + } + + $cnt = 0; + + $tw = array(); + $sum = 0; + + while ($tags->fetch() && ++$cnt <= TAGS_PER_SECTION) { + $tw[$tags->tag] = $tags->weight; + $sum += $tags->weight; + } + + if ($cnt == 0) { + // TRANS: Content displayed in a tag cloud section if there are no tags. + $this->out->element('p', null, _m('NOTAGS','None')); + return false; + } + + ksort($tw); + + $this->out->elementStart('ul', 'tags xoxo tag-cloud'); + foreach ($tw as $tag => $weight) { + $this->showTag($tag, $weight, ($sum == 0) ? 0 : $weight/$sum); + } + $this->out->elementEnd('ul'); + + return ($cnt > TAGS_PER_SECTION); + } + + function getTags() + { + return null; + } + + function showTag($tag, $weight, $relative) + { + if ($relative > 0.1) { + $rel = 'tag-cloud-7'; + } else if ($relative > 0.05) { + $rel = 'tag-cloud-6'; + } else if ($relative > 0.02) { + $rel = 'tag-cloud-5'; + } else if ($relative > 0.01) { + $rel = 'tag-cloud-4'; + } else if ($relative > 0.005) { + $rel = 'tag-cloud-3'; + } else if ($relative > 0.002) { + $rel = 'tag-cloud-2'; + } else { + $rel = 'tag-cloud-1'; + } + + $this->out->elementStart('li', $rel); + $this->out->element('a', array('href' => $this->tagUrl($tag)), + $tag); + $this->out->elementEnd('li'); + } + + function tagUrl($tag) + { + if ($this->out instanceof ShowstreamAction) { + return common_local_url('showstream', array('nickname' => $this->out->getTarget()->getNickname(), 'tag' => $tag)); + } + return common_local_url('tag', array('tag' => $tag)); + } + + function divId() + { + return 'tagcloud'; + } +}