mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-29 00:40:57 +01:00
Badges v0.1
Summary: Basic plumbing for Badges application. - You can make Badges. - You can look at a list of them. - They can be edited. - They can be assigned to people. - You can revoke them from people. - You can subscribe to them. Test Plan: Make Badges with various options. Give them to people. Take them away from people. Reviewers: lpriestley, epriestley Reviewed By: epriestley Subscribers: tycho.tatitscheff, johnny-bit, epriestley, Korvin Maniphest Tasks: T6526 Differential Revision: https://secure.phabricator.com/D13626
This commit is contained in:
parent
fdd6351a64
commit
ffadf64751
34 changed files with 2317 additions and 8 deletions
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
return array(
|
return array(
|
||||||
'names' => array(
|
'names' => array(
|
||||||
'core.pkg.css' => 'b5cc2f39',
|
'core.pkg.css' => 'f79ebe46',
|
||||||
'core.pkg.js' => 'a590b451',
|
'core.pkg.js' => 'a590b451',
|
||||||
'darkconsole.pkg.js' => 'e7393ebb',
|
'darkconsole.pkg.js' => 'e7393ebb',
|
||||||
'differential.pkg.css' => '9451634c',
|
'differential.pkg.css' => '9451634c',
|
||||||
|
@ -142,10 +142,10 @@ return array(
|
||||||
'rsrc/css/phui/phui-info-view.css' => '5b16bac6',
|
'rsrc/css/phui/phui-info-view.css' => '5b16bac6',
|
||||||
'rsrc/css/phui/phui-list.css' => '125599df',
|
'rsrc/css/phui/phui-list.css' => '125599df',
|
||||||
'rsrc/css/phui/phui-object-box.css' => '407eaf5a',
|
'rsrc/css/phui/phui-object-box.css' => '407eaf5a',
|
||||||
'rsrc/css/phui/phui-object-item-list-view.css' => 'fc19bfc1',
|
'rsrc/css/phui/phui-object-item-list-view.css' => 'a1b990b7',
|
||||||
'rsrc/css/phui/phui-pager.css' => 'bea33d23',
|
'rsrc/css/phui/phui-pager.css' => 'bea33d23',
|
||||||
'rsrc/css/phui/phui-pinboard-view.css' => '2495140e',
|
'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-remarkup-preview.css' => '867f85b3',
|
||||||
'rsrc/css/phui/phui-spacing.css' => '042804d6',
|
'rsrc/css/phui/phui-spacing.css' => '042804d6',
|
||||||
'rsrc/css/phui/phui-status.css' => '888cedb8',
|
'rsrc/css/phui/phui-status.css' => '888cedb8',
|
||||||
|
@ -797,10 +797,10 @@ return array(
|
||||||
'phui-inline-comment-view-css' => '9fadd6b8',
|
'phui-inline-comment-view-css' => '9fadd6b8',
|
||||||
'phui-list-view-css' => '125599df',
|
'phui-list-view-css' => '125599df',
|
||||||
'phui-object-box-css' => '407eaf5a',
|
'phui-object-box-css' => '407eaf5a',
|
||||||
'phui-object-item-list-view-css' => 'fc19bfc1',
|
'phui-object-item-list-view-css' => 'a1b990b7',
|
||||||
'phui-pager-css' => 'bea33d23',
|
'phui-pager-css' => 'bea33d23',
|
||||||
'phui-pinboard-view-css' => '2495140e',
|
'phui-pinboard-view-css' => '2495140e',
|
||||||
'phui-property-list-view-css' => '1baf23eb',
|
'phui-property-list-view-css' => 'aeb09581',
|
||||||
'phui-remarkup-preview-css' => '867f85b3',
|
'phui-remarkup-preview-css' => '867f85b3',
|
||||||
'phui-spacing-css' => '042804d6',
|
'phui-spacing-css' => '042804d6',
|
||||||
'phui-status-list-view-css' => '888cedb8',
|
'phui-status-list-view-css' => '888cedb8',
|
||||||
|
|
54
resources/sql/autopatches/20150712.badges.1.sql
Normal file
54
resources/sql/autopatches/20150712.badges.1.sql
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
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,
|
||||||
|
creatorPHID varbinary(64) NOT NULL,
|
||||||
|
UNIQUE KEY `key_phid` (phid),
|
||||||
|
KEY `key_creator` (creatorPHID, dateModified)
|
||||||
|
) 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};
|
|
@ -1617,6 +1617,29 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorAuthValidateController' => 'applications/auth/controller/PhabricatorAuthValidateController.php',
|
'PhabricatorAuthValidateController' => 'applications/auth/controller/PhabricatorAuthValidateController.php',
|
||||||
'PhabricatorAuthenticationConfigOptions' => 'applications/config/option/PhabricatorAuthenticationConfigOptions.php',
|
'PhabricatorAuthenticationConfigOptions' => 'applications/config/option/PhabricatorAuthenticationConfigOptions.php',
|
||||||
'PhabricatorAutoEventListener' => 'infrastructure/events/PhabricatorAutoEventListener.php',
|
'PhabricatorAutoEventListener' => 'infrastructure/events/PhabricatorAutoEventListener.php',
|
||||||
|
'PhabricatorBadgeHasRecipientEdgeType' => 'applications/badges/edge/PhabricatorBadgeHasRecipientEdgeType.php',
|
||||||
|
'PhabricatorBadgesApplication' => 'applications/badges/application/PhabricatorBadgesApplication.php',
|
||||||
|
'PhabricatorBadgesBadge' => 'applications/badges/storage/PhabricatorBadgesBadge.php',
|
||||||
|
'PhabricatorBadgesController' => 'applications/badges/controller/PhabricatorBadgesController.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',
|
||||||
|
'PhabricatorBadgesEditController' => 'applications/badges/controller/PhabricatorBadgesEditController.php',
|
||||||
|
'PhabricatorBadgesEditIconController' => 'applications/badges/controller/PhabricatorBadgesEditIconController.php',
|
||||||
|
'PhabricatorBadgesEditRecipientsController' => 'applications/badges/controller/PhabricatorBadgesEditRecipientsController.php',
|
||||||
|
'PhabricatorBadgesEditor' => 'applications/badges/editor/PhabricatorBadgesEditor.php',
|
||||||
|
'PhabricatorBadgesIcon' => 'applications/badges/icon/PhabricatorBadgesIcon.php',
|
||||||
|
'PhabricatorBadgesListController' => 'applications/badges/controller/PhabricatorBadgesListController.php',
|
||||||
|
'PhabricatorBadgesPHIDType' => 'applications/badges/phid/PhabricatorBadgesPHIDType.php',
|
||||||
|
'PhabricatorBadgesQuery' => 'applications/badges/query/PhabricatorBadgesQuery.php',
|
||||||
|
'PhabricatorBadgesRecipientsListView' => 'applications/badges/view/PhabricatorBadgesRecipientsListView.php',
|
||||||
|
'PhabricatorBadgesRemoveRecipientsController' => 'applications/badges/controller/PhabricatorBadgesRemoveRecipientsController.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',
|
||||||
|
'PhabricatorBadgesViewController' => 'applications/badges/controller/PhabricatorBadgesViewController.php',
|
||||||
'PhabricatorBarePageUIExample' => 'applications/uiexample/examples/PhabricatorBarePageUIExample.php',
|
'PhabricatorBarePageUIExample' => 'applications/uiexample/examples/PhabricatorBarePageUIExample.php',
|
||||||
'PhabricatorBarePageView' => 'view/page/PhabricatorBarePageView.php',
|
'PhabricatorBarePageView' => 'view/page/PhabricatorBarePageView.php',
|
||||||
'PhabricatorBaseURISetupCheck' => 'applications/config/check/PhabricatorBaseURISetupCheck.php',
|
'PhabricatorBaseURISetupCheck' => 'applications/config/check/PhabricatorBaseURISetupCheck.php',
|
||||||
|
@ -2561,6 +2584,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorQueryOrderTestCase' => 'infrastructure/query/order/__tests__/PhabricatorQueryOrderTestCase.php',
|
'PhabricatorQueryOrderTestCase' => 'infrastructure/query/order/__tests__/PhabricatorQueryOrderTestCase.php',
|
||||||
'PhabricatorQueryOrderVector' => 'infrastructure/query/order/PhabricatorQueryOrderVector.php',
|
'PhabricatorQueryOrderVector' => 'infrastructure/query/order/PhabricatorQueryOrderVector.php',
|
||||||
'PhabricatorRecaptchaConfigOptions' => 'applications/config/option/PhabricatorRecaptchaConfigOptions.php',
|
'PhabricatorRecaptchaConfigOptions' => 'applications/config/option/PhabricatorRecaptchaConfigOptions.php',
|
||||||
|
'PhabricatorRecipientHasBadgeEdgeType' => 'applications/badges/edge/PhabricatorRecipientHasBadgeEdgeType.php',
|
||||||
'PhabricatorRedirectController' => 'applications/base/controller/PhabricatorRedirectController.php',
|
'PhabricatorRedirectController' => 'applications/base/controller/PhabricatorRedirectController.php',
|
||||||
'PhabricatorRefreshCSRFController' => 'applications/auth/controller/PhabricatorRefreshCSRFController.php',
|
'PhabricatorRefreshCSRFController' => 'applications/auth/controller/PhabricatorRefreshCSRFController.php',
|
||||||
'PhabricatorRegistrationProfile' => 'applications/people/storage/PhabricatorRegistrationProfile.php',
|
'PhabricatorRegistrationProfile' => 'applications/people/storage/PhabricatorRegistrationProfile.php',
|
||||||
|
@ -5339,6 +5363,36 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorAuthValidateController' => 'PhabricatorAuthController',
|
'PhabricatorAuthValidateController' => 'PhabricatorAuthController',
|
||||||
'PhabricatorAuthenticationConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
'PhabricatorAuthenticationConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||||
'PhabricatorAutoEventListener' => 'PhabricatorEventListener',
|
'PhabricatorAutoEventListener' => 'PhabricatorEventListener',
|
||||||
|
'PhabricatorBadgeHasRecipientEdgeType' => 'PhabricatorEdgeType',
|
||||||
|
'PhabricatorBadgesApplication' => 'PhabricatorApplication',
|
||||||
|
'PhabricatorBadgesBadge' => array(
|
||||||
|
'PhabricatorBadgesDAO',
|
||||||
|
'PhabricatorPolicyInterface',
|
||||||
|
'PhabricatorApplicationTransactionInterface',
|
||||||
|
'PhabricatorSubscribableInterface',
|
||||||
|
'PhabricatorFlaggableInterface',
|
||||||
|
'PhabricatorDestructibleInterface',
|
||||||
|
),
|
||||||
|
'PhabricatorBadgesController' => 'PhabricatorController',
|
||||||
|
'PhabricatorBadgesCreateCapability' => 'PhabricatorPolicyCapability',
|
||||||
|
'PhabricatorBadgesDAO' => 'PhabricatorLiskDAO',
|
||||||
|
'PhabricatorBadgesDefaultEditCapability' => 'PhabricatorPolicyCapability',
|
||||||
|
'PhabricatorBadgesDefaultViewCapability' => 'PhabricatorPolicyCapability',
|
||||||
|
'PhabricatorBadgesEditController' => 'PhabricatorBadgesController',
|
||||||
|
'PhabricatorBadgesEditIconController' => 'PhabricatorBadgesController',
|
||||||
|
'PhabricatorBadgesEditRecipientsController' => 'PhabricatorBadgesController',
|
||||||
|
'PhabricatorBadgesEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||||
|
'PhabricatorBadgesIcon' => 'Phobject',
|
||||||
|
'PhabricatorBadgesListController' => 'PhabricatorBadgesController',
|
||||||
|
'PhabricatorBadgesPHIDType' => 'PhabricatorPHIDType',
|
||||||
|
'PhabricatorBadgesQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||||
|
'PhabricatorBadgesRecipientsListView' => 'AphrontTagView',
|
||||||
|
'PhabricatorBadgesRemoveRecipientsController' => 'PhabricatorBadgesController',
|
||||||
|
'PhabricatorBadgesSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
||||||
|
'PhabricatorBadgesSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||||
|
'PhabricatorBadgesTransaction' => 'PhabricatorApplicationTransaction',
|
||||||
|
'PhabricatorBadgesTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||||
|
'PhabricatorBadgesViewController' => 'PhabricatorBadgesController',
|
||||||
'PhabricatorBarePageUIExample' => 'PhabricatorUIExample',
|
'PhabricatorBarePageUIExample' => 'PhabricatorUIExample',
|
||||||
'PhabricatorBarePageView' => 'AphrontPageView',
|
'PhabricatorBarePageView' => 'AphrontPageView',
|
||||||
'PhabricatorBaseURISetupCheck' => 'PhabricatorSetupCheck',
|
'PhabricatorBaseURISetupCheck' => 'PhabricatorSetupCheck',
|
||||||
|
@ -6448,6 +6502,7 @@ phutil_register_library_map(array(
|
||||||
'Iterator',
|
'Iterator',
|
||||||
),
|
),
|
||||||
'PhabricatorRecaptchaConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
'PhabricatorRecaptchaConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||||
|
'PhabricatorRecipientHasBadgeEdgeType' => 'PhabricatorEdgeType',
|
||||||
'PhabricatorRedirectController' => 'PhabricatorController',
|
'PhabricatorRedirectController' => 'PhabricatorController',
|
||||||
'PhabricatorRefreshCSRFController' => 'PhabricatorAuthController',
|
'PhabricatorRefreshCSRFController' => 'PhabricatorAuthController',
|
||||||
'PhabricatorRegistrationProfile' => 'Phobject',
|
'PhabricatorRegistrationProfile' => 'Phobject',
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
<?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 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>[^/]+)/)?'
|
||||||
|
=> 'PhabricatorBadgesListController',
|
||||||
|
'create/'
|
||||||
|
=> 'PhabricatorBadgesEditController',
|
||||||
|
'edit/(?:(?P<id>\d+)/)?'
|
||||||
|
=> 'PhabricatorBadgesEditController',
|
||||||
|
'view/(?:(?P<id>\d+)/)?'
|
||||||
|
=> 'PhabricatorBadgesViewController',
|
||||||
|
'icon/(?P<id>[1-9]\d*)/'
|
||||||
|
=> 'PhabricatorBadgesEditIconController',
|
||||||
|
'icon/'
|
||||||
|
=> 'PhabricatorBadgesEditIconController',
|
||||||
|
'recipients/(?P<id>[1-9]\d*)/'
|
||||||
|
=> 'PhabricatorBadgesEditRecipientsController',
|
||||||
|
'recipients/(?P<id>[1-9]\d*)/remove/'
|
||||||
|
=> 'PhabricatorBadgesRemoveRecipientsController',
|
||||||
|
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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.'),
|
||||||
|
'template' => PhabricatorBadgesPHIDType::TYPECONST,
|
||||||
|
),
|
||||||
|
PhabricatorBadgesDefaultViewCapability::CAPABILITY => array(
|
||||||
|
'caption' => pht('Default view policy for badges.'),
|
||||||
|
'template' => PhabricatorBadgesPHIDType::TYPECONST,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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.');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorBadgesDefaultEditCapability
|
||||||
|
extends PhabricatorPolicyCapability {
|
||||||
|
|
||||||
|
const CAPABILITY = 'badges.default.edit';
|
||||||
|
|
||||||
|
public function getCapabilityName() {
|
||||||
|
return pht('Default Edit Badges');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class PhabricatorBadgesController extends PhabricatorController {}
|
|
@ -0,0 +1,222 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorBadgesEditController
|
||||||
|
extends PhabricatorBadgesController {
|
||||||
|
|
||||||
|
public function handleRequest(AphrontRequest $request) {
|
||||||
|
$viewer = $request->getViewer();
|
||||||
|
$id = $request->getURIData('id');
|
||||||
|
|
||||||
|
if ($id) {
|
||||||
|
$badge = id(new PhabricatorBadgesQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withIDs(array($id))
|
||||||
|
->requireCapabilities(
|
||||||
|
array(
|
||||||
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT,
|
||||||
|
))
|
||||||
|
->executeOne();
|
||||||
|
if (!$badge) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
$is_new = false;
|
||||||
|
} else {
|
||||||
|
$badge = PhabricatorBadgesBadge::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/'.$id.'/');
|
||||||
|
}
|
||||||
|
|
||||||
|
$e_name = true;
|
||||||
|
$v_name = $badge->getName();
|
||||||
|
|
||||||
|
$v_icon = $badge->getIcon();
|
||||||
|
|
||||||
|
$v_flav = $badge->getFlavor();
|
||||||
|
$v_desc = $badge->getDescription();
|
||||||
|
$v_qual = $badge->getQuality();
|
||||||
|
$v_stat = $badge->getStatus();
|
||||||
|
|
||||||
|
$v_edit = $badge->getEditPolicy();
|
||||||
|
$v_view = $badge->getViewPolicy();
|
||||||
|
|
||||||
|
$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_uri = $this->getApplicationURI('view/'.$badge->getID().'/');
|
||||||
|
return id(new AphrontRedirectResponse())->setURI($return_uri);
|
||||||
|
|
||||||
|
} catch (PhabricatorApplicationTransactionValidationException $ex) {
|
||||||
|
$validation_exception = $ex;
|
||||||
|
|
||||||
|
$e_name = $ex->getShortMessage($type_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
->setValue($v_view)
|
||||||
|
->setPolicies($policies))
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormPolicyControl())
|
||||||
|
->setName('editPolicy')
|
||||||
|
->setPolicyObject($badge)
|
||||||
|
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
|
||||||
|
->setValue($v_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,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorBadgesEditIconController
|
||||||
|
extends PhabricatorBadgesController {
|
||||||
|
|
||||||
|
public function handleRequest(AphrontRequest $request) {
|
||||||
|
$viewer = $request->getViewer();
|
||||||
|
$id = $request->getURIData('id');
|
||||||
|
|
||||||
|
if ($id) {
|
||||||
|
$badge = id(new PhabricatorBadgesQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withIDs(array($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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,120 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorBadgesEditRecipientsController
|
||||||
|
extends PhabricatorBadgesController {
|
||||||
|
|
||||||
|
public function handleRequest(AphrontRequest $request) {
|
||||||
|
$viewer = $request->getViewer();
|
||||||
|
$id = $request->getURIData('id');
|
||||||
|
|
||||||
|
$badge = id(new PhabricatorBadgesQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withIDs(array($id))
|
||||||
|
->needRecipients(true)
|
||||||
|
->requireCapabilities(
|
||||||
|
array(
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT,
|
||||||
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
|
))
|
||||||
|
->executeOne();
|
||||||
|
if (!$badge) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
$recipient_phids = $badge->getRecipientPHIDs();
|
||||||
|
|
||||||
|
if ($request->isFormPost()) {
|
||||||
|
$recipient_spec = array();
|
||||||
|
|
||||||
|
$remove = $request->getStr('remove');
|
||||||
|
if ($remove) {
|
||||||
|
$recipient_spec['-'] = array_fuse(array($remove));
|
||||||
|
}
|
||||||
|
|
||||||
|
$add_recipients = $request->getArr('phids');
|
||||||
|
if ($add_recipients) {
|
||||||
|
$recipient_spec['+'] = array_fuse($add_recipients);
|
||||||
|
}
|
||||||
|
|
||||||
|
$type_recipient = PhabricatorBadgeHasRecipientEdgeType::EDGECONST;
|
||||||
|
|
||||||
|
$xactions = array();
|
||||||
|
|
||||||
|
$xactions[] = id(new PhabricatorBadgesTransaction())
|
||||||
|
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
|
||||||
|
->setMetadataValue('edge:type', $type_recipient)
|
||||||
|
->setNewValue($recipient_spec);
|
||||||
|
|
||||||
|
$editor = id(new PhabricatorBadgesEditor($badge))
|
||||||
|
->setActor($viewer)
|
||||||
|
->setContentSourceFromRequest($request)
|
||||||
|
->setContinueOnNoEffect(true)
|
||||||
|
->setContinueOnMissingFields(true)
|
||||||
|
->applyTransactions($badge, $xactions);
|
||||||
|
|
||||||
|
return id(new AphrontRedirectResponse())
|
||||||
|
->setURI($request->getRequestURI());
|
||||||
|
}
|
||||||
|
|
||||||
|
$recipient_phids = array_reverse($recipient_phids);
|
||||||
|
$handles = $this->loadViewerHandles($recipient_phids);
|
||||||
|
|
||||||
|
$state = array();
|
||||||
|
foreach ($handles as $handle) {
|
||||||
|
$state[] = array(
|
||||||
|
'phid' => $handle->getPHID(),
|
||||||
|
'name' => $handle->getFullName(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||||
|
$viewer,
|
||||||
|
$badge,
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT);
|
||||||
|
|
||||||
|
$form_box = null;
|
||||||
|
$title = pht('Add Recipient');
|
||||||
|
if ($can_edit) {
|
||||||
|
$header_name = pht('Edit Recipients');
|
||||||
|
$view_uri = $this->getApplicationURI('view/'.$badge->getID().'/');
|
||||||
|
|
||||||
|
$form = new AphrontFormView();
|
||||||
|
$form
|
||||||
|
->setUser($viewer)
|
||||||
|
->appendControl(
|
||||||
|
id(new AphrontFormTokenizerControl())
|
||||||
|
->setName('phids')
|
||||||
|
->setLabel(pht('Add Recipients'))
|
||||||
|
->setDatasource(new PhabricatorPeopleDatasource()))
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormSubmitControl())
|
||||||
|
->addCancelButton($view_uri)
|
||||||
|
->setValue(pht('Add Recipients')));
|
||||||
|
$form_box = id(new PHUIObjectBoxView())
|
||||||
|
->setHeaderText($title)
|
||||||
|
->setForm($form);
|
||||||
|
}
|
||||||
|
|
||||||
|
$recipient_list = id(new PhabricatorBadgesRecipientsListView())
|
||||||
|
->setBadge($badge)
|
||||||
|
->setHandles($handles)
|
||||||
|
->setUser($viewer);
|
||||||
|
|
||||||
|
$badge_url = $this->getApplicationURI('view/'.$id.'/');
|
||||||
|
|
||||||
|
$crumbs = $this->buildApplicationCrumbs();
|
||||||
|
$crumbs->addTextCrumb($badge->getName(), $badge_url);
|
||||||
|
$crumbs->addTextCrumb(pht('Recipients'));
|
||||||
|
|
||||||
|
return $this->buildApplicationPage(
|
||||||
|
array(
|
||||||
|
$crumbs,
|
||||||
|
$form_box,
|
||||||
|
$recipient_list,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'title' => $title,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorBadgesListController
|
||||||
|
extends PhabricatorBadgesController {
|
||||||
|
|
||||||
|
public function shouldAllowPublic() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleRequest(AphrontRequest $request) {
|
||||||
|
$query_key = $request->getURIData('queryKey');
|
||||||
|
$controller = id(new PhabricatorApplicationSearchController())
|
||||||
|
->setQueryKey($query_key)
|
||||||
|
->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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorBadgesRemoveRecipientsController
|
||||||
|
extends PhabricatorBadgesController {
|
||||||
|
|
||||||
|
public function handleRequest(AphrontRequest $request) {
|
||||||
|
$viewer = $request->getViewer();
|
||||||
|
$id = $request->getURIData('id');
|
||||||
|
|
||||||
|
$badge = id(new PhabricatorBadgesQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withIDs(array($id))
|
||||||
|
->needRecipients(true)
|
||||||
|
->requireCapabilities(
|
||||||
|
array(
|
||||||
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT,
|
||||||
|
))
|
||||||
|
->executeOne();
|
||||||
|
if (!$badge) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
$recipient_phids = $badge->getRecipientPHIDs();
|
||||||
|
$remove_phid = $request->getStr('phid');
|
||||||
|
|
||||||
|
if (!in_array($remove_phid, $recipient_phids)) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
$recipients_uri =
|
||||||
|
$this->getApplicationURI('recipients/'.$badge->getID().'/');
|
||||||
|
|
||||||
|
if ($request->isFormPost()) {
|
||||||
|
$recipient_spec = array();
|
||||||
|
$recipient_spec['-'] = array($remove_phid => $remove_phid);
|
||||||
|
|
||||||
|
$type_recipient = PhabricatorBadgeHasRecipientEdgeType::EDGECONST;
|
||||||
|
|
||||||
|
$xactions = array();
|
||||||
|
|
||||||
|
$xactions[] = id(new PhabricatorBadgesTransaction())
|
||||||
|
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
|
||||||
|
->setMetadataValue('edge:type', $type_recipient)
|
||||||
|
->setNewValue($recipient_spec);
|
||||||
|
|
||||||
|
$editor = id(new PhabricatorBadgesEditor($badge))
|
||||||
|
->setActor($viewer)
|
||||||
|
->setContentSourceFromRequest($request)
|
||||||
|
->setContinueOnNoEffect(true)
|
||||||
|
->setContinueOnMissingFields(true)
|
||||||
|
->applyTransactions($badge, $xactions);
|
||||||
|
|
||||||
|
return id(new AphrontRedirectResponse())
|
||||||
|
->setURI($recipients_uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
$handle = id(new PhabricatorHandleQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withPHIDs(array($remove_phid))
|
||||||
|
->executeOne();
|
||||||
|
|
||||||
|
$dialog = id(new AphrontDialogView())
|
||||||
|
->setUser($viewer)
|
||||||
|
->setTitle(pht('Really Revoke Badge?'))
|
||||||
|
->appendParagraph(
|
||||||
|
pht(
|
||||||
|
'Really revoke the badge "%s" from %s?',
|
||||||
|
phutil_tag('strong', array(), $badge->getName()),
|
||||||
|
phutil_tag('strong', array(), $handle->getName())))
|
||||||
|
->addCancelButton($recipients_uri)
|
||||||
|
->addSubmitButton(pht('Revoke Badge'));
|
||||||
|
|
||||||
|
return $dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,157 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorBadgesViewController
|
||||||
|
extends PhabricatorBadgesController {
|
||||||
|
|
||||||
|
public function shouldAllowPublic() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleRequest(AphrontRequest $request) {
|
||||||
|
$viewer = $request->getViewer();
|
||||||
|
$id = $request->getURIData('id');
|
||||||
|
|
||||||
|
$badge = id(new PhabricatorBadgesQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withIDs(array($id))
|
||||||
|
->needRecipients(true)
|
||||||
|
->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(
|
||||||
|
PhabricatorBadgesBadge::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);
|
||||||
|
|
||||||
|
$recipient_phids = $badge->getRecipientPHIDs();
|
||||||
|
$recipient_phids = array_reverse($recipient_phids);
|
||||||
|
$handles = $this->loadViewerHandles($recipient_phids);
|
||||||
|
|
||||||
|
$recipient_list = id(new PhabricatorBadgesRecipientsListView())
|
||||||
|
->setBadge($badge)
|
||||||
|
->setHandles($handles)
|
||||||
|
->setUser($viewer);
|
||||||
|
|
||||||
|
return $this->buildApplicationPage(
|
||||||
|
array(
|
||||||
|
$crumbs,
|
||||||
|
$box,
|
||||||
|
$recipient_list,
|
||||||
|
$timeline,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'title' => $title,
|
||||||
|
'pageObjects' => array($badge->getPHID()),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildPropertyListView(PhabricatorBadgesBadge $badge) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$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(PhabricatorBadgesBadge $badge) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
$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('Manage Recipients')
|
||||||
|
->setIcon('fa-users')
|
||||||
|
->setDisabled(!$can_edit)
|
||||||
|
->setHref($this->getApplicationURI("/recipients/{$id}/")));
|
||||||
|
|
||||||
|
return $view;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorBadgeHasRecipientEdgeType
|
||||||
|
extends PhabricatorEdgeType {
|
||||||
|
|
||||||
|
const EDGECONST = 59;
|
||||||
|
|
||||||
|
public function getInverseEdgeConstant() {
|
||||||
|
return PhabricatorRecipientHasBadgeEdgeType::EDGECONST;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function shouldWriteInverseTransactions() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTransactionAddString(
|
||||||
|
$actor,
|
||||||
|
$add_count,
|
||||||
|
$add_edges) {
|
||||||
|
|
||||||
|
return pht(
|
||||||
|
'%s awarded %s recipients(s): %s.',
|
||||||
|
$actor,
|
||||||
|
$add_count,
|
||||||
|
$add_edges);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTransactionRemoveString(
|
||||||
|
$actor,
|
||||||
|
$rem_count,
|
||||||
|
$rem_edges) {
|
||||||
|
|
||||||
|
return pht(
|
||||||
|
'%s revoked %s recipients(s): %s.',
|
||||||
|
$actor,
|
||||||
|
$rem_count,
|
||||||
|
$rem_edges);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTransactionEditString(
|
||||||
|
$actor,
|
||||||
|
$total_count,
|
||||||
|
$add_count,
|
||||||
|
$add_edges,
|
||||||
|
$rem_count,
|
||||||
|
$rem_edges) {
|
||||||
|
|
||||||
|
return pht(
|
||||||
|
'%s edited recipient(s), awarded %s: %s; revoked %s: %s.',
|
||||||
|
$actor,
|
||||||
|
$add_count,
|
||||||
|
$add_edges,
|
||||||
|
$rem_count,
|
||||||
|
$rem_edges);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFeedAddString(
|
||||||
|
$actor,
|
||||||
|
$object,
|
||||||
|
$add_count,
|
||||||
|
$add_edges) {
|
||||||
|
|
||||||
|
return pht(
|
||||||
|
'%s awarded %s recipient(s) for %s: %s.',
|
||||||
|
$actor,
|
||||||
|
$add_count,
|
||||||
|
$object,
|
||||||
|
$add_edges);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFeedRemoveString(
|
||||||
|
$actor,
|
||||||
|
$object,
|
||||||
|
$rem_count,
|
||||||
|
$rem_edges) {
|
||||||
|
|
||||||
|
return pht(
|
||||||
|
'%s revoked %s recipient(s) for %s: %s.',
|
||||||
|
$actor,
|
||||||
|
$rem_count,
|
||||||
|
$object,
|
||||||
|
$rem_edges);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFeedEditString(
|
||||||
|
$actor,
|
||||||
|
$object,
|
||||||
|
$total_count,
|
||||||
|
$add_count,
|
||||||
|
$add_edges,
|
||||||
|
$rem_count,
|
||||||
|
$rem_edges) {
|
||||||
|
|
||||||
|
return pht(
|
||||||
|
'%s edited recipient(s) for %s, awarded %s: %s; revoked %s: %s.',
|
||||||
|
$actor,
|
||||||
|
$object,
|
||||||
|
$add_count,
|
||||||
|
$add_edges,
|
||||||
|
$rem_count,
|
||||||
|
$rem_edges);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorRecipientHasBadgeEdgeType
|
||||||
|
extends PhabricatorEdgeType {
|
||||||
|
|
||||||
|
const EDGECONST = 58;
|
||||||
|
|
||||||
|
public function getInverseEdgeConstant() {
|
||||||
|
return PhabricatorBadgeHasRecipientEdgeType::EDGECONST;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function shouldWriteInverseTransactions() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTransactionAddString(
|
||||||
|
$actor,
|
||||||
|
$add_count,
|
||||||
|
$add_edges) {
|
||||||
|
|
||||||
|
return pht(
|
||||||
|
'%s added %s badge(s): %s.',
|
||||||
|
$actor,
|
||||||
|
$add_count,
|
||||||
|
$add_edges);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTransactionRemoveString(
|
||||||
|
$actor,
|
||||||
|
$rem_count,
|
||||||
|
$rem_edges) {
|
||||||
|
|
||||||
|
return pht(
|
||||||
|
'%s revoked %s badge(s): %s.',
|
||||||
|
$actor,
|
||||||
|
$rem_count,
|
||||||
|
$rem_edges);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTransactionEditString(
|
||||||
|
$actor,
|
||||||
|
$total_count,
|
||||||
|
$add_count,
|
||||||
|
$add_edges,
|
||||||
|
$rem_count,
|
||||||
|
$rem_edges) {
|
||||||
|
|
||||||
|
return pht(
|
||||||
|
'%s edited badge(s), added %s: %s; revoked %s: %s.',
|
||||||
|
$actor,
|
||||||
|
$add_count,
|
||||||
|
$add_edges,
|
||||||
|
$rem_count,
|
||||||
|
$rem_edges);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFeedAddString(
|
||||||
|
$actor,
|
||||||
|
$object,
|
||||||
|
$add_count,
|
||||||
|
$add_edges) {
|
||||||
|
|
||||||
|
return pht(
|
||||||
|
'%s added %s badge(s) for %s: %s.',
|
||||||
|
$actor,
|
||||||
|
$add_count,
|
||||||
|
$object,
|
||||||
|
$add_edges);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFeedRemoveString(
|
||||||
|
$actor,
|
||||||
|
$object,
|
||||||
|
$rem_count,
|
||||||
|
$rem_edges) {
|
||||||
|
|
||||||
|
return pht(
|
||||||
|
'%s revoked %s badge(s) for %s: %s.',
|
||||||
|
$actor,
|
||||||
|
$rem_count,
|
||||||
|
$object,
|
||||||
|
$rem_edges);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFeedEditString(
|
||||||
|
$actor,
|
||||||
|
$object,
|
||||||
|
$total_count,
|
||||||
|
$add_count,
|
||||||
|
$add_edges,
|
||||||
|
$rem_count,
|
||||||
|
$rem_edges) {
|
||||||
|
|
||||||
|
return pht(
|
||||||
|
'%s edited badge(s) for %s, added %s: %s; revoked %s: %s.',
|
||||||
|
$actor,
|
||||||
|
$object,
|
||||||
|
$add_count,
|
||||||
|
$add_edges,
|
||||||
|
$rem_count,
|
||||||
|
$rem_edges);
|
||||||
|
}
|
||||||
|
}
|
194
src/applications/badges/editor/PhabricatorBadgesEditor.php
Normal file
194
src/applications/badges/editor/PhabricatorBadgesEditor.php
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
<?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_EDGE;
|
||||||
|
$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;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function shouldPublishFeedStory(
|
||||||
|
PhabricatorLiskDAO $object,
|
||||||
|
array $xactions) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildReplyHandler(PhabricatorLiskDAO $object) {
|
||||||
|
return id(new PhabricatorMacroReplyHandler())
|
||||||
|
->setMailReceiver($object);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildMailTemplate(PhabricatorLiskDAO $object) {
|
||||||
|
$name = $object->getName();
|
||||||
|
$id = $object->getID();
|
||||||
|
$name = pht('Badge %d', $id);
|
||||||
|
return id(new PhabricatorMetaMTAMail())
|
||||||
|
->setSubject($name)
|
||||||
|
->addHeader('Thread-Topic', $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getMailTo(PhabricatorLiskDAO $object) {
|
||||||
|
return array(
|
||||||
|
$this->requireActor()->getPHID(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildMailBody(
|
||||||
|
PhabricatorLiskDAO $object,
|
||||||
|
array $xactions) {
|
||||||
|
|
||||||
|
$description = $object->getDescription();
|
||||||
|
$body = parent::buildMailBody($object, $xactions);
|
||||||
|
|
||||||
|
if (strlen($description)) {
|
||||||
|
$body->addTextSection(
|
||||||
|
pht('BADGE DESCRIPTION'),
|
||||||
|
$object->getDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
$body->addLinkSection(
|
||||||
|
pht('BADGE DETAIL'),
|
||||||
|
PhabricatorEnv::getProductionURI('/badge/view/'.$object->getID().'/'));
|
||||||
|
return $body;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getMailSubjectPrefix() {
|
||||||
|
return pht('[Badge]');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
53
src/applications/badges/icon/PhabricatorBadgesIcon.php
Normal file
53
src/applications/badges/icon/PhabricatorBadgesIcon.php
Normal file
|
@ -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')),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
47
src/applications/badges/phid/PhabricatorBadgesPHIDType.php
Normal file
47
src/applications/badges/phid/PhabricatorBadgesPHIDType.php
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorBadgesPHIDType extends PhabricatorPHIDType {
|
||||||
|
|
||||||
|
const TYPECONST = 'BDGE';
|
||||||
|
|
||||||
|
public function getTypeName() {
|
||||||
|
return pht('Badge');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newObject() {
|
||||||
|
return new PhabricatorBadgesBadge();
|
||||||
|
}
|
||||||
|
|
||||||
|
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->setURI("/badges/view/{$id}/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
113
src/applications/badges/query/PhabricatorBadgesQuery.php
Normal file
113
src/applications/badges/query/PhabricatorBadgesQuery.php
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorBadgesQuery
|
||||||
|
extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
|
|
||||||
|
private $ids;
|
||||||
|
private $phids;
|
||||||
|
private $qualities;
|
||||||
|
private $statuses;
|
||||||
|
private $recipientPHIDs;
|
||||||
|
|
||||||
|
private $needRecipients;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withRecipientPHIDs(array $recipient_phids) {
|
||||||
|
$this->recipientPHIDs = $recipient_phids;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function needRecipients($need_recipients) {
|
||||||
|
$this->needRecipients = $need_recipients;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function loadPage() {
|
||||||
|
return $this->loadStandardPage($this->newResultObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newResultObject() {
|
||||||
|
return new PhabricatorBadgesBadge();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function didFilterPage(array $badges) {
|
||||||
|
|
||||||
|
if ($this->needRecipients) {
|
||||||
|
$edge_query = id(new PhabricatorEdgeQuery())
|
||||||
|
->withSourcePHIDs(mpull($badges, 'getPHID'))
|
||||||
|
->withEdgeTypes(
|
||||||
|
array(
|
||||||
|
PhabricatorBadgeHasRecipientEdgeType::EDGECONST,
|
||||||
|
));
|
||||||
|
$edge_query->execute();
|
||||||
|
|
||||||
|
foreach ($badges as $badge) {
|
||||||
|
$phids = $edge_query->getDestinationPHIDs(
|
||||||
|
array(
|
||||||
|
$badge->getPHID(),
|
||||||
|
));
|
||||||
|
$badge->attachRecipientPHIDs($phids);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $badges;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
|
||||||
|
$where = parent::buildWhereClauseParts($conn);
|
||||||
|
|
||||||
|
if ($this->ids !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'id IN (%Ld)',
|
||||||
|
$this->ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->phids !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'phid IN (%Ls)',
|
||||||
|
$this->phids);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->qualities !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'quality IN (%Ls)',
|
||||||
|
$this->qualities);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->statuses !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'status IN (%Ls)',
|
||||||
|
$this->statuses);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $where;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQueryApplicationClass() {
|
||||||
|
return 'PhabricatorBadgesApplication';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
143
src/applications/badges/query/PhabricatorBadgesSearchEngine.php
Normal file
143
src/applications/badges/query/PhabricatorBadgesSearchEngine.php
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
<?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'));
|
||||||
|
|
||||||
|
$saved->setParameter(
|
||||||
|
'qualities',
|
||||||
|
$this->readListFromRequest($request, 'qualities'));
|
||||||
|
|
||||||
|
return $saved;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildCustomSearchFields() {
|
||||||
|
return array(
|
||||||
|
id(new PhabricatorSearchCheckboxesField())
|
||||||
|
->setKey('qualities')
|
||||||
|
->setLabel(pht('Quality'))
|
||||||
|
->setOptions(
|
||||||
|
id(new PhabricatorBadgesBadge())
|
||||||
|
->getQualityNameMap()),
|
||||||
|
id(new PhabricatorSearchCheckboxesField())
|
||||||
|
->setKey('statuses')
|
||||||
|
->setLabel(pht('Status'))
|
||||||
|
->setOptions(
|
||||||
|
id(new PhabricatorBadgesBadge())
|
||||||
|
->getStatusNameMap()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildQueryFromParameters(array $map) {
|
||||||
|
$query = $this->newQuery();
|
||||||
|
|
||||||
|
if ($map['statuses']) {
|
||||||
|
$query->withStatuses($map['statuses']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($map['qualities']) {
|
||||||
|
$query->withQualities($map['qualities']);
|
||||||
|
}
|
||||||
|
|
||||||
|
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(
|
||||||
|
PhabricatorBadgesBadge::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, 'PhabricatorBadgesBadge');
|
||||||
|
|
||||||
|
$viewer = $this->requireViewer();
|
||||||
|
|
||||||
|
$list = id(new PHUIObjectItemListView());
|
||||||
|
foreach ($badges as $badge) {
|
||||||
|
|
||||||
|
$quality = idx($badge->getQualityNameMap(), $badge->getQuality());
|
||||||
|
$mini_badge = id(new PHUIBadgeMiniView())
|
||||||
|
->setHeader($badge->getName())
|
||||||
|
->setIcon($badge->getIcon())
|
||||||
|
->setQuality($badge->getQuality());
|
||||||
|
|
||||||
|
$item = id(new PHUIObjectItemView())
|
||||||
|
->setHeader($badge->getName())
|
||||||
|
->setBadge($mini_badge)
|
||||||
|
->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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorBadgesTransactionQuery
|
||||||
|
extends PhabricatorApplicationTransactionQuery {
|
||||||
|
|
||||||
|
public function getTemplateApplicationTransaction() {
|
||||||
|
return new PhabricatorBadgesTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
197
src/applications/badges/storage/PhabricatorBadgesBadge.php
Normal file
197
src/applications/badges/storage/PhabricatorBadgesBadge.php
Normal file
|
@ -0,0 +1,197 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorBadgesBadge extends PhabricatorBadgesDAO
|
||||||
|
implements
|
||||||
|
PhabricatorPolicyInterface,
|
||||||
|
PhabricatorApplicationTransactionInterface,
|
||||||
|
PhabricatorSubscribableInterface,
|
||||||
|
PhabricatorFlaggableInterface,
|
||||||
|
PhabricatorDestructibleInterface {
|
||||||
|
|
||||||
|
protected $name;
|
||||||
|
protected $flavor;
|
||||||
|
protected $description;
|
||||||
|
protected $icon;
|
||||||
|
protected $quality;
|
||||||
|
protected $viewPolicy;
|
||||||
|
protected $editPolicy;
|
||||||
|
protected $status;
|
||||||
|
protected $creatorPHID;
|
||||||
|
|
||||||
|
private $recipientPHIDs = self::ATTACHABLE;
|
||||||
|
|
||||||
|
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 PhabricatorBadgesBadge())
|
||||||
|
->setIcon(self::DEFAULT_ICON)
|
||||||
|
->setQuality(self::DEFAULT_QUALITY)
|
||||||
|
->setCreatorPHID($actor->getPHID())
|
||||||
|
->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',
|
||||||
|
),
|
||||||
|
self::CONFIG_KEY_SCHEMA => array(
|
||||||
|
'key_creator' => array(
|
||||||
|
'columns' => array('creatorPHID', 'dateModified'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
) + parent::getConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generatePHID() {
|
||||||
|
return
|
||||||
|
PhabricatorPHID::generateNewPHID(PhabricatorBadgesPHIDType::TYPECONST);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isClosed() {
|
||||||
|
return ($this->getStatus() == self::STATUS_CLOSED);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function attachRecipientPHIDs(array $phids) {
|
||||||
|
$this->recipientPHIDs = $phids;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRecipientPHIDs() {
|
||||||
|
return $this->assertAttached($this->recipientPHIDs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( 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 ($this->creatorPHID == $phid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function shouldShowSubscribersProperty() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function shouldAllowSubscription($phid) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( PhabricatorDestructibleInterface )----------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
public function destroyObjectPermanently(
|
||||||
|
PhabricatorDestructionEngine $engine) {
|
||||||
|
|
||||||
|
$this->openTransaction();
|
||||||
|
$this->delete();
|
||||||
|
$this->saveTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
9
src/applications/badges/storage/PhabricatorBadgesDAO.php
Normal file
9
src/applications/badges/storage/PhabricatorBadgesDAO.php
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class PhabricatorBadgesDAO extends PhabricatorLiskDAO {
|
||||||
|
|
||||||
|
public function getApplicationName() {
|
||||||
|
return 'badges';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorBadgesSchemaSpec
|
||||||
|
extends PhabricatorConfigSchemaSpec {
|
||||||
|
|
||||||
|
public function buildSchemata() {
|
||||||
|
$this->buildEdgeSchemata(new PhabricatorBadgesBadge());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
195
src/applications/badges/storage/PhabricatorBadgesTransaction.php
Normal file
195
src/applications/badges/storage/PhabricatorBadgesTransaction.php
Normal file
|
@ -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 = PhabricatorBadgesBadge::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 = PhabricatorBadgesBadge::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 PhabricatorBadgesBadge::STATUS_OPEN:
|
||||||
|
return pht(
|
||||||
|
'%s activated %s.',
|
||||||
|
$this->renderHandleLink($author_phid),
|
||||||
|
$this->renderHandleLink($object_phid));
|
||||||
|
case PhabricatorBadgesBadge::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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorBadgesRecipientsListView extends AphrontTagView {
|
||||||
|
|
||||||
|
private $badge;
|
||||||
|
private $handles;
|
||||||
|
|
||||||
|
public function setBadge(PhabricatorBadgesBadge $badge) {
|
||||||
|
$this->badge = $badge;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setHandles(array $handles) {
|
||||||
|
$this->handles = $handles;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getTagContent() {
|
||||||
|
|
||||||
|
$viewer = $this->user;
|
||||||
|
|
||||||
|
$badge = $this->badge;
|
||||||
|
$handles = $this->handles;
|
||||||
|
|
||||||
|
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||||
|
$viewer,
|
||||||
|
$badge,
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT);
|
||||||
|
|
||||||
|
$list = id(new PHUIObjectItemListView())
|
||||||
|
->setNoDataString(pht('This badge does not have any recipients.'));
|
||||||
|
|
||||||
|
foreach ($handles as $handle) {
|
||||||
|
$remove_uri = '/badges/recipients/'.
|
||||||
|
$badge->getID().'/remove/?phid='.$handle->getPHID();
|
||||||
|
|
||||||
|
$item = id(new PHUIObjectItemView())
|
||||||
|
->setHeader($handle->getFullName())
|
||||||
|
->setHref($handle->getURI())
|
||||||
|
->setImageURI($handle->getImageURI());
|
||||||
|
|
||||||
|
if ($can_edit) {
|
||||||
|
$item->addAction(
|
||||||
|
id(new PHUIListItemView())
|
||||||
|
->setIcon('fa-times')
|
||||||
|
->setName(pht('Remove'))
|
||||||
|
->setHref($remove_uri)
|
||||||
|
->setWorkflow(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
$list->addItem($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
$box = id(new PHUIObjectBoxView())
|
||||||
|
->setHeaderText(pht('Recipients'))
|
||||||
|
->setObjectList($list);
|
||||||
|
|
||||||
|
return $box;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -11,7 +11,7 @@ final class PhabricatorTokensApplication extends PhabricatorApplication {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFontIcon() {
|
public function getFontIcon() {
|
||||||
return 'fa-trophy';
|
return 'fa-thumbs-up';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTitleGlyph() {
|
public function getTitleGlyph() {
|
||||||
|
|
|
@ -89,7 +89,7 @@ final class PHUIBadgeExample extends PhabricatorUIExample {
|
||||||
$badges2[] = id(new PHUIBadgeView())
|
$badges2[] = id(new PHUIBadgeView())
|
||||||
->setIcon('fa-users')
|
->setIcon('fa-users')
|
||||||
->setHeader(pht('Security Team'))
|
->setHeader(pht('Security Team'))
|
||||||
->setSubhead(pht('<sripts>alert(1);</script>'))
|
->setSubhead(pht('<script>alert(1);</script>'))
|
||||||
->setQuality(PHUIBadgeView::EPIC)
|
->setQuality(PHUIBadgeView::EPIC)
|
||||||
->setSource('Projects (automatic)')
|
->setSource('Projects (automatic)')
|
||||||
->addByline(pht('Dec 31, 1969'))
|
->addByline(pht('Dec 31, 1969'))
|
||||||
|
@ -152,7 +152,7 @@ final class PHUIBadgeExample extends PhabricatorUIExample {
|
||||||
$flex2->addItems($badges2);
|
$flex2->addItems($badges2);
|
||||||
|
|
||||||
$box2 = id(new PHUIObjectBoxView())
|
$box2 = id(new PHUIObjectBoxView())
|
||||||
->setHeaderText(pht('Acheivements'))
|
->setHeaderText(pht('Achievements'))
|
||||||
->appendChild($flex2);
|
->appendChild($flex2);
|
||||||
|
|
||||||
$flex3 = new PHUIBadgeBoxView();
|
$flex3 = new PHUIBadgeBoxView();
|
||||||
|
|
|
@ -1182,6 +1182,80 @@ final class PhabricatorUSEnglishTranslation
|
||||||
'%s Task',
|
'%s Task',
|
||||||
'%s Tasks',
|
'%s Tasks',
|
||||||
),
|
),
|
||||||
|
'%s added %s badge(s) for %s: %s.' => array(
|
||||||
|
array(
|
||||||
|
'%s added a badge for %s: %3$s.',
|
||||||
|
'%s added badges for %s: %3$s.',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'%s added %s badge(s): %s.' => array(
|
||||||
|
array(
|
||||||
|
'%s added a badge: %3$s.',
|
||||||
|
'%s added badges: %3$s.',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'%s awarded %s recipient(s) for %s: %s.' => array(
|
||||||
|
array(
|
||||||
|
'%s awarded %3$s to %4$s.',
|
||||||
|
'%s awarded %3$s to multiple recipients: %4$s.',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'%s awarded %s recipients(s): %s.' => array(
|
||||||
|
array(
|
||||||
|
'%s awarded a recipient: %3$s.',
|
||||||
|
'%s awarded multiple recipients: %3$s.',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'%s edited badge(s) for %s, added %s: %s; revoked %s: %s.' => array(
|
||||||
|
array(
|
||||||
|
'%s edited badges for %s, added %s: %s; revoked %s: %s.',
|
||||||
|
'%s edited badges for %s, added %s: %s; revoked %s: %s.',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'%s edited badge(s), added %s: %s; revoked %s: %s.' => array(
|
||||||
|
array(
|
||||||
|
'%s edited badges, added %s: %s; revoked %s: %s.',
|
||||||
|
'%s edited badges, added %s: %s; revoked %s: %s.',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'%s edited recipient(s) for %s, awarded %s: %s; revoked %s: %s.' => array(
|
||||||
|
array(
|
||||||
|
'%s edited recipients for %s, awarded %s: %s; revoked %s: %s.',
|
||||||
|
'%s edited recipients for %s, awarded %s: %s; revoked %s: %s.',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'%s edited recipient(s), awarded %s: %s; revoked %s: %s.' => array(
|
||||||
|
array(
|
||||||
|
'%s edited recipients, awarded %s: %s; revoked %s: %s.',
|
||||||
|
'%s edited recipients, awarded %s: %s; revoked %s: %s.',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'%s revoked %s badge(s) for %s: %s.' => array(
|
||||||
|
array(
|
||||||
|
'%s revoked a badge for %3$s: %4$s.',
|
||||||
|
'%s revoked multiple badges for %3$s: %4$s.',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'%s revoked %s badge(s): %s.' => array(
|
||||||
|
array(
|
||||||
|
'%s revoked a badge: %3$s.',
|
||||||
|
'%s revoked multiple badges: %3$s.',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'%s revoked %s recipient(s) for %s: %s.' => array(
|
||||||
|
array(
|
||||||
|
'%s revoked %3$s from %4$s.',
|
||||||
|
'%s revoked multiple recipients for %3$s: %4$s.',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
'%s revoked %s recipients(s): %s.' => array(
|
||||||
|
array(
|
||||||
|
'%s revoked a recipient: %3$s.',
|
||||||
|
'%s revoked multiple recipients: %3$s.',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -106,6 +106,7 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
|
||||||
'db.almanac' => array(),
|
'db.almanac' => array(),
|
||||||
'db.multimeter' => array(),
|
'db.multimeter' => array(),
|
||||||
'db.spaces' => array(),
|
'db.spaces' => array(),
|
||||||
|
'db.badges' => array(),
|
||||||
'0000.legacy.sql' => array(
|
'0000.legacy.sql' => array(
|
||||||
'legacy' => 0,
|
'legacy' => 0,
|
||||||
),
|
),
|
||||||
|
|
|
@ -23,6 +23,7 @@ final class PHUIObjectItemView extends AphrontTagView {
|
||||||
private $fontIcon;
|
private $fontIcon;
|
||||||
private $imageIcon;
|
private $imageIcon;
|
||||||
private $titleText;
|
private $titleText;
|
||||||
|
private $badge;
|
||||||
|
|
||||||
const AGE_FRESH = 'fresh';
|
const AGE_FRESH = 'fresh';
|
||||||
const AGE_STALE = 'stale';
|
const AGE_STALE = 'stale';
|
||||||
|
@ -99,6 +100,11 @@ final class PHUIObjectItemView extends AphrontTagView {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setBadge(PHUIBadgeMiniView $badge) {
|
||||||
|
$this->badge = $badge;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function setTitleText($title_text) {
|
public function setTitleText($title_text) {
|
||||||
$this->titleText = $title_text;
|
$this->titleText = $title_text;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -588,6 +594,15 @@ final class PHUIObjectItemView extends AphrontTagView {
|
||||||
$status);
|
$status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->badge) {
|
||||||
|
$column0 = phutil_tag(
|
||||||
|
'div',
|
||||||
|
array(
|
||||||
|
'class' => 'phui-object-item-col0 phui-object-item-badge',
|
||||||
|
),
|
||||||
|
$this->badge);
|
||||||
|
}
|
||||||
|
|
||||||
$column1 = phutil_tag(
|
$column1 = phutil_tag(
|
||||||
'div',
|
'div',
|
||||||
array(
|
array(
|
||||||
|
|
|
@ -617,6 +617,17 @@ ul.phui-object-item-list-view .phui-object-item-selected
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* - Badges ---------------------------------------------------------------- */
|
||||||
|
|
||||||
|
.phui-object-item-col0.phui-object-item-badge {
|
||||||
|
width: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phui-object-item-col0.phui-object-item-badge .phui-icon-view {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* - Dashboards ------------------------------------------------------------ */
|
/* - Dashboards ------------------------------------------------------------ */
|
||||||
|
|
||||||
.phui-object-box .phui-object-item-list-view .phui-object-item-frame {
|
.phui-object-box .phui-object-item-list-view .phui-object-item-frame {
|
||||||
|
|
|
@ -98,6 +98,12 @@
|
||||||
border-width: 1px 0 0;
|
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 {
|
.phui-property-list-section-noninitial .phui-property-list-section-header {
|
||||||
border-top: none;
|
border-top: none;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue