1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-26 08:42:41 +01:00

Add "Flags" to allow users to collect the things they love

Summary:
Flags are a personal collection of things you want to take a look at later. You can use several different colors and add notes.

Not really sure if this is actually a good idea or not but it was easy to build.

Planned features:

  - Allow Herald rules to add flags.
  - In the "edit flag" dialog, have a "[x] Subscribe Me" checkbox that CCs you.
  - Support Diffusion.
  - Support Phriction.
  - Always show flags on an object if you have them (in every view)?
  - Edit dialog feels a little heavy?
  - More filtering in /flag/ tool.
  - Add a top-level links somewhere?

Test Plan: Added, edited and removed flags from things. Viewed flags in flag view.

Reviewers: aran, btrahan

Reviewed By: btrahan

CC: aran, epriestley, Koolvin

Maniphest Tasks: T1041

Differential Revision: https://secure.phabricator.com/D2024
This commit is contained in:
epriestley 2012-03-27 16:22:40 -07:00
parent a3028558ae
commit 7ad68e63e4
48 changed files with 1213 additions and 21 deletions

View file

@ -0,0 +1,2 @@
/* Do nothing, patch 121 got committed before there was a patch 120. */
SELECT 1;

View file

@ -0,0 +1,16 @@
CREATE DATABASE phabricator_flag COLLATE utf8_general_ci;
CREATE TABLE phabricator_flag.flag (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
ownerPHID varchar(64) COLLATE utf8_bin NOT NULL,
type varchar(4) COLLATE utf8_bin NOT NULL,
objectPHID varchar(64) COLLATE utf8_bin NOT NULL,
reasonPHID varchar(64) COLLATE utf8_bin NOT NULL,
color INT UNSIGNED NOT NULL,
note varchar(255) COLLATE utf8_general_ci,
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL,
UNIQUE KEY (ownerPHID, type, objectPHID),
KEY (objectPHID)
) ENGINE=InnoDB;

View file

@ -172,7 +172,7 @@ celerity_register_resource_map(array(
),
'differential-changeset-view-css' =>
array(
'uri' => '/res/13983f98/rsrc/css/application/differential/changeset-view.css',
'uri' => '/res/238f435d/rsrc/css/application/differential/changeset-view.css',
'type' => 'css',
'requires' =>
array(
@ -1595,6 +1595,15 @@ celerity_register_resource_map(array(
),
'disk' => '/rsrc/css/application/feed/feed.css',
),
'phabricator-flag-css' =>
array(
'uri' => '/res/81d9e551/rsrc/css/application/flag/flag.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/flag/flag.css',
),
'phabricator-jump-nav' =>
array(
'uri' => '/res/8bdc0fc3/rsrc/css/application/directory/phabricator-jump-nav.css',
@ -2030,7 +2039,7 @@ celerity_register_resource_map(array(
'uri' => '/res/pkg/21d01ed8/core.pkg.js',
'type' => 'js',
),
'8a5929ca' =>
'90c90e95' =>
array(
'name' => 'differential.pkg.css',
'symbols' =>
@ -2048,7 +2057,7 @@ celerity_register_resource_map(array(
10 => 'phabricator-content-source-view-css',
11 => 'differential-local-commits-view-css',
),
'uri' => '/res/pkg/8a5929ca/differential.pkg.css',
'uri' => '/res/pkg/90c90e95/differential.pkg.css',
'type' => 'css',
),
'9b256876' =>
@ -2155,7 +2164,7 @@ celerity_register_resource_map(array(
'aphront-crumbs-view-css' => '82263727',
'aphront-dialog-view-css' => '82263727',
'aphront-form-view-css' => '82263727',
'aphront-headsup-action-list-view-css' => '8a5929ca',
'aphront-headsup-action-list-view-css' => '90c90e95',
'aphront-list-filter-view-css' => '82263727',
'aphront-pager-view-css' => '82263727',
'aphront-panel-view-css' => '82263727',
@ -2163,16 +2172,16 @@ celerity_register_resource_map(array(
'aphront-table-view-css' => '82263727',
'aphront-tokenizer-control-css' => '82263727',
'aphront-typeahead-control-css' => '82263727',
'differential-changeset-view-css' => '8a5929ca',
'differential-core-view-css' => '8a5929ca',
'differential-changeset-view-css' => '90c90e95',
'differential-core-view-css' => '90c90e95',
'differential-inline-comment-editor' => '9b256876',
'differential-local-commits-view-css' => '8a5929ca',
'differential-revision-add-comment-css' => '8a5929ca',
'differential-revision-comment-css' => '8a5929ca',
'differential-revision-comment-list-css' => '8a5929ca',
'differential-revision-detail-css' => '8a5929ca',
'differential-revision-history-css' => '8a5929ca',
'differential-table-of-contents-css' => '8a5929ca',
'differential-local-commits-view-css' => '90c90e95',
'differential-revision-add-comment-css' => '90c90e95',
'differential-revision-comment-css' => '90c90e95',
'differential-revision-comment-list-css' => '90c90e95',
'differential-revision-detail-css' => '90c90e95',
'differential-revision-history-css' => '90c90e95',
'differential-table-of-contents-css' => '90c90e95',
'diffusion-commit-view-css' => '61f9d480',
'javelin-behavior' => '4fbae2af',
'javelin-behavior-aphront-basic-tokenizer' => '2af849fb',
@ -2221,7 +2230,7 @@ celerity_register_resource_map(array(
'maniphest-task-summary-css' => '31583232',
'maniphest-transaction-detail-css' => '31583232',
'phabricator-app-buttons-css' => '82263727',
'phabricator-content-source-view-css' => '8a5929ca',
'phabricator-content-source-view-css' => '90c90e95',
'phabricator-core-buttons-css' => '82263727',
'phabricator-core-css' => '82263727',
'phabricator-directory-css' => '82263727',
@ -2231,7 +2240,7 @@ celerity_register_resource_map(array(
'phabricator-keyboard-shortcut' => '21d01ed8',
'phabricator-keyboard-shortcut-manager' => '21d01ed8',
'phabricator-menu-item' => '21d01ed8',
'phabricator-object-selector-css' => '8a5929ca',
'phabricator-object-selector-css' => '90c90e95',
'phabricator-paste-file-upload' => '21d01ed8',
'phabricator-remarkup-css' => '82263727',
'phabricator-shaped-request' => '9b256876',

View file

@ -142,6 +142,8 @@ phutil_register_library_map(array(
'ConduitAPI_file_download_Method' => 'applications/conduit/method/file/download',
'ConduitAPI_file_info_Method' => 'applications/conduit/method/file/info',
'ConduitAPI_file_upload_Method' => 'applications/conduit/method/file/upload',
'ConduitAPI_flag_Method' => 'applications/conduit/method/flag/base',
'ConduitAPI_flag_query_Method' => 'applications/conduit/method/flag/query',
'ConduitAPI_macro_Method' => 'applications/conduit/method/macro/base',
'ConduitAPI_macro_query_Method' => 'applications/conduit/method/macro/query',
'ConduitAPI_maniphest_Method' => 'applications/conduit/method/maniphest/base',
@ -594,6 +596,16 @@ phutil_register_library_map(array(
'PhabricatorFileUploadController' => 'applications/files/controller/upload',
'PhabricatorFileUploadException' => 'applications/files/exception/upload',
'PhabricatorFileUploadView' => 'applications/files/view/upload',
'PhabricatorFlag' => 'applications/flag/storage/flag',
'PhabricatorFlagColor' => 'applications/flag/constants/color',
'PhabricatorFlagConstants' => 'applications/flag/constants/base',
'PhabricatorFlagController' => 'applications/flag/controller/base',
'PhabricatorFlagDAO' => 'applications/flag/storage/base',
'PhabricatorFlagDeleteController' => 'applications/flag/controller/delete',
'PhabricatorFlagEditController' => 'applications/flag/controller/edit',
'PhabricatorFlagListController' => 'applications/flag/controller/list',
'PhabricatorFlagListView' => 'applications/flag/view/list',
'PhabricatorFlagQuery' => 'applications/flag/query/flag',
'PhabricatorGarbageCollectorDaemon' => 'infrastructure/daemon/garbagecollector',
'PhabricatorGoodForNothingWorker' => 'infrastructure/daemon/workers/worker/goodfornothing',
'PhabricatorHandleObjectSelectorDataView' => 'applications/phid/handle/view/selector',
@ -1052,6 +1064,8 @@ phutil_register_library_map(array(
'ConduitAPI_file_download_Method' => 'ConduitAPIMethod',
'ConduitAPI_file_info_Method' => 'ConduitAPIMethod',
'ConduitAPI_file_upload_Method' => 'ConduitAPIMethod',
'ConduitAPI_flag_Method' => 'ConduitAPIMethod',
'ConduitAPI_flag_query_Method' => 'ConduitAPI_flag_Method',
'ConduitAPI_macro_Method' => 'ConduitAPIMethod',
'ConduitAPI_macro_query_Method' => 'ConduitAPI_macro_Method',
'ConduitAPI_maniphest_Method' => 'ConduitAPIMethod',
@ -1409,6 +1423,14 @@ phutil_register_library_map(array(
'PhabricatorFileTransformController' => 'PhabricatorFileController',
'PhabricatorFileUploadController' => 'PhabricatorFileController',
'PhabricatorFileUploadView' => 'AphrontView',
'PhabricatorFlag' => 'PhabricatorFlagDAO',
'PhabricatorFlagColor' => 'PhabricatorFlagConstants',
'PhabricatorFlagController' => 'PhabricatorController',
'PhabricatorFlagDAO' => 'PhabricatorLiskDAO',
'PhabricatorFlagDeleteController' => 'PhabricatorFlagController',
'PhabricatorFlagEditController' => 'PhabricatorFlagController',
'PhabricatorFlagListController' => 'PhabricatorFlagController',
'PhabricatorFlagListView' => 'AphrontView',
'PhabricatorGarbageCollectorDaemon' => 'PhabricatorDaemon',
'PhabricatorGoodForNothingWorker' => 'PhabricatorWorker',
'PhabricatorHelpController' => 'PhabricatorController',

View file

@ -409,6 +409,13 @@ class AphrontDefaultApplicationConfiguration
),
'/aphlict/' => 'PhabricatorAphlictTestPageController',
'/flag/' => array(
'' => 'PhabricatorFlagListController',
'view/(?P<view>[^/]+)/' => 'PhabricatorFlagListController',
'edit/(?P<phid>[^/]+)/' => 'PhabricatorFlagEditController',
'delete/(?P<id>\d+)/' => 'PhabricatorFlagDeleteController',
),
);
}

View file

@ -0,0 +1,25 @@
<?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.
*/
/**
* @group conduit
*/
abstract class ConduitAPI_flag_Method extends ConduitAPIMethod {
}

View file

@ -0,0 +1,12 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/conduit/method/base');
phutil_require_source('ConduitAPI_flag_Method.php');

View file

@ -0,0 +1,100 @@
<?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.
*/
/**
* @group conduit
*/
final class ConduitAPI_flag_query_Method extends ConduitAPI_flag_Method {
public function getMethodDescription() {
return "Query flag markers.";
}
public function defineParamTypes() {
return array(
'ownerPHIDs' => 'optional list<phid>',
'types' => 'optional list<type>',
'objectPHIDs' => 'optional list<phid>',
'offset' => 'optional int',
'limit' => 'optional int (default = 100)',
);
}
public function defineReturnType() {
return 'list<dict>';
}
public function defineErrorTypes() {
return array(
);
}
protected function execute(ConduitAPIRequest $request) {
$query = new PhabricatorFlagQuery();
$owner_phids = $request->getValue('ownerPHIDs', array());
if ($owner_phids) {
$query->withOwnerPHIDs($owner_phids);
}
$object_phids = $request->getValue('objectPHIDs', array());
if ($object_phids) {
$query->withObjectPHIDs($object_phids);
}
$types = $request->getValue('types', array());
if ($types) {
$query->withTypes($types);
}
$query->needHandles(true);
$query->setOffset($request->getValue('offset', 0));
$query->setLimit($request->getValue('limit', 100));
$flags = $query->execute();
$results = array();
foreach ($flags as $flag) {
$color = $flag->getColor();
$uri = PhabricatorEnv::getProductionURI($flag->getHandle()->getURI());
$results[] = array(
'id' => $flag->getID(),
'ownerPHID' => $flag->getOwnerPHID(),
'type' => $flag->getType(),
'objectPHID' => $flag->getObjectPHID(),
'reasonPHID' => $flag->getReasonPHID(),
'color' => $color,
'colorName' => PhabricatorFlagColor::getColorName($color),
'note' => $flag->getNote(),
'handle' => array(
'uri' => $uri,
'name' => $flag->getHandle()->getName(),
),
'dateCreated' => $flag->getDateCreated(),
'dateModified' => $flag->getDateModified(),
);
}
return $results;
}
}

View file

@ -0,0 +1,15 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/conduit/method/flag/base');
phutil_require_module('phabricator', 'applications/flag/constants/color');
phutil_require_module('phabricator', 'applications/flag/query/flag');
phutil_require_module('phabricator', 'infrastructure/env');
phutil_require_source('ConduitAPI_flag_query_Method.php');

View file

@ -164,13 +164,20 @@ final class DifferentialRevisionListController extends DifferentialController {
$views = $this->buildViews($this->filter, $params['phid'], $revisions);
$view_objects = ipull($views, 'view');
$view_objects = array();
foreach ($views as $view) {
if (empty($view['special'])) {
$view_objects[] = $view['view'];
}
}
$phids = array_mergev(mpull($view_objects, 'getRequiredHandlePHIDs'));
$phids[] = $params['phid'];
$handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
foreach ($views as $view) {
$view['view']->setHandles($handles);
if (empty($view['special'])) {
$view['view']->setHandles($handles);
}
$panel = new AphrontPanelView();
$panel->setHeader($view['title']);
$panel->appendChild($view['view']);
@ -436,6 +443,26 @@ final class DifferentialRevisionListController extends DifferentialController {
'view' => $view,
);
// Flags are sort of private, so only show the flag panel if you're
// looking at your own requests.
if ($user_phid == $user->getPHID()) {
$flags = id(new PhabricatorFlagQuery())
->withOwnerPHIDs(array($user_phid))
->withTypes(array(PhabricatorPHIDConstants::PHID_TYPE_DREV))
->needHandles(true)
->execute();
$view = id(new PhabricatorFlagListView())
->setFlags($flags)
->setUser($user);
$views[] = array(
'title' => 'Flagged Revisions',
'view' => $view,
'special' => true,
);
}
$view = id(clone $template)
->setRevisions($waiting)
->setNoDataString("You have no active revisions waiting on others.");

View file

@ -11,7 +11,10 @@ phutil_require_module('phabricator', 'aphront/response/redirect');
phutil_require_module('phabricator', 'applications/differential/controller/base');
phutil_require_module('phabricator', 'applications/differential/query/revision');
phutil_require_module('phabricator', 'applications/differential/view/revisionlist');
phutil_require_module('phabricator', 'applications/flag/query/flag');
phutil_require_module('phabricator', 'applications/flag/view/list');
phutil_require_module('phabricator', 'applications/people/storage/user');
phutil_require_module('phabricator', 'applications/phid/constants');
phutil_require_module('phabricator', 'applications/phid/handle/data');
phutil_require_module('phabricator', 'view/control/pager');
phutil_require_module('phabricator', 'view/form/base');

View file

@ -358,7 +358,8 @@ final class DifferentialRevisionViewController extends DifferentialController {
}
private function getRevisionActions(DifferentialRevision $revision) {
$viewer_phid = $this->getRequest()->getUser()->getPHID();
$user = $this->getRequest()->getUser();
$viewer_phid = $user->getPHID();
$viewer_is_owner = ($revision->getAuthorPHID() == $viewer_phid);
$viewer_is_reviewer = in_array($viewer_phid, $revision->getReviewers());
$viewer_is_cc = in_array($viewer_phid, $revision->getCCPHIDs());
@ -378,6 +379,28 @@ final class DifferentialRevisionViewController extends DifferentialController {
}
if (!$viewer_is_anonymous) {
require_celerity_resource('phabricator-flag-css');
$flag = PhabricatorFlagQuery::loadUserFlag($user, $revision_phid);
if ($flag) {
$class = PhabricatorFlagColor::getCSSClass($flag->getColor());
$color = PhabricatorFlagColor::getColorName($flag->getColor());
$links[] = array(
'class' => 'flag-clear '.$class,
'href' => '/flag/delete/'.$flag->getID().'/',
'name' => phutil_escape_html('Remove '.$color.' Flag'),
'sigil' => 'workflow',
);
} else {
$links[] = array(
'class' => 'flag-add phabricator-flag-ghost',
'href' => '/flag/edit/'.$revision_phid.'/',
'name' => 'Flag Revision',
'sigil' => 'workflow',
);
}
if (!$viewer_is_owner && !$viewer_is_reviewer) {
$action = $viewer_is_cc ? 'rem' : 'add';
$links[] = array(

View file

@ -28,6 +28,8 @@ phutil_require_module('phabricator', 'applications/differential/view/revisioncom
phutil_require_module('phabricator', 'applications/differential/view/revisiondetail');
phutil_require_module('phabricator', 'applications/differential/view/revisionupdatehistory');
phutil_require_module('phabricator', 'applications/draft/storage/draft');
phutil_require_module('phabricator', 'applications/flag/constants/color');
phutil_require_module('phabricator', 'applications/flag/query/flag');
phutil_require_module('phabricator', 'applications/markup/syntax');
phutil_require_module('phabricator', 'applications/phid/handle/data');
phutil_require_module('phabricator', 'infrastructure/celerity/api');

View file

@ -74,6 +74,8 @@ final class PhabricatorDirectoryMainController
$tasks_panel = null;
}
$flagged_panel = $this->buildFlaggedPanel();
$jump_panel = $this->buildJumpPanel();
$revision_panel = $this->buildRevisionPanel();
$app_panel = $this->buildAppPanel();
@ -87,6 +89,7 @@ final class PhabricatorDirectoryMainController
$triage_panel,
$revision_panel,
$tasks_panel,
$flagged_panel,
$audit_panel,
$commit_panel,
);
@ -193,6 +196,43 @@ final class PhabricatorDirectoryMainController
return $panel;
}
private function buildFlaggedPanel() {
$user = $this->getRequest()->getUser();
$flag_query = id(new PhabricatorFlagQuery())
->withOwnerPHIDs(array($user->getPHID()))
->needHandles(true)
->setLimit(10);
$flags = $flag_query->execute();
if (!$flags) {
return $this->renderMiniPanel(
'No Flags',
"You haven't flagged anything.");
}
$panel = new AphrontPanelView();
$panel->setHeader('Flagged Objects');
$panel->setCaption("Objects you've flagged.");
$flag_view = new PhabricatorFlagListView();
$flag_view->setFlags($flags);
$flag_view->setUser($user);
$panel->appendChild($flag_view);
$panel->addButton(
phutil_render_tag(
'a',
array(
'href' => '/flag/',
'class' => 'grey button',
),
"View All Flags \xC2\xBB"));
return $panel;
}
private function buildNeedsTriagePanel(array $projects) {
$user = $this->getRequest()->getUser();
$user_phid = $user->getPHID();

View file

@ -17,6 +17,8 @@ phutil_require_module('phabricator', 'applications/differential/view/revisionlis
phutil_require_module('phabricator', 'applications/directory/controller/base');
phutil_require_module('phabricator', 'applications/feed/builder/feed');
phutil_require_module('phabricator', 'applications/feed/query');
phutil_require_module('phabricator', 'applications/flag/query/flag');
phutil_require_module('phabricator', 'applications/flag/view/list');
phutil_require_module('phabricator', 'applications/maniphest/constants/priority');
phutil_require_module('phabricator', 'applications/maniphest/query');
phutil_require_module('phabricator', 'applications/maniphest/view/tasklist');

View file

@ -0,0 +1,21 @@
<?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.
*/
abstract class PhabricatorFlagConstants {
}

View file

@ -0,0 +1,10 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_source('PhabricatorFlagConstants.php');

View file

@ -0,0 +1,51 @@
<?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 PhabricatorFlagColor extends PhabricatorFlagConstants {
const COLOR_RED = 0;
const COLOR_ORANGE = 1;
const COLOR_YELLOW = 2;
const COLOR_GREEN = 3;
const COLOR_BLUE = 4;
const COLOR_PINK = 5;
const COLOR_PURPLE = 6;
const COLOR_CHECKERED = 7;
public static function getColorNameMap() {
return array(
self::COLOR_RED => 'Red',
self::COLOR_ORANGE => 'Orange',
self::COLOR_YELLOW => 'Yellow',
self::COLOR_GREEN => 'Green',
self::COLOR_BLUE => 'Blue',
self::COLOR_PINK => 'Pink',
self::COLOR_PURPLE => 'Purple',
self::COLOR_CHECKERED => 'Checkered',
);
}
public static function getColorName($color) {
return idx(self::getColorNameMap(), $color, 'Unknown');
}
public static function getCSSClass($color) {
return 'phabricator-flag-color-'.(int)$color;
}
}

View file

@ -0,0 +1,14 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/flag/constants/base');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorFlagColor.php');

View file

@ -0,0 +1,35 @@
<?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.
*/
abstract class PhabricatorFlagController extends PhabricatorController {
public function buildStandardPageResponse($view, array $data) {
$page = $this->buildStandardPageView();
$page->setApplicationName('Flag');
$page->setBaseURI('/flag/');
$page->setTitle(idx($data, 'title'));
$page->setGlyph("\xE2\x9A\x90"); // Subtle!
$page->appendChild($view);
$response = new AphrontWebpageResponse();
return $response->setContent($page->render());
}
}

View file

@ -0,0 +1,15 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'aphront/response/webpage');
phutil_require_module('phabricator', 'applications/base/controller/base');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorFlagController.php');

View file

@ -0,0 +1,45 @@
<?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 PhabricatorFlagDeleteController extends PhabricatorFlagController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$flag = id(new PhabricatorFlag())->load($this->id);
if (!$flag) {
return new Aphront404Response();
}
if ($flag->getOwnerPHID() != $user->getPHID()) {
return new Aphront400Response();
}
$flag->delete();
return id(new AphrontReloadResponse())->setURI('/flag/');
}
}

View file

@ -0,0 +1,18 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'aphront/response/400');
phutil_require_module('phabricator', 'aphront/response/404');
phutil_require_module('phabricator', 'aphront/response/reload');
phutil_require_module('phabricator', 'applications/flag/controller/base');
phutil_require_module('phabricator', 'applications/flag/storage/flag');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorFlagDeleteController.php');

View file

@ -0,0 +1,99 @@
<?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 PhabricatorFlagEditController extends PhabricatorFlagController {
private $phid;
public function willProcessRequest(array $data) {
$this->phid = $data['phid'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$phid = $this->phid;
$handles = id(new PhabricatorObjectHandleData(array($phid)))
->loadHandles();
$handle = $handles[$phid];
if (!$handle->isComplete()) {
return new Aphront404Response();
}
$flag = PhabricatorFlagQuery::loadUserFlag($user, $phid);
if (!$flag) {
$flag = new PhabricatorFlag();
$flag->setOwnerPHID($user->getPHID());
$flag->setType($handle->getType());
$flag->setObjectPHID($handle->getPHID());
$flag->setReasonPHID($user->getPHID());
}
if ($request->isDialogFormPost()) {
$flag->setColor($request->getInt('color'));
$flag->setNote($request->getStr('note'));
$flag->save();
return id(new AphrontReloadResponse())->setURI('/flag/');
}
$type_name = $handle->getTypeName();
$dialog = new AphrontDialogView();
$dialog->setUser($user);
$dialog->setTitle("Flag {$type_name}");
$form = new AphrontFormLayoutView();
$is_new = !$flag->getID();
if ($is_new) {
$form
->appendChild(
"<p>You can flag this {$type_name} if you want to remember to look ".
"at it later.</p><br />");
}
$form
->appendChild(
id(new AphrontFormSelectControl())
->setName('color')
->setLabel('Flag Color')
->setValue($flag->getColor())
->setOptions(PhabricatorFlagColor::getColorNameMap()))
->appendChild(
id(new AphrontFormTextAreaControl())
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_SHORT)
->setName('note')
->setLabel('Note')
->setValue($flag->getNote()));
$dialog->appendChild($form);
$dialog->addCancelButton($handle->getURI());
$dialog->addSubmitButton(
$is_new ? "Flag {$type_name}" : 'Save');
return id(new AphrontDialogResponse())->setDialog($dialog);
}
}

View file

@ -0,0 +1,25 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'aphront/response/404');
phutil_require_module('phabricator', 'aphront/response/dialog');
phutil_require_module('phabricator', 'aphront/response/reload');
phutil_require_module('phabricator', 'applications/flag/constants/color');
phutil_require_module('phabricator', 'applications/flag/controller/base');
phutil_require_module('phabricator', 'applications/flag/query/flag');
phutil_require_module('phabricator', 'applications/flag/storage/flag');
phutil_require_module('phabricator', 'applications/phid/handle/data');
phutil_require_module('phabricator', 'view/dialog');
phutil_require_module('phabricator', 'view/form/control/select');
phutil_require_module('phabricator', 'view/form/control/textarea');
phutil_require_module('phabricator', 'view/form/layout');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorFlagEditController.php');

View file

@ -0,0 +1,53 @@
<?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 PhabricatorFlagListController extends PhabricatorFlagController {
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI('/flag/view/'));
$nav->addFilter('all', 'Flags');
$nav->selectFilter('all', 'all');
$query = new PhabricatorFlagQuery();
$query->withOwnerPHIDs(array($user->getPHID()));
$query->needHandles(true);
$flags = $query->execute();
$view = new PhabricatorFlagListView();
$view->setFlags($flags);
$view->setUser($user);
$panel = new AphrontPanelView();
$panel->setHeader('Flags');
$panel->appendChild($view);
$nav->appendChild($panel);
return $this->buildStandardPageResponse(
$nav,
array(
'title' => 'Flags',
));
}
}

View file

@ -0,0 +1,18 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/flag/controller/base');
phutil_require_module('phabricator', 'applications/flag/query/flag');
phutil_require_module('phabricator', 'applications/flag/view/list');
phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phabricator', 'view/layout/sidenavfilter');
phutil_require_module('phutil', 'parser/uri');
phutil_require_source('PhabricatorFlagListController.php');

View file

@ -0,0 +1,170 @@
<?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 PhabricatorFlagQuery {
private $ownerPHIDs;
private $types;
private $objectPHIDs;
private $limit;
private $offset;
private $needHandles;
private $needObjects;
public function withOwnerPHIDs(array $owner_phids) {
$this->ownerPHIDs = $owner_phids;
return $this;
}
public function withTypes(array $types) {
$this->types = $types;
return $this;
}
public function withObjectPHIDs(array $object_phids) {
$this->objectPHIDs = $object_phids;
return $this;
}
public function needHandles($need) {
$this->needHandles = $need;
return $this;
}
public function needObjects($need) {
$this->needObjects = $need;
return $this;
}
public function setLimit($limit) {
$this->limit = $limit;
return $this;
}
public function setOffset($offset) {
$this->offset = $offset;
return $this;
}
public static function loadUserFlag(PhabricatorUser $user, $object_phid) {
// Specifying the type in the query allows us to use a key.
return id(new PhabricatorFlag())->loadOneWhere(
'ownerPHID = %s AND type = %s AND objectPHID = %s',
$user->getPHID(),
PhabricatorObjectHandleData::lookupType($object_phid),
$object_phid);
}
public function execute() {
$table = new PhabricatorFlag();
$conn_r = $table->establishConnection('r');
$where = $this->buildWhereClause($conn_r);
$limit = $this->buildLimitClause($conn_r);
$order = $this->buildOrderClause($conn_r);
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T flag %Q %Q %Q',
$table->getTableName(),
$where,
$order,
$limit);
$flags = $table->loadAllFromArray($data);
if ($this->needHandles || $this->needObjects) {
$phids = ipull($data, 'objectPHID');
$query = new PhabricatorObjectHandleData($phids);
if ($this->needHandles) {
$handles = $query->loadHandles();
foreach ($flags as $flag) {
$handle = idx($handles, $flag->getObjectPHID());
if ($handle) {
$flag->attachHandle($handle);
}
}
}
if ($this->needObjects) {
$objects = $query->loadObjects();
foreach ($flags as $flag) {
$object = idx($objects, $flag->getObjectPHID());
if ($object) {
$flag->attachObject($object);
}
}
}
}
return $flags;
}
private function buildWhereClause($conn_r) {
$where = array();
if ($this->ownerPHIDs) {
$where[] = qsprintf(
$conn_r,
'flag.ownerPHID IN (%Ls)',
$this->ownerPHIDs);
}
if ($this->types) {
$where[] = qsprintf(
$conn_r,
'flag.type IN (%Ls)',
$this->types);
}
if ($this->objectPHIDs) {
$where[] = qsprintf(
$conn_r,
'flag.objectPHID IN (%Ls)',
$this->objectPHIDs);
}
if ($where) {
return 'WHERE ('.implode(') AND (', $where).')';
} else {
return '';
}
}
private function buildOrderClause($conn_r) {
return 'ORDER BY id DESC';
}
private function buildLimitClause($conn_r) {
if ($this->limit && $this->offset) {
return qsprintf($conn_r, 'LIMIT %d, %d', $this->offset, $this->limit);
} else if ($this->limit) {
return qsprintf($conn_r, 'LIMIT %d', $this->limit);
} else if ($this->offset) {
return qsprintf($conn_r, 'LIMIT %d, %d', $this->offset, PHP_INT_MAX);
} else {
return '';
}
}
}

View file

@ -0,0 +1,17 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/flag/storage/flag');
phutil_require_module('phabricator', 'applications/phid/handle/data');
phutil_require_module('phabricator', 'storage/qsprintf');
phutil_require_module('phabricator', 'storage/queryfx');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorFlagQuery.php');

View file

@ -0,0 +1,25 @@
<?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.
*/
abstract class PhabricatorFlagDAO extends PhabricatorLiskDAO {
public function getApplicationName() {
return 'flag';
}
}

View file

@ -0,0 +1,12 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/base/storage/lisk');
phutil_require_source('PhabricatorFlagDAO.php');

View file

@ -0,0 +1,55 @@
<?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 PhabricatorFlag extends PhabricatorFlagDAO {
protected $ownerPHID;
protected $type;
protected $objectPHID;
protected $reasonPHID;
protected $color = PhabricatorFlagColor::COLOR_BLUE;
protected $note;
private $handle = false;
private $object = false;
public function getObject() {
if ($this->object === false) {
throw new Exception('Call attachObject() before getObject()!');
}
return $this->object;
}
public function attachObject($object) {
$this->object = $object;
return $this;
}
public function getHandle() {
if ($this->handle === false) {
throw new Exception('Call attachHandle() before getHandle()!');
}
return $this->handle;
}
public function attachHandle(PhabricatorObjectHandle $handle) {
$this->handle = $handle;
return $this;
}
}

View file

@ -0,0 +1,13 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/flag/constants/color');
phutil_require_module('phabricator', 'applications/flag/storage/base');
phutil_require_source('PhabricatorFlag.php');

View file

@ -0,0 +1,105 @@
<?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 PhabricatorFlagListView extends AphrontView {
private $flags;
private $user;
public function setFlags(array $flags) {
$this->flags = $flags;
return $this;
}
public function setUser(PhabricatorUser $user) {
$this->user = $user;
return $this;
}
public function render() {
$user = $this->user;
require_celerity_resource('phabricator-flag-css');
$rows = array();
foreach ($this->flags as $flag) {
$class = PhabricatorFlagColor::getCSSClass($flag->getColor());
$rows[] = array(
phutil_render_tag(
'div',
array(
'class' => 'phabricator-flag-icon '.$class,
),
''),
$flag->getHandle()->renderLink(),
phutil_escape_html($flag->getNote()),
phabricator_datetime($flag->getDateCreated(), $user),
phabricator_render_form(
$user,
array(
'method' => 'POST',
'action' => '/flag/edit/'.$flag->getObjectPHID().'/',
'sigil' => 'workflow',
),
phutil_render_tag(
'button',
array(
'class' => 'small grey',
),
'Edit Flag')),
phabricator_render_form(
$user,
array(
'method' => 'POST',
'action' => '/flag/delete/'.$flag->getID().'/',
'sigil' => 'workflow',
),
phutil_render_tag(
'button',
array(
'class' => 'small grey',
),
'Remove Flag')),
);
}
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
'',
'Flagged Object',
'Note',
'Flagged On',
'',
'',
));
$table->setColumnClasses(
array(
'',
'pri',
'wide',
'',
'action',
'action',
));
$table->setNoDataString('No flags.');
return $table->render();
}
}

View file

@ -0,0 +1,19 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/flag/constants/color');
phutil_require_module('phabricator', 'infrastructure/celerity/api');
phutil_require_module('phabricator', 'infrastructure/javelin/markup');
phutil_require_module('phabricator', 'view/base');
phutil_require_module('phabricator', 'view/control/table');
phutil_require_module('phabricator', 'view/utils');
phutil_require_module('phutil', 'markup');
phutil_require_source('PhabricatorFlagListView.php');

View file

@ -253,6 +253,27 @@ final class ManiphestTaskDetailController extends ManiphestController {
$action->setClass('action-edit');
$actions[] = $action;
require_celerity_resource('phabricator-flag-css');
$flag = PhabricatorFlagQuery::loadUserFlag($user, $task->getPHID());
if ($flag) {
$class = PhabricatorFlagColor::getCSSClass($flag->getColor());
$color = PhabricatorFlagColor::getColorName($flag->getColor());
$action = new AphrontHeadsupActionView();
$action->setClass('flag-clear '.$class);
$action->setURI('/flag/delete/'.$flag->getID().'/');
$action->setName('Remove '.$color.' Flag');
$action->setWorkflow(true);
$actions[] = $action;
} else {
$action = new AphrontHeadsupActionView();
$action->setClass('phabricator-flag-ghost');
$action->setURI('/flag/edit/'.$task->getPHID().'/');
$action->setName('Flag Task');
$action->setWorkflow(true);
$actions[] = $action;
}
require_celerity_resource('phabricator-object-selector-css');
require_celerity_resource('javelin-behavior-phabricator-object-selector');

View file

@ -9,6 +9,8 @@
phutil_require_module('phabricator', 'aphront/response/404');
phutil_require_module('phabricator', 'applications/draft/storage/draft');
phutil_require_module('phabricator', 'applications/files/storage/file');
phutil_require_module('phabricator', 'applications/flag/constants/color');
phutil_require_module('phabricator', 'applications/flag/query/flag');
phutil_require_module('phabricator', 'applications/maniphest/constants/priority');
phutil_require_module('phabricator', 'applications/maniphest/constants/status');
phutil_require_module('phabricator', 'applications/maniphest/constants/transactiontype');

View file

@ -27,7 +27,7 @@ final class PhabricatorObjectHandleData {
public function loadObjects() {
$types = array();
foreach ($this->phids as $phid) {
$type = $this->lookupType($phid);
$type = self::lookupType($phid);
$types[$type][] = $phid;
}
@ -95,7 +95,7 @@ final class PhabricatorObjectHandleData {
$types = array();
foreach ($this->phids as $phid) {
$type = $this->lookupType($phid);
$type = self::lookupType($phid);
$types[$type][] = $phid;
}
@ -493,7 +493,7 @@ final class PhabricatorObjectHandleData {
return $handles;
}
private function lookupType($phid) {
public static function lookupType($phid) {
$matches = null;
if (preg_match('/^PHID-([^-]{4})-/', $phid, $matches)) {
return $matches[1];

View file

@ -0,0 +1,44 @@
/**
* @provides phabricator-flag-css
*/
.phabricator-flag-icon {
padding: 8px;
background: transparent 0 0 no-repeat;
}
.phabricator-flag-color-0 {
background-image: url(/rsrc/image/icon/fatcow/flag_red.png);
}
.phabricator-flag-color-1 {
background-image: url(/rsrc/image/icon/fatcow/flag_orange.png);
}
.phabricator-flag-color-2 {
background-image: url(/rsrc/image/icon/fatcow/flag_yellow.png);
}
.phabricator-flag-color-3 {
background-image: url(/rsrc/image/icon/fatcow/flag_green.png);
}
.phabricator-flag-color-4 {
background-image: url(/rsrc/image/icon/fatcow/flag_blue.png);
}
.phabricator-flag-color-5 {
background-image: url(/rsrc/image/icon/fatcow/flag_pink.png);
}
.phabricator-flag-color-6 {
background-image: url(/rsrc/image/icon/fatcow/flag_purple.png);
}
.phabricator-flag-color-7 {
background-image: url(/rsrc/image/icon/fatcow/flag_finish.png);
}
.phabricator-flag-ghost {
background-image: url(/rsrc/image/icon/fatcow/flag_ghost.png);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 726 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 786 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 561 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 722 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 709 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 711 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 744 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 737 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 671 B