1
0
Fork 0
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:
epriestley 2012-08-24 13:19:47 -07:00
parent 36e71a0601
commit 85bf88e400
21 changed files with 209 additions and 18 deletions

View file

@ -159,9 +159,18 @@ $action_template = id(new PhutilSprite())
->setSourceSize(16, 16);
$action_map = array(
'file' => 'icon/page_white_text.png',
'fork' => 'icon/arrow_branch.png',
'edit' => 'icon/page_white_edit.png',
'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) {

View file

@ -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(

View file

@ -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',

View file

@ -89,6 +89,10 @@ abstract class PhabricatorApplication {
return null;
}
public function getEventListeners() {
return array();
}
/* -( URI Routing )-------------------------------------------------------- */

View file

@ -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) {

View file

@ -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);

View file

@ -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();

View file

@ -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();

View file

@ -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();

View file

@ -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);
}
}

View file

@ -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();

View file

@ -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'))

View file

@ -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];
}

View file

@ -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();
}
}
}
}

View file

@ -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';
}

View file

@ -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));
}

View file

@ -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>');
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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