Page MenuHomePhabricator

D13626.id32923.diff
No OneTemporary

D13626.id32923.diff

diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -7,7 +7,7 @@
*/
return array(
'names' => array(
- 'core.pkg.css' => '1716a671',
+ 'core.pkg.css' => '7aa19a1f',
'core.pkg.js' => 'a590b451',
'darkconsole.pkg.js' => 'e7393ebb',
'differential.pkg.css' => '9451634c',
@@ -145,7 +145,7 @@
'rsrc/css/phui/phui-object-item-list-view.css' => 'fc19bfc1',
'rsrc/css/phui/phui-pager.css' => 'bea33d23',
'rsrc/css/phui/phui-pinboard-view.css' => '2495140e',
- 'rsrc/css/phui/phui-property-list-view.css' => '1baf23eb',
+ 'rsrc/css/phui/phui-property-list-view.css' => 'aeb09581',
'rsrc/css/phui/phui-remarkup-preview.css' => '867f85b3',
'rsrc/css/phui/phui-spacing.css' => '042804d6',
'rsrc/css/phui/phui-status.css' => '888cedb8',
@@ -800,7 +800,7 @@
'phui-object-item-list-view-css' => 'fc19bfc1',
'phui-pager-css' => 'bea33d23',
'phui-pinboard-view-css' => '2495140e',
- 'phui-property-list-view-css' => '1baf23eb',
+ 'phui-property-list-view-css' => 'aeb09581',
'phui-remarkup-preview-css' => '867f85b3',
'phui-spacing-css' => '042804d6',
'phui-status-list-view-css' => '888cedb8',
diff --git a/resources/sql/autopatches/20150712.badges.1.sql b/resources/sql/autopatches/20150712.badges.1.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20150712.badges.1.sql
@@ -0,0 +1,52 @@
+CREATE TABLE {$NAMESPACE}_badges.badges_badge (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ phid VARBINARY(64) NOT NULL,
+ name VARCHAR(255) NOT NULL COLLATE {$COLLATE_TEXT},
+ flavor VARCHAR(255) NOT NULL COLLATE {$COLLATE_TEXT},
+ description LONGTEXT NOT NULL,
+ icon VARCHAR(255) NOT NULL,
+ quality VARCHAR(255) NOT NULL,
+ status VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT},
+ dateCreated INT UNSIGNED NOT NULL,
+ dateModified INT UNSIGNED NOT NULL,
+ viewPolicy VARBINARY(64) NOT NULL,
+ editPolicy VARBINARY(64) NOT NULL,
+ UNIQUE KEY `key_phid` (phid)
+) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
+
+CREATE TABLE {$NAMESPACE}_badges.badges_transaction (
+ id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ phid VARBINARY(64) NOT NULL,
+ authorPHID VARBINARY(64) NOT NULL,
+ objectPHID VARBINARY(64) NOT NULL,
+ viewPolicy VARBINARY(64) NOT NULL,
+ editPolicy VARBINARY(64) NOT NULL,
+ commentPHID VARBINARY(64),
+ commentVersion INT UNSIGNED NOT NULL,
+ transactionType VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT},
+ oldValue LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
+ newValue LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
+ contentSource LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
+ metadata LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
+ dateCreated INT UNSIGNED NOT NULL,
+ dateModified INT UNSIGNED NOT NULL,
+ UNIQUE KEY `key_phid` (phid),
+ KEY `key_object` (objectPHID)
+) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
+
+CREATE TABLE {$NAMESPACE}_badges.edge (
+ src VARBINARY(64) NOT NULL,
+ type INT UNSIGNED NOT NULL,
+ dst VARBINARY(64) NOT NULL,
+ dateCreated INT UNSIGNED NOT NULL,
+ seq INT UNSIGNED NOT NULL,
+ dataID INT UNSIGNED,
+ PRIMARY KEY (src, type, dst),
+ KEY `src` (src, type, dateCreated, seq),
+ UNIQUE KEY `key_dst` (dst, type, src)
+) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
+
+CREATE TABLE {$NAMESPACE}_badges.edgedata (
+ id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ data LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}
+) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
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
@@ -176,6 +176,11 @@
'AuditConduitAPIMethod' => 'applications/audit/conduit/AuditConduitAPIMethod.php',
'AuditQueryConduitAPIMethod' => 'applications/audit/conduit/AuditQueryConduitAPIMethod.php',
'AuthManageProvidersCapability' => 'applications/auth/capability/AuthManageProvidersCapability.php',
+ 'BadgesController' => 'applications/badges/controller/BadgesController.php',
+ 'BadgesEditController' => 'applications/badges/controller/BadgesEditController.php',
+ 'BadgesEditIconController' => 'applications/badges/controller/BadgesEditIconController.php',
+ 'BadgesListController' => 'applications/badges/controller/BadgesListController.php',
+ 'BadgesViewController' => 'applications/badges/controller/BadgesViewController.php',
'CalendarTimeUtil' => 'applications/calendar/util/CalendarTimeUtil.php',
'CalendarTimeUtilTestCase' => 'applications/calendar/__tests__/CalendarTimeUtilTestCase.php',
'CelerityAPI' => 'applications/celerity/CelerityAPI.php',
@@ -1600,6 +1605,21 @@
'PhabricatorAuthValidateController' => 'applications/auth/controller/PhabricatorAuthValidateController.php',
'PhabricatorAuthenticationConfigOptions' => 'applications/config/option/PhabricatorAuthenticationConfigOptions.php',
'PhabricatorAutoEventListener' => 'infrastructure/events/PhabricatorAutoEventListener.php',
+ 'PhabricatorBadge' => 'applications/badges/storage/PhabricatorBadge.php',
+ 'PhabricatorBadgesApplication' => 'applications/badges/application/PhabricatorBadgesApplication.php',
+ 'PhabricatorBadgesCreateCapability' => 'applications/badges/capability/PhabricatorBadgesCreateCapability.php',
+ 'PhabricatorBadgesDAO' => 'applications/badges/storage/PhabricatorBadgesDAO.php',
+ 'PhabricatorBadgesDefaultEditCapability' => 'applications/badges/capability/PhabricatorBadgesDefaultEditCapability.php',
+ 'PhabricatorBadgesDefaultViewCapability' => 'applications/badges/capability/PhabricatorBadgesDefaultViewCapability.php',
+ 'PhabricatorBadgesEditor' => 'applications/badges/editor/PhabricatorBadgesEditor.php',
+ 'PhabricatorBadgesIcon' => 'applications/badges/icon/PhabricatorBadgesIcon.php',
+ 'PhabricatorBadgesInterface' => 'applications/badges/interface/PhabricatorBadgesInterface.php',
+ 'PhabricatorBadgesPHIDType' => 'applications/badges/phid/PhabricatorBadgesPHIDType.php',
+ 'PhabricatorBadgesQuery' => 'applications/badges/query/PhabricatorBadgesQuery.php',
+ 'PhabricatorBadgesSchemaSpec' => 'applications/badges/storage/PhabricatorBadgesSchemaSpec.php',
+ 'PhabricatorBadgesSearchEngine' => 'applications/badges/query/PhabricatorBadgesSearchEngine.php',
+ 'PhabricatorBadgesTransaction' => 'applications/badges/storage/PhabricatorBadgesTransaction.php',
+ 'PhabricatorBadgesTransactionQuery' => 'applications/badges/query/PhabricatorBadgesTransactionQuery.php',
'PhabricatorBarePageUIExample' => 'applications/uiexample/examples/PhabricatorBarePageUIExample.php',
'PhabricatorBarePageView' => 'view/page/PhabricatorBarePageView.php',
'PhabricatorBaseURISetupCheck' => 'applications/config/check/PhabricatorBaseURISetupCheck.php',
@@ -3653,6 +3673,11 @@
'AuditConduitAPIMethod' => 'ConduitAPIMethod',
'AuditQueryConduitAPIMethod' => 'AuditConduitAPIMethod',
'AuthManageProvidersCapability' => 'PhabricatorPolicyCapability',
+ 'BadgesController' => 'PhabricatorController',
+ 'BadgesEditController' => 'BadgesController',
+ 'BadgesEditIconController' => 'BadgesController',
+ 'BadgesListController' => 'BadgesController',
+ 'BadgesViewController' => 'BadgesController',
'CalendarTimeUtil' => 'Phobject',
'CalendarTimeUtilTestCase' => 'PhabricatorTestCase',
'CelerityAPI' => 'Phobject',
@@ -5286,6 +5311,26 @@
'PhabricatorAuthValidateController' => 'PhabricatorAuthController',
'PhabricatorAuthenticationConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorAutoEventListener' => 'PhabricatorEventListener',
+ 'PhabricatorBadge' => array(
+ 'PhabricatorBadgesDAO',
+ 'PhabricatorPolicyInterface',
+ 'PhabricatorApplicationTransactionInterface',
+ 'PhabricatorDestructibleInterface',
+ ),
+ 'PhabricatorBadgesApplication' => 'PhabricatorApplication',
+ 'PhabricatorBadgesCreateCapability' => 'PhabricatorPolicyCapability',
+ 'PhabricatorBadgesDAO' => 'PhabricatorLiskDAO',
+ 'PhabricatorBadgesDefaultEditCapability' => 'PhabricatorPolicyCapability',
+ 'PhabricatorBadgesDefaultViewCapability' => 'PhabricatorPolicyCapability',
+ 'PhabricatorBadgesEditor' => 'PhabricatorApplicationTransactionEditor',
+ 'PhabricatorBadgesIcon' => 'Phobject',
+ 'PhabricatorBadgesInterface' => 'PhabricatorPHIDInterface',
+ 'PhabricatorBadgesPHIDType' => 'PhabricatorPHIDType',
+ 'PhabricatorBadgesQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
+ 'PhabricatorBadgesSchemaSpec' => 'PhabricatorConfigSchemaSpec',
+ 'PhabricatorBadgesSearchEngine' => 'PhabricatorApplicationSearchEngine',
+ 'PhabricatorBadgesTransaction' => 'PhabricatorApplicationTransaction',
+ 'PhabricatorBadgesTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhabricatorBarePageUIExample' => 'PhabricatorUIExample',
'PhabricatorBarePageView' => 'AphrontPageView',
'PhabricatorBaseURISetupCheck' => 'PhabricatorSetupCheck',
diff --git a/src/applications/badges/application/PhabricatorBadgesApplication.php b/src/applications/badges/application/PhabricatorBadgesApplication.php
new file mode 100644
--- /dev/null
+++ b/src/applications/badges/application/PhabricatorBadgesApplication.php
@@ -0,0 +1,69 @@
+<?php
+
+final class PhabricatorBadgesApplication extends PhabricatorApplication {
+
+ public function getName() {
+ return pht('Badges');
+ }
+
+ public function getBaseURI() {
+ return '/badges/';
+ }
+
+ public function getShortDescription() {
+ return pht('Achievements and Notority');
+ }
+
+ public function getFontIcon() {
+ return 'fa-trophy';
+ }
+
+ public function getFlavorText() {
+ return pht('Build developer self esteem through gamification.');
+ }
+
+ public function getApplicationGroup() {
+ return self::GROUP_UTILITIES;
+ }
+
+ public function canUninstall() {
+ return true;
+ }
+
+ public function isPrototype() {
+ return true;
+ }
+
+ public function getRoutes() {
+ return array(
+ '/badges/' => array(
+ '(?:query/(?P<queryKey>[^/]+)/)?' => 'BadgesListController',
+ 'create/' => 'BadgesEditController',
+ 'edit/(?:(?P<id>\d+)/)?' => 'BadgesEditController',
+ 'view/(?:(?P<id>\d+)/)?' => 'BadgesViewController',
+ 'icon/(?P<id>[1-9]\d*)/'
+ => 'BadgesEditIconController',
+ 'icon/'
+ => 'BadgesEditIconController',
+
+ ),
+ );
+ }
+
+ protected function getCustomCapabilities() {
+ return array(
+ PhabricatorBadgesCreateCapability::CAPABILITY => array(
+ 'default' => PhabricatorPolicies::POLICY_ADMIN,
+ 'caption' => pht('Default create policy for badges.'),
+ ),
+ PhabricatorBadgesDefaultEditCapability::CAPABILITY => array(
+ 'default' => PhabricatorPolicies::POLICY_ADMIN,
+ 'caption' => pht('Default edit policy for badges.'),
+ ),
+ PhabricatorBadgesDefaultViewCapability::CAPABILITY => array(
+ 'caption' => pht('Default view policy for badges.'),
+ ),
+ );
+ }
+
+}
diff --git a/src/applications/badges/capability/PhabricatorBadgesCreateCapability.php b/src/applications/badges/capability/PhabricatorBadgesCreateCapability.php
new file mode 100644
--- /dev/null
+++ b/src/applications/badges/capability/PhabricatorBadgesCreateCapability.php
@@ -0,0 +1,16 @@
+<?php
+
+final class PhabricatorBadgesCreateCapability
+ extends PhabricatorPolicyCapability {
+
+ const CAPABILITY = 'badges.default.create';
+
+ public function getCapabilityName() {
+ return pht('Can Create Badges');
+ }
+
+ public function describeCapabilityRejection() {
+ return pht('You do not have permission to create badges.');
+ }
+
+}
diff --git a/src/applications/badges/capability/PhabricatorBadgesDefaultEditCapability.php b/src/applications/badges/capability/PhabricatorBadgesDefaultEditCapability.php
new file mode 100644
--- /dev/null
+++ b/src/applications/badges/capability/PhabricatorBadgesDefaultEditCapability.php
@@ -0,0 +1,16 @@
+<?php
+
+final class PhabricatorBadgesDefaultEditCapability
+ extends PhabricatorPolicyCapability {
+
+ const CAPABILITY = 'badges.default.edit';
+
+ public function getCapabilityName() {
+ return pht('Can Edit Badges');
+ }
+
+ public function describeCapabilityRejection() {
+ return pht('You do not have permission to edit badges.');
+ }
+
+}
diff --git a/src/applications/badges/capability/PhabricatorBadgesDefaultViewCapability.php b/src/applications/badges/capability/PhabricatorBadgesDefaultViewCapability.php
new file mode 100644
--- /dev/null
+++ b/src/applications/badges/capability/PhabricatorBadgesDefaultViewCapability.php
@@ -0,0 +1,16 @@
+<?php
+
+final class PhabricatorBadgesDefaultViewCapability extends
+ PhabricatorPolicyCapability {
+
+ const CAPABILITY = 'badges.default.view';
+
+ public function getCapabilityName() {
+ return pht('Default View Policy');
+ }
+
+ public function shouldAllowPublicPolicySetting() {
+ return true;
+ }
+
+}
diff --git a/src/applications/badges/controller/BadgesController.php b/src/applications/badges/controller/BadgesController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/badges/controller/BadgesController.php
@@ -0,0 +1,3 @@
+<?php
+
+abstract class BadgesController extends PhabricatorController {}
diff --git a/src/applications/badges/controller/BadgesEditController.php b/src/applications/badges/controller/BadgesEditController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/badges/controller/BadgesEditController.php
@@ -0,0 +1,228 @@
+<?php
+
+final class BadgesEditController
+ extends BadgesController {
+
+ private $id;
+
+ public function willProcessRequest(array $data) {
+ $this->id = idx($data, 'id');
+ }
+
+ public function processRequest() {
+ $request = $this->getRequest();
+ $viewer = $request->getUser();
+
+ if ($this->id) {
+ $badge = id(new PhabricatorBadgesQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($this->id))
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
+ ->executeOne();
+ if (!$badge) {
+ return new Aphront404Response();
+ }
+ $is_new = false;
+ } else {
+ $badge = PhabricatorBadge::initializeNewBadge($viewer);
+ $is_new = true;
+ }
+
+ if ($is_new) {
+ $title = pht('Create Badge');
+ $button_text = pht('Create Badge');
+ $cancel_uri = $this->getApplicationURI();
+ } else {
+ $title = pht(
+ 'Edit %s',
+ $badge->getName());
+ $button_text = pht('Save Changes');
+ $cancel_uri = $this->getApplicationURI().'view/'.$this->id.'/';
+ }
+
+ $e_name = true;
+ $v_name = $badge->getName();
+
+ $e_icon = true;
+ $v_icon = $badge->getIcon();
+
+ $v_flav = $badge->getFlavor();
+ $v_desc = $badge->getDescription();
+ $v_qual = $badge->getQuality();
+ $v_stat = $badge->getStatus();
+
+ $validation_exception = null;
+ if ($request->isFormPost()) {
+ $v_name = $request->getStr('name');
+ $v_flav = $request->getStr('flavor');
+ $v_desc = $request->getStr('description');
+ $v_icon = $request->getStr('icon');
+ $v_stat = $request->getStr('status');
+ $v_qual = $request->getStr('quality');
+
+ $v_view = $request->getStr('viewPolicy');
+ $v_edit = $request->getStr('editPolicy');
+
+ $type_name = PhabricatorBadgesTransaction::TYPE_NAME;
+ $type_flav = PhabricatorBadgesTransaction::TYPE_FLAVOR;
+ $type_desc = PhabricatorBadgesTransaction::TYPE_DESCRIPTION;
+ $type_icon = PhabricatorBadgesTransaction::TYPE_ICON;
+ $type_qual = PhabricatorBadgesTransaction::TYPE_QUALITY;
+ $type_stat = PhabricatorBadgesTransaction::TYPE_STATUS;
+
+ $type_view = PhabricatorTransactions::TYPE_VIEW_POLICY;
+ $type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY;
+
+ $xactions = array();
+
+ $xactions[] = id(new PhabricatorBadgesTransaction())
+ ->setTransactionType($type_name)
+ ->setNewValue($v_name);
+
+ $xactions[] = id(new PhabricatorBadgesTransaction())
+ ->setTransactionType($type_flav)
+ ->setNewValue($v_flav);
+
+ $xactions[] = id(new PhabricatorBadgesTransaction())
+ ->setTransactionType($type_desc)
+ ->setNewValue($v_desc);
+
+ $xactions[] = id(new PhabricatorBadgesTransaction())
+ ->setTransactionType($type_icon)
+ ->setNewValue($v_icon);
+
+ $xactions[] = id(new PhabricatorBadgesTransaction())
+ ->setTransactionType($type_qual)
+ ->setNewValue($v_qual);
+
+ $xactions[] = id(new PhabricatorBadgesTransaction())
+ ->setTransactionType($type_stat)
+ ->setNewValue($v_stat);
+
+ $xactions[] = id(new PhabricatorBadgesTransaction())
+ ->setTransactionType($type_view)
+ ->setNewValue($v_view);
+
+ $xactions[] = id(new PhabricatorBadgesTransaction())
+ ->setTransactionType($type_edit)
+ ->setNewValue($v_edit);
+
+ $editor = id(new PhabricatorBadgesEditor())
+ ->setActor($viewer)
+ ->setContentSourceFromRequest($request)
+ ->setContinueOnNoEffect(true);
+
+ try {
+ $editor->applyTransactions($badge, $xactions);
+
+ return id(new AphrontRedirectResponse())
+ ->setURI('/badges/');
+ } catch (PhabricatorApplicationTransactionValidationException $ex) {
+ $validation_exception = $ex;
+
+ $e_name = $ex->getShortMessage($type_name);
+ $e_desc = $ex->getShortMessage($type_desc);
+
+ $badge->setViewPolicy($v_view);
+ $badge->setEditPolicy($v_edit);
+ }
+ }
+
+ if ($is_new) {
+ $icon_uri = $this->getApplicationURI('icon/');
+ } else {
+ $icon_uri = $this->getApplicationURI('icon/'.$badge->getID().'/');
+ }
+ $icon_display = PhabricatorBadgesIcon::renderIconForChooser($v_icon);
+
+ $policies = id(new PhabricatorPolicyQuery())
+ ->setViewer($viewer)
+ ->setObject($badge)
+ ->execute();
+
+ $form = id(new AphrontFormView())
+ ->setUser($viewer)
+ ->appendChild(
+ id(new AphrontFormTextControl())
+ ->setName('name')
+ ->setLabel(pht('Name'))
+ ->setValue($v_name)
+ ->setError($e_name))
+ ->appendChild(
+ id(new AphrontFormTextControl())
+ ->setName('flavor')
+ ->setLabel(pht('Flavor Text'))
+ ->setValue($v_flav))
+ ->appendChild(
+ id(new AphrontFormChooseButtonControl())
+ ->setLabel(pht('Icon'))
+ ->setName('icon')
+ ->setDisplayValue($icon_display)
+ ->setButtonText(pht('Choose Icon...'))
+ ->setChooseURI($icon_uri)
+ ->setValue($v_icon))
+ ->appendChild(
+ id(new AphrontFormSelectControl())
+ ->setName('quality')
+ ->setLabel(pht('Quality'))
+ ->setValue($v_qual)
+ ->setOptions($badge->getQualityNameMap()))
+ ->appendChild(
+ id(new AphrontFormSelectControl())
+ ->setLabel(pht('Status'))
+ ->setName('status')
+ ->setValue($v_stat)
+ ->setOptions($badge->getStatusNameMap()))
+ ->appendChild(
+ id(new PhabricatorRemarkupControl())
+ ->setUser($viewer)
+ ->setName('description')
+ ->setLabel(pht('Description'))
+ ->setValue($v_desc))
+ ->appendChild(
+ id(new AphrontFormPolicyControl())
+ ->setName('viewPolicy')
+ ->setPolicyObject($badge)
+ ->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
+ ->setPolicies($policies))
+ ->appendChild(
+ id(new AphrontFormPolicyControl())
+ ->setName('editPolicy')
+ ->setPolicyObject($badge)
+ ->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
+ ->setPolicies($policies))
+ ->appendChild(
+ id(new AphrontFormSubmitControl())
+ ->setValue($button_text)
+ ->addCancelButton($cancel_uri));
+
+ $crumbs = $this->buildApplicationCrumbs();
+ if ($is_new) {
+ $crumbs->addTextCrumb(pht('Create Badge'));
+ } else {
+ $crumbs->addTextCrumb(
+ $badge->getName(),
+ '/badges/view/'.$badge->getID().'/');
+ $crumbs->addTextCrumb(pht('Edit'));
+ }
+
+ $box = id(new PHUIObjectBoxView())
+ ->setValidationException($validation_exception)
+ ->setHeaderText($title)
+ ->appendChild($form);
+
+ return $this->buildApplicationPage(
+ array(
+ $crumbs,
+ $box,
+ ),
+ array(
+ 'title' => $title,
+ ));
+ }
+
+}
diff --git a/src/applications/badges/controller/BadgesEditIconController.php b/src/applications/badges/controller/BadgesEditIconController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/badges/controller/BadgesEditIconController.php
@@ -0,0 +1,107 @@
+<?php
+
+final class BadgesEditIconController
+ extends BadgesController {
+
+ private $id;
+
+ public function willProcessRequest(array $data) {
+ $this->id = idx($data, 'id');
+ }
+
+ public function processRequest() {
+ $request = $this->getRequest();
+ $viewer = $request->getUser();
+
+ if ($this->id) {
+ $badge = id(new PhabricatorBadgesQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($this->id))
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
+ ->executeOne();
+ if (!$badge) {
+ return new Aphront404Response();
+ }
+ $cancel_uri =
+ $this->getApplicationURI('view/'.$badge->getID().'/');
+ $badge_icon = $badge->getIcon();
+ } else {
+ $this->requireApplicationCapability(
+ PhabricatorBadgesCreateCapability::CAPABILITY);
+
+ $cancel_uri = '/badges/';
+ $badge_icon = $request->getStr('value');
+ }
+
+ require_celerity_resource('project-icon-css');
+ Javelin::initBehavior('phabricator-tooltips');
+
+ $badge_icons = PhabricatorBadgesIcon::getIconMap();
+
+ if ($request->isFormPost()) {
+ $v_icon = $request->getStr('icon');
+
+ return id(new AphrontAjaxResponse())->setContent(
+ array(
+ 'value' => $v_icon,
+ 'display' => PhabricatorBadgesIcon::renderIconForChooser($v_icon),
+ ));
+ }
+
+ $ii = 0;
+ $buttons = array();
+ foreach ($badge_icons as $icon => $label) {
+ $view = id(new PHUIIconView())
+ ->setIconFont($icon);
+
+ $aural = javelin_tag(
+ 'span',
+ array(
+ 'aural' => true,
+ ),
+ pht('Choose "%s" Icon', $label));
+
+ if ($icon == $badge_icon) {
+ $class_extra = ' selected';
+ } else {
+ $class_extra = null;
+ }
+
+ $buttons[] = javelin_tag(
+ 'button',
+ array(
+ 'class' => 'icon-button'.$class_extra,
+ 'name' => 'icon',
+ 'value' => $icon,
+ 'type' => 'submit',
+ 'sigil' => 'has-tooltip',
+ 'meta' => array(
+ 'tip' => $label,
+ ),
+ ),
+ array(
+ $aural,
+ $view,
+ ));
+ if ((++$ii % 4) == 0) {
+ $buttons[] = phutil_tag('br');
+ }
+ }
+
+ $buttons = phutil_tag(
+ 'div',
+ array(
+ 'class' => 'icon-grid',
+ ),
+ $buttons);
+
+ return $this->newDialog()
+ ->setTitle(pht('Choose Badge Icon'))
+ ->appendChild($buttons)
+ ->addCancelButton($cancel_uri);
+ }
+}
diff --git a/src/applications/badges/controller/BadgesListController.php b/src/applications/badges/controller/BadgesListController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/badges/controller/BadgesListController.php
@@ -0,0 +1,57 @@
+<?php
+
+final class BadgesListController
+ extends BadgesController {
+
+ private $queryKey;
+
+ public function shouldAllowPublic() {
+ return true;
+ }
+
+ public function willProcessRequest(array $data) {
+ $this->queryKey = idx($data, 'queryKey');
+ }
+
+ public function processRequest() {
+ $controller = id(new PhabricatorApplicationSearchController())
+ ->setQueryKey($this->queryKey)
+ ->setSearchEngine(new PhabricatorBadgesSearchEngine())
+ ->setNavigation($this->buildSideNavView());
+
+ return $this->delegateToController($controller);
+ }
+
+ public function buildSideNavView() {
+ $user = $this->getRequest()->getUser();
+
+ $nav = new AphrontSideNavFilterView();
+ $nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
+
+ id(new PhabricatorBadgesSearchEngine())
+ ->setViewer($user)
+ ->addNavigationItems($nav->getMenu());
+
+ $nav->selectFilter(null);
+
+ return $nav;
+ }
+
+ protected function buildApplicationCrumbs() {
+ $crumbs = parent::buildApplicationCrumbs();
+
+ $can_create = $this->hasApplicationCapability(
+ PhabricatorBadgesCreateCapability::CAPABILITY);
+
+ $crumbs->addAction(
+ id(new PHUIListItemView())
+ ->setName(pht('Create Badge'))
+ ->setHref($this->getApplicationURI('create/'))
+ ->setIcon('fa-plus-square')
+ ->setDisabled(!$can_create)
+ ->setWorkflow(!$can_create));
+
+ return $crumbs;
+ }
+
+}
diff --git a/src/applications/badges/controller/BadgesViewController.php b/src/applications/badges/controller/BadgesViewController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/badges/controller/BadgesViewController.php
@@ -0,0 +1,152 @@
+<?php
+
+final class BadgesViewController
+ extends BadgesController {
+
+ private $id;
+
+ public function shouldAllowPublic() {
+ return true;
+ }
+
+ public function willProcessRequest(array $data) {
+ $this->id = $data['id'];
+ }
+
+ public function processRequest() {
+ $request = $this->getRequest();
+ $viewer = $request->getUser();
+
+ $badge = id(new PhabricatorBadgesQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($this->id))
+ ->executeOne();
+ if (!$badge) {
+ return new Aphront404Response();
+ }
+
+ $crumbs = $this->buildApplicationCrumbs();
+ $crumbs->addTextCrumb($badge->getName());
+ $title = $badge->getName();
+
+ if ($badge->isClosed()) {
+ $status_icon = 'fa-ban';
+ $status_color = 'dark';
+ } else {
+ $status_icon = 'fa-check';
+ $status_color = 'bluegrey';
+ }
+ $status_name = idx(
+ PhabricatorBadge::getStatusNameMap(),
+ $badge->getStatus());
+
+ $header = id(new PHUIHeaderView())
+ ->setHeader($badge->getName())
+ ->setUser($viewer)
+ ->setPolicyObject($badge)
+ ->setStatus($status_icon, $status_color, $status_name);
+
+ $properties = $this->buildPropertyListView($badge);
+ $actions = $this->buildActionListView($badge);
+ $properties->setActionList($actions);
+
+ $box = id(new PHUIObjectBoxView())
+ ->setHeader($header)
+ ->addPropertyList($properties);
+
+ $timeline = $this->buildTransactionTimeline(
+ $badge,
+ new PhabricatorBadgesTransactionQuery());
+ $timeline
+ ->setShouldTerminate(true);
+
+ return $this->buildApplicationPage(
+ array(
+ $crumbs,
+ $box,
+ $timeline,
+ ),
+ array(
+ 'title' => $title,
+ 'pageObjects' => array($badge->getPHID()),
+ ));
+ }
+
+ private function buildPropertyListView(PhabricatorBadge $badge) {
+ $viewer = $this->getRequest()->getUser();
+
+ $view = id(new PHUIPropertyListView())
+ ->setUser($viewer)
+ ->setObject($badge);
+
+ $quality = idx($badge->getQualityNameMap(), $badge->getQuality());
+ $icon = idx($badge->getIconNameMap(), $badge->getIcon());
+
+ $view->addProperty(
+ pht('Quality'),
+ $quality);
+
+ $view->addProperty(
+ pht('Icon'),
+ $icon);
+
+ $view->addProperty(
+ pht('Flavor'),
+ $badge->getFlavor());
+
+ $view->invokeWillRenderEvent();
+
+ $description = $badge->getDescription();
+ if (strlen($description)) {
+ $description = PhabricatorMarkupEngine::renderOneObject(
+ id(new PhabricatorMarkupOneOff())->setContent($description),
+ 'default',
+ $viewer);
+
+ $view->addSectionHeader(pht('Description'));
+ $view->addTextContent($description);
+ }
+
+ $badge = id(new PHUIBadgeView())
+ ->setIcon($badge->getIcon())
+ ->setHeader($badge->getName())
+ ->setSubhead($badge->getFlavor())
+ ->setQuality($badge->getQuality());
+
+ $view->addTextContent($badge);
+
+ return $view;
+ }
+
+ private function buildActionListView(PhabricatorBadge $badge) {
+ $viewer = $this->getRequest()->getUser();
+ $id = $badge->getID();
+
+ $can_edit = PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $badge,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
+ $view = id(new PhabricatorActionListView())
+ ->setUser($viewer)
+ ->setObject($badge);
+
+ $view->addAction(
+ id(new PhabricatorActionView())
+ ->setName(pht('Edit Badge'))
+ ->setIcon('fa-pencil')
+ ->setDisabled(!$can_edit)
+ ->setWorkflow(!$can_edit)
+ ->setHref($this->getApplicationURI("/edit/{$id}/")));
+
+ $view->addAction(
+ id(new PhabricatorActionView())
+ ->setName('View Recipients')
+ ->setIcon('fa-users')
+ ->setDisabled(!$can_edit)
+ ->setHref($this->getApplicationURI("/recipients/{$id}/")));
+
+ return $view;
+ }
+
+}
diff --git a/src/applications/badges/editor/PhabricatorBadgesEditor.php b/src/applications/badges/editor/PhabricatorBadgesEditor.php
new file mode 100644
--- /dev/null
+++ b/src/applications/badges/editor/PhabricatorBadgesEditor.php
@@ -0,0 +1,144 @@
+<?php
+
+final class PhabricatorBadgesEditor
+ extends PhabricatorApplicationTransactionEditor {
+
+ public function getEditorApplicationClass() {
+ return 'PhabricatorBadgesApplication';
+ }
+
+ public function getEditorObjectsDescription() {
+ return pht('Badges');
+ }
+
+ public function getTransactionTypes() {
+ $types = parent::getTransactionTypes();
+
+ $types[] = PhabricatorBadgesTransaction::TYPE_NAME;
+ $types[] = PhabricatorBadgesTransaction::TYPE_FLAVOR;
+ $types[] = PhabricatorBadgesTransaction::TYPE_DESCRIPTION;
+ $types[] = PhabricatorBadgesTransaction::TYPE_ICON;
+ $types[] = PhabricatorBadgesTransaction::TYPE_STATUS;
+ $types[] = PhabricatorBadgesTransaction::TYPE_QUALITY;
+
+ $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
+ $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
+
+ return $types;
+ }
+
+ protected function getCustomTransactionOldValue(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+ switch ($xaction->getTransactionType()) {
+ case PhabricatorBadgesTransaction::TYPE_NAME:
+ return $object->getName();
+ case PhabricatorBadgesTransaction::TYPE_FLAVOR:
+ return $object->getFlavor();
+ case PhabricatorBadgesTransaction::TYPE_DESCRIPTION:
+ return $object->getDescription();
+ case PhabricatorBadgesTransaction::TYPE_ICON:
+ return $object->getIcon();
+ case PhabricatorBadgesTransaction::TYPE_QUALITY:
+ return $object->getQuality();
+ case PhabricatorBadgesTransaction::TYPE_STATUS:
+ return $object->getStatus();
+ }
+
+ return parent::getCustomTransactionOldValue($object, $xaction);
+ }
+
+ protected function getCustomTransactionNewValue(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+
+ switch ($xaction->getTransactionType()) {
+ case PhabricatorBadgesTransaction::TYPE_NAME:
+ case PhabricatorBadgesTransaction::TYPE_FLAVOR:
+ case PhabricatorBadgesTransaction::TYPE_DESCRIPTION:
+ case PhabricatorBadgesTransaction::TYPE_ICON:
+ case PhabricatorBadgesTransaction::TYPE_STATUS:
+ case PhabricatorBadgesTransaction::TYPE_QUALITY:
+ return $xaction->getNewValue();
+ }
+
+ return parent::getCustomTransactionNewValue($object, $xaction);
+ }
+
+ protected function applyCustomInternalTransaction(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+
+ $type = $xaction->getTransactionType();
+ switch ($type) {
+ case PhabricatorBadgesTransaction::TYPE_NAME:
+ $object->setName($xaction->getNewValue());
+ return;
+ case PhabricatorBadgesTransaction::TYPE_FLAVOR:
+ $object->setFlavor($xaction->getNewValue());
+ return;
+ case PhabricatorBadgesTransaction::TYPE_DESCRIPTION:
+ $object->setDescription($xaction->getNewValue());
+ return;
+ case PhabricatorBadgesTransaction::TYPE_ICON:
+ $object->setIcon($xaction->getNewValue());
+ return;
+ case PhabricatorBadgesTransaction::TYPE_QUALITY:
+ $object->setQuality($xaction->getNewValue());
+ return;
+ case PhabricatorBadgesTransaction::TYPE_STATUS:
+ $object->setStatus($xaction->getNewValue());
+ return;
+ }
+
+ return parent::applyCustomInternalTransaction($object, $xaction);
+ }
+
+ protected function applyCustomExternalTransaction(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+
+ $type = $xaction->getTransactionType();
+ switch ($type) {
+ case PhabricatorBadgesTransaction::TYPE_NAME:
+ case PhabricatorBadgesTransaction::TYPE_FLAVOR:
+ case PhabricatorBadgesTransaction::TYPE_DESCRIPTION:
+ case PhabricatorBadgesTransaction::TYPE_ICON:
+ case PhabricatorBadgesTransaction::TYPE_STATUS:
+ case PhabricatorBadgesTransaction::TYPE_QUALITY:
+ return;
+ }
+
+ return parent::applyCustomExternalTransaction($object, $xaction);
+ }
+
+ protected function validateTransaction(
+ PhabricatorLiskDAO $object,
+ $type,
+ array $xactions) {
+
+ $errors = parent::validateTransaction($object, $type, $xactions);
+
+ switch ($type) {
+ case PhabricatorBadgesTransaction::TYPE_NAME:
+ $missing = $this->validateIsEmptyTextField(
+ $object->getName(),
+ $xactions);
+
+ if ($missing) {
+ $error = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Required'),
+ pht('Badge name is required.'),
+ nonempty(last($xactions), null));
+
+ $error->setIsMissingFieldError(true);
+ $errors[] = $error;
+ }
+ break;
+ }
+
+ return $errors;
+ }
+
+}
diff --git a/src/applications/badges/icon/PhabricatorBadgesIcon.php b/src/applications/badges/icon/PhabricatorBadgesIcon.php
new file mode 100644
--- /dev/null
+++ b/src/applications/badges/icon/PhabricatorBadgesIcon.php
@@ -0,0 +1,53 @@
+<?php
+
+final class PhabricatorBadgesIcon extends Phobject {
+
+ public static function getIconMap() {
+ return
+ array(
+ 'fa-star' => pht('Superstar'),
+ 'fa-user' => pht('Average Person'),
+ 'fa-bug' => pht('Ladybug'),
+ 'fa-users' => pht('Triplets'),
+
+ 'fa-book' => pht('Nominomicon'),
+ 'fa-rocket' => pht('Escape Route'),
+ 'fa-life-ring' => pht('Foam Circle'),
+ 'fa-birthday-cake' => pht('Cake Day'),
+
+ 'fa-camera-retro' => pht('Leica Enthusiast'),
+ 'fa-beer' => pht('Liquid Lunch'),
+ 'fa-gift' => pht('Free Stuff'),
+ 'fa-eye' => pht('Eye See You'),
+
+ 'fa-heart' => pht('Love is Love'),
+ 'fa-trophy' => pht('Winner at Things'),
+ 'fa-umbrella' => pht('Rain Defender'),
+ 'fa-graduation-cap' => pht('In Debt'),
+
+ );
+ }
+
+ public static function getLabel($key) {
+ $map = self::getIconMap();
+ return $map[$key];
+ }
+
+ public static function getAPIName($key) {
+ return substr($key, 3);
+ }
+
+ public static function renderIconForChooser($icon) {
+ $badge_icons = self::getIconMap();
+
+ return phutil_tag(
+ 'span',
+ array(),
+ array(
+ id(new PHUIIconView())->setIconFont($icon),
+ ' ',
+ idx($badge_icons, $icon, pht('Unknown Icon')),
+ ));
+ }
+
+}
diff --git a/src/applications/badges/interface/PhabricatorBadgesInterface.php b/src/applications/badges/interface/PhabricatorBadgesInterface.php
new file mode 100644
--- /dev/null
+++ b/src/applications/badges/interface/PhabricatorBadgesInterface.php
@@ -0,0 +1,3 @@
+<?php
+
+interface PhabricatorBadgesInterface extends PhabricatorPHIDInterface {}
diff --git a/src/applications/badges/phid/PhabricatorBadgesPHIDType.php b/src/applications/badges/phid/PhabricatorBadgesPHIDType.php
new file mode 100644
--- /dev/null
+++ b/src/applications/badges/phid/PhabricatorBadgesPHIDType.php
@@ -0,0 +1,73 @@
+<?php
+
+final class PhabricatorBadgesPHIDType extends PhabricatorPHIDType {
+
+ const TYPECONST = 'BDGE';
+
+ public function getTypeName() {
+ return pht('Badge');
+ }
+
+ public function newObject() {
+ return new PhabricatorBadge();
+ }
+
+ public function getPHIDTypeApplicationClass() {
+ return 'PhabricatorBadgesApplication';
+ }
+
+ protected function buildQueryForObjects(
+ PhabricatorObjectQuery $query,
+ array $phids) {
+
+ return id(new PhabricatorBadgesQuery())
+ ->withPHIDs($phids);
+ }
+
+ public function loadHandles(
+ PhabricatorHandleQuery $query,
+ array $handles,
+ array $objects) {
+
+ foreach ($handles as $phid => $handle) {
+ $badge = $objects[$phid];
+
+ $id = $badge->getID();
+ $name = $badge->getName();
+
+ if ($badge->isClosed()) {
+ $handle->setStatus(PhabricatorObjectHandle::STATUS_CLOSED);
+ }
+
+ $handle->setName($name);
+ $handle->setFullName("{$name}");
+ $handle->setURI("/badges/view/{$id}/");
+ }
+ }
+
+ public function loadNamedObjects(
+ PhabricatorObjectQuery $query,
+ array $names) {
+
+ $id_map = array();
+ foreach ($names as $name) {
+ $id = (int)substr($name, 1);
+ $id_map[$id][] = $name;
+ }
+
+ $objects = id(new PhabricatorBadgesQuery())
+ ->setViewer($query->getViewer())
+ ->withIDs(array_keys($id_map))
+ ->execute();
+
+ $results = array();
+ foreach ($objects as $id => $object) {
+ foreach (idx($id_map, $id, array()) as $name) {
+ $results[$name] = $object;
+ }
+ }
+
+ return $results;
+ }
+
+}
diff --git a/src/applications/badges/query/PhabricatorBadgesQuery.php b/src/applications/badges/query/PhabricatorBadgesQuery.php
new file mode 100644
--- /dev/null
+++ b/src/applications/badges/query/PhabricatorBadgesQuery.php
@@ -0,0 +1,90 @@
+<?php
+
+final class PhabricatorBadgesQuery
+ extends PhabricatorCursorPagedPolicyAwareQuery {
+
+ private $ids;
+ private $phids;
+ private $qualities;
+ private $statuses;
+
+ public function withIDs(array $ids) {
+ $this->ids = $ids;
+ return $this;
+ }
+
+ public function withPHIDs(array $phids) {
+ $this->phids = $phids;
+ return $this;
+ }
+
+ public function withQualities(array $qualities) {
+ $this->qualities = $qualities;
+ return $this;
+ }
+
+ public function withStatuses(array $statuses) {
+ $this->statuses = $statuses;
+ return $this;
+ }
+
+ protected function loadPage() {
+ $table = new PhabricatorBadge();
+ $conn_r = $table->establishConnection('r');
+
+ $rows = queryfx_all(
+ $conn_r,
+ 'SELECT * FROM %T %Q %Q %Q',
+ $table->getTableName(),
+ $this->buildWhereClause($conn_r),
+ $this->buildOrderClause($conn_r),
+ $this->buildLimitClause($conn_r));
+
+ return $table->loadAllFromArray($rows);
+ }
+
+ protected function didFilterPage(array $badges) {
+ return $badges;
+ }
+
+ protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
+ $where = array();
+
+ $where[] = $this->buildPagingClause($conn_r);
+
+ if ($this->ids !== null) {
+ $where[] = qsprintf(
+ $conn_r,
+ 'id IN (%Ld)',
+ $this->ids);
+ }
+
+ if ($this->phids !== null) {
+ $where[] = qsprintf(
+ $conn_r,
+ 'phid IN (%Ls)',
+ $this->phids);
+ }
+
+ if ($this->qualities !== null) {
+ $where[] = qsprintf(
+ $conn_r,
+ 'quality IN (%Ls)',
+ $this->qualities);
+ }
+
+ if ($this->statuses !== null) {
+ $where[] = qsprintf(
+ $conn_r,
+ 'status IN (%Ls)',
+ $this->statuses);
+ }
+
+ return $this->formatWhereClause($where);
+ }
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorBadgesApplication';
+ }
+
+}
diff --git a/src/applications/badges/query/PhabricatorBadgesSearchEngine.php b/src/applications/badges/query/PhabricatorBadgesSearchEngine.php
new file mode 100644
--- /dev/null
+++ b/src/applications/badges/query/PhabricatorBadgesSearchEngine.php
@@ -0,0 +1,136 @@
+<?php
+
+final class PhabricatorBadgesSearchEngine
+ extends PhabricatorApplicationSearchEngine {
+
+ public function getResultTypeDescription() {
+ return pht('Badge');
+ }
+
+ public function getApplicationClassName() {
+ return 'PhabricatorBadgesApplication';
+ }
+
+ public function newQuery() {
+ return new PhabricatorBadgesQuery();
+ }
+
+ public function buildSavedQueryFromRequest(AphrontRequest $request) {
+ $saved = new PhabricatorSavedQuery();
+
+ $saved->setParameter(
+ 'statuses',
+ $this->readListFromRequest($request, 'statuses'));
+
+ return $saved;
+ }
+
+ public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
+ $query = new PhabricatorBadgesQuery();
+
+ $statuses = $saved->getParameter('statuses');
+ if ($statuses) {
+ $query->withStatuses($statuses);
+ }
+
+ return $query;
+ }
+
+ protected function buildCustomSearchFields() {
+ return array(
+ id(new PhabricatorSearchCheckboxesField())
+ ->setKey('statuses')
+ ->setLabel(pht('Status'))
+ ->setOptions(
+ id(new PhabricatorBadge())
+ ->getStatusNameMap()),
+ );
+ }
+
+ protected function buildQueryFromParameters(array $map) {
+ $query = $this->newQuery();
+
+ if ($map['statuses']) {
+ $query->withStatuses($map['statuses']);
+ }
+
+ return $query;
+ }
+
+ protected function getURI($path) {
+ return '/badges/'.$path;
+ }
+
+ protected function getBuiltinQueryNames() {
+ $names = array();
+
+ $names['open'] = pht('Active Badges');
+ $names['all'] = pht('All Badges');
+
+ return $names;
+ }
+
+ public function buildSavedQueryFromBuiltin($query_key) {
+ $query = $this->newSavedQuery();
+ $query->setQueryKey($query_key);
+
+ switch ($query_key) {
+ case 'all':
+ return $query;
+ case 'open':
+ return $query->setParameter(
+ 'statuses',
+ array(
+ PhabricatorBadge::STATUS_OPEN,
+ ));
+ }
+
+ return parent::buildSavedQueryFromBuiltin($query_key);
+ }
+
+ protected function getRequiredHandlePHIDsForResultList(
+ array $badges,
+ PhabricatorSavedQuery $query) {
+
+ $phids = array();
+
+ return $phids;
+ }
+
+ protected function renderResultList(
+ array $badges,
+ PhabricatorSavedQuery $query,
+ array $handles) {
+ assert_instances_of($badges, 'PhabricatorBadge');
+
+ $viewer = $this->requireViewer();
+
+ $list = id(new PHUIObjectItemListView());
+ foreach ($badges as $badge) {
+
+ $quality = idx($badge->getQualityNameMap(), $badge->getQuality());
+
+ $item = id(new PHUIObjectItemView())
+ ->setHeader($badge->getName())
+ ->setStatusIcon($badge->getIcon().' '.$badge->getQuality())
+ ->setHref('/badges/view/'.$badge->getID().'/')
+ ->addAttribute($quality)
+ ->addAttribute($badge->getFlavor());
+
+ if ($badge->isClosed()) {
+ $item->setDisabled(true);
+ $item->addIcon('fa-ban', pht('Archived'));
+ }
+
+ $list->addItem($item);
+ }
+
+ $result = new PhabricatorApplicationSearchResultView();
+ $result->setObjectList($list);
+ $result->setNoDataString(pht('No badges found.'));
+
+ return $result;
+
+ }
+
+}
diff --git a/src/applications/badges/query/PhabricatorBadgesTransactionQuery.php b/src/applications/badges/query/PhabricatorBadgesTransactionQuery.php
new file mode 100644
--- /dev/null
+++ b/src/applications/badges/query/PhabricatorBadgesTransactionQuery.php
@@ -0,0 +1,10 @@
+<?php
+
+final class PhabricatorBadgesTransactionQuery
+ extends PhabricatorApplicationTransactionQuery {
+
+ public function getTemplateApplicationTransaction() {
+ return new PhabricatorBadgesTransaction();
+ }
+
+}
diff --git a/src/applications/badges/storage/PhabricatorBadge.php b/src/applications/badges/storage/PhabricatorBadge.php
new file mode 100644
--- /dev/null
+++ b/src/applications/badges/storage/PhabricatorBadge.php
@@ -0,0 +1,177 @@
+<?php
+
+final class PhabricatorBadge extends PhabricatorBadgesDAO
+ implements
+ PhabricatorPolicyInterface,
+ PhabricatorApplicationTransactionInterface,
+ PhabricatorDestructibleInterface {
+
+ protected $name;
+ protected $flavor;
+ protected $description;
+ protected $icon;
+ protected $quality;
+ protected $viewPolicy;
+ protected $editPolicy;
+ protected $status;
+
+ const STATUS_OPEN = 'open';
+ const STATUS_CLOSED = 'closed';
+
+ const DEFAULT_ICON = 'fa-star';
+ const DEFAULT_QUALITY = 'green';
+
+ const POOR = 'grey';
+ const COMMON = 'white';
+ const UNCOMMON = 'green';
+ const RARE = 'blue';
+ const EPIC = 'indigo';
+ const LEGENDARY = 'orange';
+ const HEIRLOOM = 'yellow';
+
+ public static function getStatusNameMap() {
+ return array(
+ self::STATUS_OPEN => pht('Active'),
+ self::STATUS_CLOSED => pht('Archived'),
+ );
+ }
+
+ public static function getQualityNameMap() {
+ return array(
+ self::POOR => pht('Poor'),
+ self::COMMON => pht('Common'),
+ self::UNCOMMON => pht('Uncommon'),
+ self::RARE => pht('Rare'),
+ self::EPIC => pht('Epic'),
+ self::LEGENDARY => pht('Legendary'),
+ self::HEIRLOOM => pht('Heirloom'),
+ );
+ }
+
+ public static function getIconNameMap() {
+ return PhabricatorBadgesIcon::getIconMap();
+ }
+
+ public static function initializeNewBadge(PhabricatorUser $actor) {
+ $app = id(new PhabricatorApplicationQuery())
+ ->setViewer($actor)
+ ->withClasses(array('PhabricatorBadgesApplication'))
+ ->executeOne();
+
+ $view_policy =
+ $app->getPolicy(PhabricatorBadgesDefaultViewCapability::CAPABILITY);
+
+ $edit_policy =
+ $app->getPolicy(PhabricatorBadgesDefaultEditCapability::CAPABILITY);
+
+ return id(new PhabricatorBadge())
+ ->setIcon(self::DEFAULT_ICON)
+ ->setQuality(self::DEFAULT_QUALITY)
+ ->setViewPolicy($view_policy)
+ ->setEditPolicy($edit_policy)
+ ->setStatus(self::STATUS_OPEN);
+ }
+
+ protected function getConfiguration() {
+ return array(
+ self::CONFIG_AUX_PHID => true,
+ self::CONFIG_COLUMN_SCHEMA => array(
+ 'name' => 'text255',
+ 'flavor' => 'text255',
+ 'description' => 'text',
+ 'icon' => 'text255',
+ 'quality' => 'text255',
+ 'status' => 'text32',
+ ),
+ ) + parent::getConfiguration();
+ }
+
+ public function generatePHID() {
+ return
+ PhabricatorPHID::generateNewPHID(PhabricatorBadgesPHIDType::TYPECONST);
+ }
+
+ public function isClosed() {
+ return ($this->getStatus() == self::STATUS_CLOSED);
+ }
+
+
+/* -( PhabricatorPolicyInterface )----------------------------------------- */
+
+
+ public function getCapabilities() {
+ return array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ );
+ }
+
+ public function getPolicy($capability) {
+ switch ($capability) {
+ case PhabricatorPolicyCapability::CAN_VIEW:
+ return $this->getViewPolicy();
+ case PhabricatorPolicyCapability::CAN_EDIT:
+ return $this->getEditPolicy();
+ }
+ }
+
+ public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
+ return false;
+ }
+
+ public function describeAutomaticCapability($capability) {
+ return null;
+ }
+
+
+/* -( PhabricatorApplicationTransactionInterface )------------------------- */
+
+
+ public function getApplicationTransactionEditor() {
+ return new PhabricatorBadgesEditor();
+ }
+
+ public function getApplicationTransactionObject() {
+ return $this;
+ }
+
+ public function getApplicationTransactionTemplate() {
+ return new PhabricatorBadgesTransaction();
+ }
+
+ public function willRenderTimeline(
+ PhabricatorApplicationTransactionView $timeline,
+ AphrontRequest $request) {
+
+ return $timeline;
+ }
+
+
+/* -( PhabricatorSubscribableInterface )----------------------------------- */
+
+
+ public function isAutomaticallySubscribed($phid) {
+ return false;
+ }
+
+ public function shouldShowSubscribersProperty() {
+ return false;
+ }
+
+ public function shouldAllowSubscription($phid) {
+ return false;
+ }
+
+
+/* -( PhabricatorDestructibleInterface )----------------------------------- */
+
+
+ public function destroyObjectPermanently(
+ PhabricatorDestructionEngine $engine) {
+
+ $this->openTransaction();
+ $this->delete();
+ $this->saveTransaction();
+ }
+
+}
diff --git a/src/applications/badges/storage/PhabricatorBadgesDAO.php b/src/applications/badges/storage/PhabricatorBadgesDAO.php
new file mode 100644
--- /dev/null
+++ b/src/applications/badges/storage/PhabricatorBadgesDAO.php
@@ -0,0 +1,9 @@
+<?php
+
+abstract class PhabricatorBadgesDAO extends PhabricatorLiskDAO {
+
+ public function getApplicationName() {
+ return 'badges';
+ }
+
+}
diff --git a/src/applications/badges/storage/PhabricatorBadgesSchemaSpec.php b/src/applications/badges/storage/PhabricatorBadgesSchemaSpec.php
new file mode 100644
--- /dev/null
+++ b/src/applications/badges/storage/PhabricatorBadgesSchemaSpec.php
@@ -0,0 +1,10 @@
+<?php
+
+final class PhabricatorBadgesSchemaSpec
+ extends PhabricatorConfigSchemaSpec {
+
+ public function buildSchemata() {
+ $this->buildEdgeSchemata(new PhabricatorBadge());
+ }
+
+}
diff --git a/src/applications/badges/storage/PhabricatorBadgesTransaction.php b/src/applications/badges/storage/PhabricatorBadgesTransaction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/badges/storage/PhabricatorBadgesTransaction.php
@@ -0,0 +1,195 @@
+<?php
+
+final class PhabricatorBadgesTransaction
+ extends PhabricatorApplicationTransaction {
+
+ const TYPE_NAME = 'badges:name';
+ const TYPE_DESCRIPTION = 'badges:description';
+ const TYPE_QUALITY = 'badges:quality';
+ const TYPE_ICON = 'badges:icon';
+ const TYPE_STATUS = 'badges:status';
+ const TYPE_FLAVOR = 'badges:flavor';
+
+ public function getApplicationName() {
+ return 'badges';
+ }
+
+ public function getApplicationTransactionType() {
+ return PhabricatorBadgesPHIDType::TYPECONST;
+ }
+
+ public function getApplicationTransactionCommentObject() {
+ return null;
+ }
+
+ public function getTitle() {
+ $author_phid = $this->getAuthorPHID();
+ $object_phid = $this->getObjectPHID();
+
+ $old = $this->getOldValue();
+ $new = $this->getNewValue();
+
+ $type = $this->getTransactionType();
+ switch ($type) {
+ case self::TYPE_NAME:
+ if ($old === null) {
+ return pht(
+ '%s created this badge.',
+ $this->renderHandleLink($author_phid));
+ } else {
+ return pht(
+ '%s renamed this badge from "%s" to "%s".',
+ $this->renderHandleLink($author_phid),
+ $old,
+ $new);
+ }
+ break;
+ case self::TYPE_FLAVOR:
+ if ($old === null) {
+ return pht(
+ '%s set the flavor text for this badge.',
+ $this->renderHandleLink($author_phid));
+ } else {
+ return pht(
+ '%s updated the flavor text for this badge.',
+ $this->renderHandleLink($author_phid));
+ }
+ break;
+ case self::TYPE_DESCRIPTION:
+ if ($old === null) {
+ return pht(
+ '%s set the description for this badge.',
+ $this->renderHandleLink($author_phid));
+ } else {
+ return pht(
+ '%s updated the description for this badge.',
+ $this->renderHandleLink($author_phid));
+ }
+ break;
+ case self::TYPE_ICON:
+ if ($old === null) {
+ return pht(
+ '%s set the icon for this badge as "%s".',
+ $this->renderHandleLink($author_phid),
+ $new);
+ } else {
+ $icon_map = PhabricatorBadge::getIconNameMap();
+ $icon_new = idx($icon_map, $new, $new);
+ $icon_old = idx($icon_map, $old, $old);
+ return pht(
+ '%s updated the icon for this badge from "%s" to "%s".',
+ $this->renderHandleLink($author_phid),
+ $icon_old,
+ $icon_new);
+ }
+ break;
+ case self::TYPE_QUALITY:
+ if ($old === null) {
+ return pht(
+ '%s set the quality for this badge as "%s".',
+ $this->renderHandleLink($author_phid),
+ $new);
+ } else {
+ $qual_map = PhabricatorBadge::getQualityNameMap();
+ $qual_new = idx($qual_map, $new, $new);
+ $qual_old = idx($qual_map, $old, $old);
+ return pht(
+ '%s updated the quality for this badge from "%s" to "%s".',
+ $this->renderHandleLink($author_phid),
+ $qual_old,
+ $qual_new);
+ }
+ break;
+ }
+
+ return parent::getTitle();
+ }
+
+ public function getTitleForFeed() {
+ $author_phid = $this->getAuthorPHID();
+ $object_phid = $this->getObjectPHID();
+
+ $old = $this->getOldValue();
+ $new = $this->getNewValue();
+
+ $type = $this->getTransactionType();
+ switch ($type) {
+ case self::TYPE_NAME:
+ if ($old === null) {
+ return pht(
+ '%s created %s.',
+ $this->renderHandleLink($author_phid),
+ $this->renderHandleLink($object_phid));
+
+ } else {
+ return pht(
+ '%s renamed %s.',
+ $this->renderHandleLink($author_phid),
+ $this->renderHandleLink($object_phid));
+ }
+ break;
+ case self::TYPE_FLAVOR:
+ return pht(
+ '%s updated the flavor text for %s.',
+ $this->renderHandleLink($author_phid),
+ $this->renderHandleLink($object_phid));
+ case self::TYPE_ICON:
+ return pht(
+ '%s updated the icon for %s.',
+ $this->renderHandleLink($author_phid),
+ $this->renderHandleLink($object_phid));
+ case self::TYPE_QUALITY:
+ return pht(
+ '%s updated the quality level for %s.',
+ $this->renderHandleLink($author_phid),
+ $this->renderHandleLink($object_phid));
+ case self::TYPE_DESCRIPTION:
+ return pht(
+ '%s updated the description for %s.',
+ $this->renderHandleLink($author_phid),
+ $this->renderHandleLink($object_phid));
+ case self::TYPE_STATUS:
+ switch ($new) {
+ case PhabricatorBadge::STATUS_OPEN:
+ return pht(
+ '%s activated %s.',
+ $this->renderHandleLink($author_phid),
+ $this->renderHandleLink($object_phid));
+ case PhabricatorBadge::STATUS_CLOSED:
+ return pht(
+ '%s archived %s.',
+ $this->renderHandleLink($author_phid),
+ $this->renderHandleLink($object_phid));
+ }
+ break;
+ }
+
+ return parent::getTitleForFeed();
+ }
+
+
+ public function shouldHide() {
+ $old = $this->getOldValue();
+ switch ($this->getTransactionType()) {
+ case self::TYPE_DESCRIPTION:
+ return ($old === null);
+ }
+ return parent::shouldHide();
+ }
+
+ public function hasChangeDetails() {
+ switch ($this->getTransactionType()) {
+ case self::TYPE_DESCRIPTION:
+ return ($this->getOldValue() !== null);
+ }
+
+ return parent::hasChangeDetails();
+ }
+
+ public function renderChangeDetails(PhabricatorUser $viewer) {
+ return $this->renderTextCorpusChangeDetails(
+ $viewer,
+ $this->getOldValue(),
+ $this->getNewValue());
+ }
+}
diff --git a/src/applications/tokens/application/PhabricatorTokensApplication.php b/src/applications/tokens/application/PhabricatorTokensApplication.php
--- a/src/applications/tokens/application/PhabricatorTokensApplication.php
+++ b/src/applications/tokens/application/PhabricatorTokensApplication.php
@@ -11,7 +11,7 @@
}
public function getFontIcon() {
- return 'fa-trophy';
+ return 'fa-heart';
}
public function getTitleGlyph() {
diff --git a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
--- a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
+++ b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
@@ -106,6 +106,7 @@
'db.almanac' => array(),
'db.multimeter' => array(),
'db.spaces' => array(),
+ 'db.badges' => array(),
'0000.legacy.sql' => array(
'legacy' => 0,
),
diff --git a/webroot/rsrc/css/phui/phui-property-list-view.css b/webroot/rsrc/css/phui/phui-property-list-view.css
--- a/webroot/rsrc/css/phui/phui-property-list-view.css
+++ b/webroot/rsrc/css/phui/phui-property-list-view.css
@@ -98,6 +98,12 @@
border-width: 1px 0 0;
}
+.phui-property-list-container + .phui-property-list-text-content {
+ border-color: {$thinblueborder};
+ border-style: solid;
+ border-width: 1px 0 0;
+}
+
.phui-property-list-section-noninitial .phui-property-list-section-header {
border-top: none;
}

File Metadata

Mime Type
text/plain
Expires
Sun, May 12, 2:58 PM (3 w, 2 d ago)
Storage Engine
amazon-s3
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
phabricator/secure/rn/g4/xw6iwlznrrwqgle5
Default Alt Text
D13626.id32923.diff (56 KB)

Event Timeline