Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15395210
D10035.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
19 KB
Referenced Files
None
Subscribers
None
D10035.id.diff
View Options
diff --git a/resources/sql/autopatches/20150420.imagemacro.1.sql b/resources/sql/autopatches/20150420.imagemacro.1.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20150420.imagemacro.1.sql
@@ -0,0 +1,8 @@
+CREATE TABLE {$NAMESPACE}_file.file_imagemacrousage (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ macroPHID VARBINARY(64) NOT NULL,
+ authorPHID VARBINARY(64) NOT NULL,
+ count INT UNSIGNED NOT NULL,
+ dateCreated INT UNSIGNED NOT NULL,
+ dateModified INT UNSIGNED NOT NULL
+) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
diff --git a/resources/sql/autopatches/20150420.imagemacro.2.backfill.php b/resources/sql/autopatches/20150420.imagemacro.2.backfill.php
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20150420.imagemacro.2.backfill.php
@@ -0,0 +1,38 @@
+<?php
+
+$engine = PhabricatorMarkupEngine::getEngine('extract');
+$engine->setConfig('viewer', PhabricatorUser::getOmnipotentUser());
+PhabricatorEnv::setRequestBaseURI('/'); // Doesn't matter during backfill
+
+$macro_usage_table = new PhabricatorFileImageMacroUsage();
+$macro_usage_key = PhabricatorImageMacroRemarkupRule::KEY_MACRO_USAGE;
+
+$xaction_classes = id(new PhutilSymbolLoader())
+ ->setAncestorClass('PhabricatorApplicationTransaction')
+ ->loadObjects();
+
+foreach ($xaction_classes as $xaction_class) {
+ /*if (get_class($xaction_class) != "DifferentialTransaction") {
+ continue;
+ }*/
+ $objs = $xaction_class->loadAll();
+ // foreach (new LiskMigrationIterator($xaction_class) as $xaction) {
+ foreach ($objs as $obj) {
+ // $obj = $xaction_class->load($xaction->getID());
+ try {
+ $blocks = $obj->getRemarkupBlocks();
+ } catch (Exception $e) {
+ continue;
+ }
+ // $authorPHID = $xaction->getAuthorPHID();
+ $authorPHID = '';
+
+ foreach ($blocks as $block) {
+ $engine->markupText($block);
+ $macro_usage = $engine->getTextMetadata($macro_usage_key, array());
+ //$macro_usage_table->updateUsage($authorPHID, $macro_usage);
+ print($authorPHID . "\n");
+ print_r($macro_usage);
+ }
+ }
+}
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1832,6 +1832,7 @@
'PhabricatorFileFilePHIDType' => 'applications/files/phid/PhabricatorFileFilePHIDType.php',
'PhabricatorFileHasObjectEdgeType' => 'applications/files/edge/PhabricatorFileHasObjectEdgeType.php',
'PhabricatorFileImageMacro' => 'applications/macro/storage/PhabricatorFileImageMacro.php',
+ 'PhabricatorFileImageMacroUsage' => 'applications/macro/storage/PhabricatorFileImageMacroUsage.php',
'PhabricatorFileInfoController' => 'applications/files/controller/PhabricatorFileInfoController.php',
'PhabricatorFileLinkListView' => 'view/layout/PhabricatorFileLinkListView.php',
'PhabricatorFileLinkView' => 'view/layout/PhabricatorFileLinkView.php',
@@ -5194,6 +5195,7 @@
'PhabricatorFlaggableInterface',
'PhabricatorPolicyInterface',
),
+ 'PhabricatorFileImageMacroUsage' => 'PhabricatorFileDAO',
'PhabricatorFileInfoController' => 'PhabricatorFileController',
'PhabricatorFileLinkListView' => 'AphrontView',
'PhabricatorFileLinkView' => 'AphrontView',
diff --git a/src/applications/macro/markup/PhabricatorImageMacroRemarkupRule.php b/src/applications/macro/markup/PhabricatorImageMacroRemarkupRule.php
--- a/src/applications/macro/markup/PhabricatorImageMacroRemarkupRule.php
+++ b/src/applications/macro/markup/PhabricatorImageMacroRemarkupRule.php
@@ -5,6 +5,7 @@
private $macros;
const KEY_RULE_MACRO = 'rule.macro';
+ const KEY_MACRO_USAGE = 'rule.macro_usage';
public function apply($text) {
return preg_replace_callback(
@@ -151,6 +152,17 @@
$engine->overwriteStoredText($spec['token'], $result);
}
+ $macro_usage_key = self::KEY_MACRO_USAGE;
+ $macro_usage = $engine->getTextMetadata($macro_usage_key, array());
+ foreach ($metadata as $spec) {
+ if (isset($macro_usage[$spec['phid']])) {
+ $macro_usage[$spec['phid']]++;
+ } else {
+ $macro_usage[$spec['phid']] = 1;
+ }
+ }
+ $engine->setTextMetadata($macro_usage_key, $macro_usage);
+
$engine->setTextMetadata($metadata_key, array());
}
diff --git a/src/applications/macro/markup/PhabricatorMemeRemarkupRule.php b/src/applications/macro/markup/PhabricatorMemeRemarkupRule.php
--- a/src/applications/macro/markup/PhabricatorMemeRemarkupRule.php
+++ b/src/applications/macro/markup/PhabricatorMemeRemarkupRule.php
@@ -59,7 +59,26 @@
));
}
- return $this->getEngine()->storeText($img);
+ $engine = $this->getEngine();
+
+ $macro_usage_key = PhabricatorImageMacroRemarkupRule::KEY_MACRO_USAGE;
+ $macro_usage = $engine->getTextMetadata($macro_usage_key, array());
+ $viewer = $engine->getConfig('viewer');
+ $macro = id(new PhabricatorMacroQuery())
+ ->setViewer($viewer)
+ ->withNames(array($options['src']))
+ ->needFiles(false)
+ ->executeOne();
+ if ($macro) {
+ if (isset($macro_usage[$macro->getPHID()])) {
+ $macro_usage[$macro->getPHID()]++;
+ } else {
+ $macro_usage[$macro->getPHID()] = 1;
+ }
+ $engine->setTextMetadata($macro_usage_key, $macro_usage);
+ }
+
+ return $engine->storeText($img);
}
}
diff --git a/src/applications/macro/query/PhabricatorMacroQuery.php b/src/applications/macro/query/PhabricatorMacroQuery.php
--- a/src/applications/macro/query/PhabricatorMacroQuery.php
+++ b/src/applications/macro/query/PhabricatorMacroQuery.php
@@ -3,9 +3,11 @@
final class PhabricatorMacroQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
+ private $byPopularity;
private $ids;
private $phids;
private $authors;
+ private $abusers;
private $names;
private $nameLike;
private $namePrefix;
@@ -41,6 +43,11 @@
return $options;
}
+ public function withPopularitySort($use_popularity_sort) {
+ $this->byPopularity = $use_popularity_sort;
+ return $this;
+ }
+
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
@@ -56,6 +63,11 @@
return $this;
}
+ public function withAbuserPHIDs(array $abusers) {
+ $this->abusers = $abusers;
+ return $this;
+ }
+
public function withNameLike($name) {
$this->nameLike = $name;
return $this;
@@ -100,15 +112,77 @@
$macro_table = new PhabricatorFileImageMacro();
$conn = $macro_table->establishConnection('r');
- $rows = queryfx_all(
+ if ($this->byPopularity) {
+ $rows = queryfx_all(
+ $conn,
+ 'SELECT m.*, SUM(n.count) as popularity FROM %T m %Q %Q %Q %Q %Q %Q',
+ $macro_table->getTableName(),
+ $this->buildJoinClause($conn),
+ $this->buildWhereClause($conn),
+ $this->buildGroupClause($conn),
+ $this->buildHavingClause($conn),
+ $this->buildOrderClause($conn),
+ $this->buildLimitClause($conn));
+ } else {
+ $rows = queryfx_all(
+ $conn,
+ 'SELECT m.* FROM %T m %Q %Q %Q',
+ $macro_table->getTableName(),
+ $this->buildWhereClause($conn),
+ $this->buildOrderClause($conn),
+ $this->buildLimitClause($conn));
+ }
+
+ $macros = $macro_table->loadAllFromArray($rows);
+ if ($this->byPopularity) {
+ $macros_by_id = mpull($macros, null, 'getId');
+
+ $macro_usage_table = new PhabricatorFileImageMacroUsage();
+ $author_popularity_rows = queryfx_all(
+ $conn,
+ 'SELECT macroPHID, authorPHID, SUM(count) as authorUses FROM %T
+ GROUP BY macroPHID, authorPHID
+ ORDER BY macroPHID, authorUses',
+ $macro_usage_table->getTableName());
+ $by_macro = ipull($author_popularity_rows, null, 'macroPHID');
+
+ foreach ($rows as $row) {
+ $macro_id = $row['id'];
+ $macro_phid = $row['phid'];
+ $author_phid = isset($by_macro[$macro_phid]) ?
+ $by_macro[$macro_phid]['authorPHID'] : null;
+ $author_uses = isset($by_macro[$macro_phid]) ?
+ $by_macro[$macro_phid]['authorUses'] : null;
+ $macros_by_id[$macro_id]->setPopularity(
+ $row['popularity'] != null ? $row['popularity'] : 0,
+ $author_phid,
+ $author_uses);
+ }
+ }
+ return $macros;
+ }
+
+
+ protected function buildHavingClause(AphrontDatabaseConnection $conn) {
+ $where = $this->buildPagingClause($conn);
+ if ($where != null) {
+ return 'HAVING '.$where;
+ }
+ return '';
+ }
+
+ protected function buildJoinClause(AphrontDatabaseConnection $conn) {
+ assert($this->byPopularity);
+ $macro_usage_table = new PhabricatorFileImageMacroUsage();
+
+ $joins = array();
+ $joins[] = qsprintf(
$conn,
- 'SELECT m.* FROM %T m %Q %Q %Q',
- $macro_table->getTableName(),
- $this->buildWhereClause($conn),
- $this->buildOrderClause($conn),
- $this->buildLimitClause($conn));
+ 'LEFT JOIN %T n ON m.phid = n.macroPHID',
+ $macro_usage_table->getTableName());
+
+ return implode(' ', $joins);
- return $macro_table->loadAllFromArray($rows);
}
protected function buildWhereClause(AphrontDatabaseConnection $conn) {
@@ -135,6 +209,13 @@
$this->authors);
}
+ if ($this->abusers && $this->byPopularity) {
+ $where[] = qsprintf(
+ $conn,
+ 'n.authorPHID IN (%Ls)',
+ $this->abusers);
+ }
+
if ($this->nameLike) {
$where[] = qsprintf(
$conn,
@@ -210,11 +291,22 @@
}
}
- $where[] = $this->buildPagingClause($conn);
+ if (!$this->byPopularity) {
+ $where[] = $this->buildPagingClause($conn);
+ }
return $this->formatWhereClause($where);
}
+ protected function buildGroupClause(AphrontDatabaseConnection $conn) {
+ $group = array();
+ $group[] = qsprintf(
+ $conn,
+ 'GROUP BY m.phid');
+
+ return implode(' ', $group);
+ }
+
protected function didFilterPage(array $macros) {
if ($this->needFiles) {
$file_phids = mpull($macros, 'getFilePHID');
@@ -242,6 +334,53 @@
return 'm';
}
+ protected function getPagingColumn() {
+ $popularity_order = 'popularity '.
+ ($this->getBeforeID() ? 'ASC' : 'DESC').', m.id';
+ return $this->byPopularity ? $popularity_order : 'm.id';
+ }
+
+ protected function getPagingValue($macro) {
+ if ($this->byPopularity) {
+ $popularity = $macro->getPopularity();
+ return (string)$popularity['popularity'].','.(string)$macro->getId();
+ }
+ return PhabricatorCursorPagedPolicyAwareQuery::getPagingValue($macro);
+ }
+
+ protected function buildPagingClause(
+ AphrontDatabaseConnection $conn_r) {
+
+ if ($this->byPopularity) {
+ if ($this->getBeforeID()) {
+ $strs = explode(',', $this->getBeforeID());
+ $beforePopularity = (int)$strs[0];
+ $beforeId = (int)$strs[1];
+
+ return qsprintf(
+ $conn_r,
+ 'popularity > %d OR (popularity = %d AND m.id > %d)',
+ $beforePopularity,
+ $beforePopularity,
+ $beforeId);
+ } else if ($this->getAfterID()) {
+ $strs = explode(',', $this->getAfterID());
+ $afterPopularity = (int)$strs[0];
+ $afterId = (int)$strs[1];
+
+ return qsprintf(
+ $conn_r,
+ 'popularity < %d OR (popularity = %d AND m.id < %d)',
+ $afterPopularity,
+ $afterPopularity,
+ $afterId);
+ }
+
+ return null;
+ }
+ return PhabricatorCursorPagedPolicyAwareQuery::buildPagingClause($conn_r);
+ }
+
public function getQueryApplicationClass() {
return 'PhabricatorMacroApplication';
}
diff --git a/src/applications/macro/query/PhabricatorMacroSearchEngine.php b/src/applications/macro/query/PhabricatorMacroSearchEngine.php
--- a/src/applications/macro/query/PhabricatorMacroSearchEngine.php
+++ b/src/applications/macro/query/PhabricatorMacroSearchEngine.php
@@ -17,6 +17,12 @@
'authorPHIDs',
$this->readUsersFromRequest($request, 'authors'));
+ $abuser_phids = $this->readUsersFromRequest($request, 'abusers');
+ $saved->setParameter(
+ 'abuserPHIDs',
+ $abuser_phids);
+ $saved->setParameter('popularitySort', !empty($abuser_phids));
+
$saved->setParameter('status', $request->getStr('status'));
$saved->setParameter('names', $request->getStrList('names'));
$saved->setParameter('nameLike', $request->getStr('nameLike'));
@@ -34,7 +40,9 @@
->needFiles(true)
->withIDs($saved->getParameter('ids', array()))
->withPHIDs($saved->getParameter('phids', array()))
- ->withAuthorPHIDs($saved->getParameter('authorPHIDs', array()));
+ ->withAuthorPHIDs($saved->getParameter('authorPHIDs', array()))
+ ->withAbuserPHIDs($saved->getParameter('abuserPHIDs', array()))
+ ->withPopularitySort($saved->getParameter('popularitySort', false));
$this->setQueryOrder($query, $saved);
@@ -79,6 +87,7 @@
PhabricatorSavedQuery $saved) {
$author_phids = $saved->getParameter('authorPHIDs', array());
+ $abuser_phids = $saved->getParameter('abuserPHIDs', array());
$status = $saved->getParameter('status');
$names = implode(', ', $saved->getParameter('names', array()));
$like = $saved->getParameter('nameLike');
@@ -97,6 +106,12 @@
->setName('authors')
->setLabel(pht('Authors'))
->setValue($author_phids))
+ ->appendControl(
+ id(new AphrontFormTokenizerControl())
+ ->setDatasource(new PhabricatorPeopleDatasource())
+ ->setName('abusers')
+ ->setLabel(pht('Abused By'))
+ ->setValue($abuser_phids))
->appendChild(
id(new AphrontFormTextControl())
->setName('nameLike')
@@ -135,11 +150,13 @@
protected function getBuiltinQueryNames() {
$names = array(
'active' => pht('Active'),
- 'all' => pht('All'),
+ 'recent' => pht('Recent'),
+ 'popular' => pht('Popular'),
);
if ($this->requireViewer()->isLoggedIn()) {
$names['authored'] = pht('Authored');
+ $names['favorites'] = pht('Favorites');
}
return $names;
@@ -152,7 +169,7 @@
switch ($query_key) {
case 'active':
return $query;
- case 'all':
+ case 'recent':
return $query->setParameter(
'status',
PhabricatorMacroQuery::STATUS_ANY);
@@ -160,6 +177,25 @@
return $query->setParameter(
'authorPHIDs',
array($this->requireViewer()->getPHID()));
+ case 'popular':
+ return $query
+ ->setParameter(
+ 'status',
+ PhabricatorMacroQuery::STATUS_ANY)
+ ->setParameter(
+ 'popularitySort',
+ true);
+ case 'favorites':
+ return $query
+ ->setParameter(
+ 'status',
+ PhabricatorMacroQuery::STATUS_ANY)
+ ->setParameter(
+ 'popularitySort',
+ true)
+ ->setParameter(
+ 'abuserPHIDs',
+ array($this->requireViewer()->getPHID()));
}
return parent::buildSavedQueryFromBuiltin($query_key);
@@ -168,7 +204,9 @@
protected function getRequiredHandlePHIDsForResultList(
array $macros,
PhabricatorSavedQuery $query) {
- return mpull($macros, 'getAuthorPHID');
+ return array_merge(
+ mpull($macros, 'getAuthorPHID'),
+ ipull(mpull($macros, 'getPopularity'), 'top_abuser'));
}
protected function renderResultList(
@@ -212,6 +250,30 @@
pht('Created by %s', $author_handle->renderLink()));
}
+ $popularity = $macro->getPopularity();
+ if ($popularity != null) {
+ $popularizer_phid = $popularity['top_abuser'];
+
+ $item->appendChild(
+ phutil_tag(
+ 'div',
+ array(),
+ pht('Popularity %d', $popularity['popularity'])));
+
+ $message = $popularity['popularity'] == 0 ?
+ pht('Wow. This macro must suck.') :
+ pht('Abused most by %s (%d time%s)',
+ $handles[$popularizer_phid]->renderLink(),
+ $popularity['top_abuser_count'],
+ $popularity['top_abuser_count'] != 1 ? 's' : '');
+ $item->appendChild(
+ phutil_tag(
+ 'div',
+ array(),
+ $message));
+ }
+
+
$item->setURI($this->getApplicationURI('/view/'.$macro->getID().'/'));
$item->setDisabled($macro->getisDisabled());
$item->setHeader($macro->getName());
diff --git a/src/applications/macro/storage/PhabricatorFileImageMacro.php b/src/applications/macro/storage/PhabricatorFileImageMacro.php
--- a/src/applications/macro/storage/PhabricatorFileImageMacro.php
+++ b/src/applications/macro/storage/PhabricatorFileImageMacro.php
@@ -17,6 +17,9 @@
private $file = self::ATTACHABLE;
private $audio = self::ATTACHABLE;
+ private $popularity = null;
+ private $topAbuser = null;
+ private $topAbuserCount = null;
const AUDIO_BEHAVIOR_NONE = 'audio:none';
const AUDIO_BEHAVIOR_ONCE = 'audio:once';
@@ -79,6 +82,23 @@
return parent::save();
}
+ public function setPopularity($popularity, $top_abuser, $top_abuser_count) {
+ $this->popularity = $popularity;
+ $this->topAbuser = $top_abuser;
+ $this->topAbuserCount = $top_abuser_count;
+ }
+
+ public function getPopularity() {
+ if (is_null($this->popularity)) {
+ return null;
+ } else {
+ return array(
+ 'popularity' => $this->popularity,
+ 'top_abuser' => $this->topAbuser,
+ 'top_abuser_count' => $this->topAbuserCount);
+ }
+ }
+
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
diff --git a/src/applications/macro/storage/PhabricatorFileImageMacroUsage.php b/src/applications/macro/storage/PhabricatorFileImageMacroUsage.php
new file mode 100644
--- /dev/null
+++ b/src/applications/macro/storage/PhabricatorFileImageMacroUsage.php
@@ -0,0 +1,51 @@
+<?php
+final class PhabricatorFileImageMacroUsage extends PhabricatorFileDAO {
+
+ /********************************/
+ /* Update our macro usage table */
+ /********************************/
+
+ private $macroPHID;
+ private $authorPHID;
+ private $count;
+
+ protected function getConfiguration() {
+ return array(
+ self::CONFIG_COLUMN_SCHEMA => array(
+ 'macroPHID' => 'phid?',
+ 'authorPHID' => 'phid?',
+ 'count' => 'uint32',
+ ),
+ self::CONFIG_KEY_SCHEMA => array(
+ ),
+ ) + parent::getConfiguration();
+ }
+
+ public function updateUsage($author_phid, $macros) {
+ if (empty($macros)) {
+ return;
+ }
+
+ $conn = $this->establishConnection('w');
+
+ // Append to our wonderful macrousage table
+ $values = array();
+ foreach ($macros as $macro_phid => $count) {
+ $values[] = qsprintf(
+ $conn,
+ '(%s, %s, %d)',
+ $macro_phid,
+ $author_phid,
+ $count);
+ }
+ $values_clause = implode(',', $values);
+
+ queryfx(
+ $conn,
+ 'INSERT INTO %T
+ (macroPHID, authorPHID, count)
+ VALUES %Q',
+ $this->getTableName(),
+ $values_clause);
+ }
+}
diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
--- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
+++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
@@ -1265,6 +1265,18 @@
$blocks,
$engine);
+ $macro_usage_table = new PhabricatorFileImageMacroUsage();
+ $macro_usage_key = PhabricatorImageMacroRemarkupRule::KEY_MACRO_USAGE;
+ $authorPHID = $this->getActor()->getPHID();
+
+ foreach ($blocks as $key => $xaction_blocks) {
+ foreach ($xaction_blocks as $block) {
+ $engine->markupText($block);
+ $macro_usage = $engine->getTextMetadata($macro_usage_key, array());
+ $macro_usage_table->updateUsage($authorPHID, $macro_usage);
+ }
+ }
+
$mentioned_phids = array();
if ($this->shouldEnableMentions($object, $xactions)) {
foreach ($blocks as $key => $xaction_blocks) {
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Mar 17, 3:46 AM (1 w, 2 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7346549
Default Alt Text
D10035.id.diff (19 KB)
Attached To
Mode
D10035: Added popularity-based ranking to macros app (WIP)
Attached
Detach File
Event Timeline
Log In to Comment