mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-25 16:22:43 +01:00
Allow pastes to be flagged
Summary: This does a few things: - Allows you to flag pastes. This is straightforward. - Allows Applications to register event listeners. - Makes object action lists emit a 'didrenderactions' event, so other applications can add more actions. The Flags application injects its action in this way. This should generally make it much easier to add actions to objects when we add new applications, with less code duplication and better modularity. We have a really hacky version of this in Differential that I want to get rid of in lieu of this more general approach. I'm going to make object lists do the same thing, so any application can jump in and add stuff. Test Plan: Flagged and unflagged pastes. Viewed home page, differential, flags list. Reviewers: vrana, btrahan Reviewed By: btrahan CC: aran Differential Revision: https://secure.phabricator.com/D3377
This commit is contained in:
parent
36e71a0601
commit
85bf88e400
21 changed files with 209 additions and 18 deletions
|
@ -162,6 +162,15 @@ $action_map = array(
|
|||
'file' => 'icon/page_white_text.png',
|
||||
'fork' => 'icon/arrow_branch.png',
|
||||
'edit' => 'icon/page_white_edit.png',
|
||||
'flag-0' => 'icon/flag-0.png',
|
||||
'flag-1' => 'icon/flag-1.png',
|
||||
'flag-2' => 'icon/flag-2.png',
|
||||
'flag-3' => 'icon/flag-3.png',
|
||||
'flag-4' => 'icon/flag-4.png',
|
||||
'flag-5' => 'icon/flag-5.png',
|
||||
'flag-6' => 'icon/flag-6.png',
|
||||
'flag-7' => 'icon/flag-7.png',
|
||||
'flag-ghost' => 'icon/flag-ghost.png',
|
||||
);
|
||||
|
||||
foreach ($action_map as $icon => $source) {
|
||||
|
|
|
@ -65,8 +65,8 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'/rsrc/image/autosprite.png' =>
|
||||
array(
|
||||
'hash' => '4a85e6c178f7dbdf1dc4f59f47a5e56b',
|
||||
'uri' => '/res/4a85e6c1/rsrc/image/autosprite.png',
|
||||
'hash' => 'bd70ca6308d6f80a87a10068a04867f8',
|
||||
'uri' => '/res/bd70ca63/rsrc/image/autosprite.png',
|
||||
'disk' => '/rsrc/image/autosprite.png',
|
||||
'type' => 'png',
|
||||
),
|
||||
|
@ -671,7 +671,7 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'autosprite-css' =>
|
||||
array(
|
||||
'uri' => '/res/9ed6c0e6/rsrc/css/autosprite.css',
|
||||
'uri' => '/res/114f6e40/rsrc/css/autosprite.css',
|
||||
'type' => 'css',
|
||||
'requires' =>
|
||||
array(
|
||||
|
@ -2265,7 +2265,7 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'phabricator-action-list-view-css' =>
|
||||
array(
|
||||
'uri' => '/res/f70fbcd4/rsrc/css/layout/phabricator-action-list-view.css',
|
||||
'uri' => '/res/1b4eef71/rsrc/css/layout/phabricator-action-list-view.css',
|
||||
'type' => 'css',
|
||||
'requires' =>
|
||||
array(
|
||||
|
@ -2603,7 +2603,7 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'phabricator-property-list-view-css' =>
|
||||
array(
|
||||
'uri' => '/res/598fccad/rsrc/css/layout/phabricator-property-list-view.css',
|
||||
'uri' => '/res/ff5d093d/rsrc/css/layout/phabricator-property-list-view.css',
|
||||
'type' => 'css',
|
||||
'requires' =>
|
||||
array(
|
||||
|
|
|
@ -743,6 +743,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFlagListController' => 'applications/flag/controller/PhabricatorFlagListController.php',
|
||||
'PhabricatorFlagListView' => 'applications/flag/view/PhabricatorFlagListView.php',
|
||||
'PhabricatorFlagQuery' => 'applications/flag/query/PhabricatorFlagQuery.php',
|
||||
'PhabricatorFlagsUIEventListener' => 'applications/flag/events/PhabricatorFlagsUIEventListener.php',
|
||||
'PhabricatorFormExample' => 'applications/uiexample/examples/PhabricatorFormExample.php',
|
||||
'PhabricatorGarbageCollectorDaemon' => 'infrastructure/daemon/PhabricatorGarbageCollectorDaemon.php',
|
||||
'PhabricatorGitGraphStream' => 'applications/repository/daemon/PhabricatorGitGraphStream.php',
|
||||
|
@ -1864,6 +1865,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFlagEditController' => 'PhabricatorFlagController',
|
||||
'PhabricatorFlagListController' => 'PhabricatorFlagController',
|
||||
'PhabricatorFlagListView' => 'AphrontView',
|
||||
'PhabricatorFlagsUIEventListener' => 'PhutilEventListener',
|
||||
'PhabricatorFormExample' => 'PhabricatorUIExample',
|
||||
'PhabricatorGarbageCollectorDaemon' => 'PhabricatorDaemon',
|
||||
'PhabricatorGlobalLock' => 'PhutilLock',
|
||||
|
|
|
@ -89,6 +89,10 @@ abstract class PhabricatorApplication {
|
|||
return null;
|
||||
}
|
||||
|
||||
public function getEventListeners() {
|
||||
return array();
|
||||
}
|
||||
|
||||
|
||||
/* -( URI Routing )-------------------------------------------------------- */
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ final class ConduitAPI_flag_query_Method extends ConduitAPI_flag_Method {
|
|||
protected function execute(ConduitAPIRequest $request) {
|
||||
|
||||
$query = new PhabricatorFlagQuery();
|
||||
$query->setViewer($request->getUser());
|
||||
|
||||
$owner_phids = $request->getValue('ownerPHIDs', array());
|
||||
if ($owner_phids) {
|
||||
|
|
|
@ -206,6 +206,7 @@ final class PhabricatorDirectoryMainController
|
|||
$user = $this->getRequest()->getUser();
|
||||
|
||||
$flag_query = id(new PhabricatorFlagQuery())
|
||||
->setViewer($user)
|
||||
->withOwnerPHIDs(array($user->getPHID()))
|
||||
->needHandles(true)
|
||||
->setLimit(10);
|
||||
|
|
|
@ -30,6 +30,12 @@ final class PhabricatorApplicationFlags extends PhabricatorApplication {
|
|||
return 'flags';
|
||||
}
|
||||
|
||||
public function getEventListeners() {
|
||||
return array(
|
||||
new PhabricatorFlagsUIEventListener(),
|
||||
);
|
||||
}
|
||||
|
||||
public function loadStatus(PhabricatorUser $user) {
|
||||
$status = array();
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ final class PhabricatorFlagEditController extends PhabricatorFlagController {
|
|||
$user = $request->getUser();
|
||||
|
||||
$phid = $this->phid;
|
||||
$handle = PhabricatorObjectHandleData::loadOneHandle($phid);
|
||||
$handle = PhabricatorObjectHandleData::loadOneHandle($phid, $user);
|
||||
|
||||
if (!$handle->isComplete()) {
|
||||
return new Aphront404Response();
|
||||
|
|
|
@ -29,6 +29,7 @@ final class PhabricatorFlagListController extends PhabricatorFlagController {
|
|||
|
||||
$query = new PhabricatorFlagQuery();
|
||||
$query->withOwnerPHIDs(array($user->getPHID()));
|
||||
$query->setViewer($user);
|
||||
$query->needHandles(true);
|
||||
|
||||
$flags = $query->execute();
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2012 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
final class PhabricatorFlagsUIEventListener extends PhutilEventListener {
|
||||
|
||||
public function register() {
|
||||
$this->listen(PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS);
|
||||
}
|
||||
|
||||
public function handleEvent(PhutilEvent $event) {
|
||||
switch ($event->getType()) {
|
||||
case PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS:
|
||||
$this->handleActionEvent($event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private function handleActionEvent($event) {
|
||||
$user = $event->getUser();
|
||||
$object = $event->getValue('object');
|
||||
|
||||
$flag = PhabricatorFlagQuery::loadUserFlag($user, $object->getPHID());
|
||||
|
||||
if ($flag) {
|
||||
$color = PhabricatorFlagColor::getColorName($flag->getColor());
|
||||
$flag_action = id(new PhabricatorActionView())
|
||||
->setWorkflow(true)
|
||||
->setHref('/flag/delete/'.$flag->getID().'/')
|
||||
->setName(phutil_escape_html('Remove '.$color.' Flag'))
|
||||
->setIcon('flag-'.$flag->getColor());
|
||||
} else {
|
||||
$flag_action = id(new PhabricatorActionView())
|
||||
->setWorkflow(true)
|
||||
->setHref('/flag/edit/'.$object->getPHID().'/')
|
||||
->setName('Flag For Later')
|
||||
->setIcon('flag-ghost');
|
||||
|
||||
if (!$user->isLoggedIn()) {
|
||||
$flag_action->setDisabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
$actions = $event->getValue('actions');
|
||||
$actions[] = $flag_action;
|
||||
$event->setValue('actions', $actions);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -27,6 +27,12 @@ final class PhabricatorFlagQuery {
|
|||
|
||||
private $needHandles;
|
||||
private $needObjects;
|
||||
private $viewer;
|
||||
|
||||
public function setViewer($viewer) {
|
||||
$this->viewer = $viewer;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withOwnerPHIDs(array $owner_phids) {
|
||||
$this->ownerPHIDs = $owner_phids;
|
||||
|
@ -94,6 +100,9 @@ final class PhabricatorFlagQuery {
|
|||
if ($this->needHandles || $this->needObjects) {
|
||||
$phids = ipull($data, 'objectPHID');
|
||||
$query = new PhabricatorObjectHandleData($phids);
|
||||
if ($this->viewer) {
|
||||
$query->setViewer($this->viewer);
|
||||
}
|
||||
|
||||
if ($this->needHandles) {
|
||||
$handles = $query->loadHandles();
|
||||
|
|
|
@ -99,6 +99,8 @@ final class PhabricatorPasteViewController extends PhabricatorPasteController {
|
|||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
|
||||
return id(new PhabricatorActionListView())
|
||||
->setUser($user)
|
||||
->setObject($paste)
|
||||
->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Fork This Paste'))
|
||||
|
|
|
@ -30,8 +30,14 @@ final class PhabricatorObjectHandleData {
|
|||
return $this;
|
||||
}
|
||||
|
||||
public static function loadOneHandle($phid) {
|
||||
$handles = id(new PhabricatorObjectHandleData(array($phid)))->loadHandles();
|
||||
public static function loadOneHandle($phid, $viewer = null) {
|
||||
$query = new PhabricatorObjectHandleData(array($phid));
|
||||
|
||||
if ($viewer) {
|
||||
$query->setViewer($viewer);
|
||||
}
|
||||
|
||||
$handles = $query->loadHandles();
|
||||
return $handles[$phid];
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,14 @@ final class PhabricatorEventEngine {
|
|||
id(new DarkConsoleEventPluginAPI())->register();
|
||||
id(new ManiphestEdgeEventListener())->register();
|
||||
|
||||
$applications = PhabricatorApplication::getAllInstalledApplications();
|
||||
foreach ($applications as $application) {
|
||||
$listeners = $application->getEventListeners();
|
||||
foreach ($listeners as $listener) {
|
||||
$listener->register();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -38,4 +38,10 @@ final class PhabricatorEventType extends PhutilEventType {
|
|||
|
||||
const TYPE_TEST_DIDRUNTEST = 'test.didRunTest';
|
||||
|
||||
const TYPE_UI_DIDRENDERACTIONS = 'ui.didRenderActions';
|
||||
|
||||
const TYPE_UI_WILLRENDEROBJECTS = 'ui.willRenderObjects';
|
||||
const TYPE_UI_DDIDRENDEROBJECT = 'ui.didRenderObject';
|
||||
const TYPE_UI_DIDRENDEROBJECTS = 'ui.didRenderObjects';
|
||||
|
||||
}
|
||||
|
|
|
@ -19,22 +19,51 @@
|
|||
final class PhabricatorActionListView extends AphrontView {
|
||||
|
||||
private $actions = array();
|
||||
private $object;
|
||||
private $user;
|
||||
|
||||
public function setObject(PhabricatorLiskDAO $object) {
|
||||
$this->object = $object;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setUser(PhabricatorUser $user) {
|
||||
$this->user = $user;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addAction(PhabricatorActionView $view) {
|
||||
$this->actions[] = $view;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function render() {
|
||||
require_celerity_resource('phabricator-action-list-view-css');
|
||||
if (!$this->user) {
|
||||
throw new Exception("Call setUser() before render()!");
|
||||
}
|
||||
|
||||
if (!$this->object) {
|
||||
throw new Exception("Call setObject() before render()!");
|
||||
}
|
||||
|
||||
$event = new PhabricatorEvent(
|
||||
PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS,
|
||||
array(
|
||||
'object' => $this->object,
|
||||
'actions' => $this->actions,
|
||||
));
|
||||
$event->setUser($this->user);
|
||||
PhutilEventEngine::dispatchEvent($event);
|
||||
|
||||
$actions = $event->getValue('actions');
|
||||
|
||||
require_celerity_resource('phabricator-action-list-view-css');
|
||||
return phutil_render_tag(
|
||||
'ul',
|
||||
array(
|
||||
'class' => 'phabricator-action-list-view',
|
||||
),
|
||||
$this->renderSingleView($this->actions));
|
||||
$this->renderSingleView($actions));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -55,7 +55,12 @@ final class PhabricatorPropertyListView extends AphrontView {
|
|||
array(
|
||||
'class' => 'phabricator-property-list-view',
|
||||
),
|
||||
$list);
|
||||
$list.
|
||||
// NOTE: We need this (which is basically a "clear: both;" div) to make
|
||||
// sure the property list is taller than the action list for objects with
|
||||
// few properties but many actions. Otherwise, the action list may
|
||||
// obscure the document content.
|
||||
'<div class="phabriator-property-list-view-end"></div>');
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -262,3 +262,39 @@
|
|||
.action-edit {
|
||||
background-position: 0px -2555px;
|
||||
}
|
||||
|
||||
.action-flag-0 {
|
||||
background-position: 0px -2572px;
|
||||
}
|
||||
|
||||
.action-flag-1 {
|
||||
background-position: 0px -2589px;
|
||||
}
|
||||
|
||||
.action-flag-2 {
|
||||
background-position: 0px -2606px;
|
||||
}
|
||||
|
||||
.action-flag-3 {
|
||||
background-position: 0px -2623px;
|
||||
}
|
||||
|
||||
.action-flag-4 {
|
||||
background-position: 0px -2640px;
|
||||
}
|
||||
|
||||
.action-flag-5 {
|
||||
background-position: 0px -2657px;
|
||||
}
|
||||
|
||||
.action-flag-6 {
|
||||
background-position: 0px -2674px;
|
||||
}
|
||||
|
||||
.action-flag-7 {
|
||||
background-position: 0px -2691px;
|
||||
}
|
||||
|
||||
.action-flag-ghost {
|
||||
background-position: 0px -2708px;
|
||||
}
|
||||
|
|
|
@ -10,9 +10,9 @@
|
|||
border: 1px solid #dcdcdc;
|
||||
padding: .5em 0;
|
||||
|
||||
position: absolute;
|
||||
float: right;
|
||||
margin-top: -30px;
|
||||
right: 1%;
|
||||
margin-right: 1%;
|
||||
width: 20%;
|
||||
border-radius: 2px;
|
||||
font-size: 12px;
|
||||
|
|
|
@ -7,8 +7,10 @@
|
|||
border-style: solid;
|
||||
border-width: 1px 0;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
overflow: hidden;
|
||||
.phabriator-property-list-view-end {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.device-desktop .phabricator-property-list-view {
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 88 KiB |
Loading…
Reference in a new issue