1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-23 15:22:41 +01:00

Phriction Documents can be moved

Summary: Refs T1575

Test Plan:
created plenty of pages, did all kind of stuff with them, moved them around, verified expected behaviour.

{F34453}

{F34455}

Reviewers: epriestley, chad, btrahan

Reviewed By: epriestley

CC: aran, epriestley, Korvin

Maniphest Tasks: T1575

Differential Revision: https://secure.phabricator.com/D5198
This commit is contained in:
Anh Nhan Nguyen 2013-03-06 13:12:04 -08:00 committed by epriestley
parent cd99850cb8
commit 911d02e2d1
8 changed files with 274 additions and 21 deletions

View file

@ -1497,6 +1497,7 @@ phutil_register_library_map(array(
'PhrictionEditController' => 'applications/phriction/controller/PhrictionEditController.php', 'PhrictionEditController' => 'applications/phriction/controller/PhrictionEditController.php',
'PhrictionHistoryController' => 'applications/phriction/controller/PhrictionHistoryController.php', 'PhrictionHistoryController' => 'applications/phriction/controller/PhrictionHistoryController.php',
'PhrictionListController' => 'applications/phriction/controller/PhrictionListController.php', 'PhrictionListController' => 'applications/phriction/controller/PhrictionListController.php',
'PhrictionMoveController' => 'applications/phriction/controller/PhrictionMoveController.php',
'PhrictionNewController' => 'applications/phriction/controller/PhrictionNewController.php', 'PhrictionNewController' => 'applications/phriction/controller/PhrictionNewController.php',
'PhrictionRemarkupRule' => 'applications/phriction/remarkup/PhrictionRemarkupRule.php', 'PhrictionRemarkupRule' => 'applications/phriction/remarkup/PhrictionRemarkupRule.php',
'PhrictionSearchIndexer' => 'applications/phriction/search/PhrictionSearchIndexer.php', 'PhrictionSearchIndexer' => 'applications/phriction/search/PhrictionSearchIndexer.php',
@ -3003,6 +3004,7 @@ phutil_register_library_map(array(
'PhrictionEditController' => 'PhrictionController', 'PhrictionEditController' => 'PhrictionController',
'PhrictionHistoryController' => 'PhrictionController', 'PhrictionHistoryController' => 'PhrictionController',
'PhrictionListController' => 'PhrictionController', 'PhrictionListController' => 'PhrictionController',
'PhrictionMoveController' => 'PhrictionController',
'PhrictionNewController' => 'PhrictionController', 'PhrictionNewController' => 'PhrictionController',
'PhrictionRemarkupRule' => 'PhutilRemarkupRule', 'PhrictionRemarkupRule' => 'PhutilRemarkupRule',
'PhrictionSearchIndexer' => 'PhabricatorSearchDocumentIndexer', 'PhrictionSearchIndexer' => 'PhabricatorSearchDocumentIndexer',

View file

@ -43,7 +43,7 @@ final class PhabricatorApplicationPhriction extends PhabricatorApplication {
'edit/(?:(?P<id>[1-9]\d*)/)?' => 'PhrictionEditController', 'edit/(?:(?P<id>[1-9]\d*)/)?' => 'PhrictionEditController',
'delete/(?P<id>[1-9]\d*)/' => 'PhrictionDeleteController', 'delete/(?P<id>[1-9]\d*)/' => 'PhrictionDeleteController',
'new/' => 'PhrictionNewController', 'new/' => 'PhrictionNewController',
'move/(?P<id>[1-9]\d*)/' => 'PhrictionMoveController', 'move/(?:(?P<id>[1-9]\d*)/)?' => 'PhrictionMoveController',
'preview/' => 'PhrictionDocumentPreviewController', 'preview/' => 'PhrictionDocumentPreviewController',
'diff/(?P<id>[1-9]\d*)/' => 'PhrictionDiffController', 'diff/(?P<id>[1-9]\d*)/' => 'PhrictionDiffController',

View file

@ -8,12 +8,16 @@ final class PhrictionActionConstants extends PhrictionConstants {
const ACTION_CREATE = 'create'; const ACTION_CREATE = 'create';
const ACTION_EDIT = 'edit'; const ACTION_EDIT = 'edit';
const ACTION_DELETE = 'delete'; const ACTION_DELETE = 'delete';
const ACTION_MOVE_AWAY = 'move to';
const ACTION_MOVE_HERE = 'move here';
public static function getActionPastTenseVerb($action) { public static function getActionPastTenseVerb($action) {
static $map = array( static $map = array(
self::ACTION_CREATE => 'created', self::ACTION_CREATE => 'created',
self::ACTION_EDIT => 'edited', self::ACTION_EDIT => 'edited',
self::ACTION_DELETE => 'deleted', self::ACTION_DELETE => 'deleted',
self::ACTION_MOVE_AWAY => 'moved a document to',
self::ACTION_MOVE_HERE => 'moved a document from',
); );
return idx($map, $action, "brazenly {$action}'d"); return idx($map, $action, "brazenly {$action}'d");

View file

@ -12,8 +12,9 @@ final class PhrictionDocumentStatus extends PhrictionConstants {
public static function getConduitConstant($const) { public static function getConduitConstant($const) {
static $map = array( static $map = array(
self::STATUS_EXISTS => 'exists', self::STATUS_EXISTS => 'exists',
self::STATUS_DELETED => 'deleted', self::STATUS_DELETED => 'deleted',
self::STATUS_MOVED => 'moved',
self::STATUS_STUB => 'stubbed', self::STATUS_STUB => 'stubbed',
); );

View file

@ -13,7 +13,6 @@ final class PhrictionDocumentController
} }
public function processRequest() { public function processRequest() {
$request = $this->getRequest(); $request = $this->getRequest();
$user = $request->getUser(); $user = $request->getUser();
@ -148,14 +147,43 @@ final class PhrictionDocumentController
pht('This document is empty. You can edit it to put some proper '. pht('This document is empty. You can edit it to put some proper '.
'content here.')); 'content here.'));
$core_content = $notice->render(); $core_content = $notice->render();
} else if ($doc_status == PhrictionDocumentStatus::STATUS_MOVED) {
$new_doc_id = $content->getChangeRef();
$new_doc = new PhrictionDocument();
$new_doc->load($new_doc_id);
$slug_uri = PhrictionDocument::getSlugURI($new_doc->getSlug());
$notice = new AphrontErrorView();
$notice->setSeverity(AphrontErrorView::SEVERITY_NOTICE);
$notice->setTitle(pht('Document Moved'));
$notice->appendChild(phutil_tag('p', array(),
pht('This document has been moved to %s. You can edit it to put new '.
'content here, or use history to revert to an earlier version.',
phutil_tag('a', array('href' => $slug_uri), $slug_uri))));
$core_content = $notice->render();
} else { } else {
throw new Exception("Unknown document status '{$doc_status}'!"); throw new Exception("Unknown document status '{$doc_status}'!");
} }
$move_notice = null;
if ($content->getChangeType() == PhrictionChangeType::CHANGE_MOVE_HERE) {
$from_doc_id = $content->getChangeRef();
$from_doc = id(new PhrictionDocument())->load($from_doc_id);
$slug_uri = PhrictionDocument::getSlugURI($from_doc->getSlug());
$move_notice = id(new AphrontErrorView())
->setSeverity(AphrontErrorView::SEVERITY_NOTICE)
->appendChild(pht('This document was moved from %s',
phutil_tag('a', array('href' => $slug_uri), $slug_uri)))
->render();
}
$page_content = hsprintf( $page_content = hsprintf(
'<div class="phriction-content">%s%s%s</div>', '<div class="phriction-content">%s%s%s%s</div>',
$index_link, $index_link,
$byline, $byline,
$move_notice,
$core_content); $core_content);
} }
@ -221,6 +249,13 @@ final class PhrictionDocumentController
->setHref('/phriction/edit/'.$document->getID().'/')); ->setHref('/phriction/edit/'.$document->getID().'/'));
if ($document->getStatus() == PhrictionDocumentStatus::STATUS_EXISTS) { if ($document->getStatus() == PhrictionDocumentStatus::STATUS_EXISTS) {
$action_view->addAction(
id(new PhabricatorActionView())
->setName(pht('Move Document'))
->setIcon('move')
->setHref('/phriction/move/'.$document->getID().'/')
->setWorkflow(true));
$action_view->addAction( $action_view->addAction(
id(new PhabricatorActionView()) id(new PhabricatorActionView())
->setName(pht('Delete Document')) ->setName(pht('Delete Document'))

View file

@ -115,9 +115,13 @@ final class PhrictionListController
case 'active': case 'active':
$data = queryfx_all( $data = queryfx_all(
$conn, $conn,
'SELECT * FROM %T WHERE status != %d ORDER BY id DESC LIMIT %d, %d', 'SELECT * FROM %T WHERE status NOT IN (%Ld) ORDER BY id DESC '.
'LIMIT %d, %d',
$document_dao->getTableName(), $document_dao->getTableName(),
PhrictionDocumentStatus::STATUS_DELETED, array(
PhrictionDocumentStatus::STATUS_DELETED,
PhrictionDocumentStatus::STATUS_MOVED,
),
$pager->getOffset(), $pager->getOffset(),
$pager->getPageSize() + 1); $pager->getPageSize() + 1);
break; break;

View file

@ -0,0 +1,188 @@
<?php
/**
* @group phriction
*/
final class PhrictionMoveController
extends PhrictionController {
private $id;
public function willProcessRequest(array $data) {
$this->id = idx($data, 'id');
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
if ($this->id) {
$document = id(new PhrictionDocument())->load($this->id);
} else {
$slug = PhabricatorSlug::normalize(
$request->getStr('slug'));
if (!$slug) {
return new Aphront404Response();
}
$document = id(new PhrictionDocument())->loadOneWhere(
'slug = %s',
$slug);
}
if (!$document) {
return new Aphront404Response();
}
if (!isset($slug)) {
$slug = $document->getSlug();
}
$target_slug = PhabricatorSlug::normalize(
$request->getStr('new-slug', $slug));
$submit_uri = $request->getRequestURI()->getPath();
$cancel_uri = PhrictionDocument::getSlugURI($slug);
$errors = array();
$error_view = null;
$e_url = null;
$e_block = false;
$disallowed_statuses = array(
PhrictionDocumentStatus::STATUS_DELETED, // Stupid
PhrictionDocumentStatus::STATUS_MOVED, // Plain stupid
);
if (in_array($document->getStatus(), $disallowed_statuses)) {
$error_view = new AphrontErrorView();
$error_view->setSeverity(AphrontErrorView::SEVERITY_ERROR);
$error_view->appendChild(pht('An already moved or deleted document '.
'can not be moved again.'));
$error_dialog = new AphrontDialogView();
$error_dialog->setUser($user);
$error_dialog->setTitle("");
$error_dialog->appendChild($error_view);
$error_dialog->addCancelButton($cancel_uri, pht('I understand'));
return id(new AphrontDialogResponse())->setDialog($error_dialog);
}
$content = id(new PhrictionContent())->load($document->getContentID());
if ($request->isFormPost() && !count($errors)) {
if (!count($errors)) { // First check if the target document exists
$target_document = id(new PhrictionDocument())->loadOneWhere(
'slug = %s',
$target_slug);
// Considering to overwrite existing docs? Nuke this!
if ($target_document && $target_document->getStatus() ==
PhrictionDocumentStatus::STATUS_EXISTS) {
$errors[] = pht('Can not overwrite existing target document.');
$e_url = pht('Already exists.');
}
}
if (!count($errors)) { // I like to move it, move it!
$from_editor = id(PhrictionDocumentEditor::newForSlug($slug))
->setActor($user)
->setTitle($content->getTitle())
->setContent($content->getContent())
->setDescription($content->getDescription());
$target_editor = id(PhrictionDocumentEditor::newForSlug(
$target_slug))
->setActor($user)
->setTitle($content->getTitle())
->setContent($content->getContent())
->setDescription($content->getDescription());
// Move it!
$target_editor->moveHere($document->getID());
// Retrieve the target doc directly from the editor
// No need to load it per Sql again
$target_document = $target_editor->getDocument();
$from_editor->moveAway($target_document->getID());
$redir_uri = PhrictionDocument::getSlugURI($target_document->getSlug());
return id(new AphrontRedirectResponse())->setURI($redir_uri);
}
}
if ($errors) {
$error_view = id(new AphrontErrorView())
->setTitle(pht('Form Errors'))
->setErrors($errors);
}
$descr_caption = $is_serious ? pht('A reason for the move.') :
pht('You better give a good reason for this.');
if ($request->isAjax()) {
$form = new AphrontFormLayoutView();
} else {
$form = new AphrontFormView();
$form->setAction($submit_uri)
// Append title so the user can verify that he's touching
// the right document
->appendChild(
id(new AphrontFormStaticControl())
->setLabel(pht('Title'))
->setValue($content->getTitle()));
}
$form
->setUser($user)
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('New URI'))
->setValue($target_slug)
->setError($e_url)
->setName('new-slug')
->setCaption(pht('The new location of the document.')))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Edit Notes'))
->setValue($content->getDescription())
->setError(null)
->setName('description')
->setCaption($descr_caption));
if ($request->isAjax()) {
$dialog = new AphrontDialogView();
$dialog->setUser($user);
$dialog->setTitle(pht('Move Document'));
$dialog->appendChild($form);
$dialog->setSubmitURI($submit_uri);
$dialog->addSubmitButton(pht('Move Document'));
$dialog->addCancelButton($cancel_uri);
return id(new AphrontDialogResponse())->setDialog($dialog);
} else {
$form->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($cancel_uri)
->setValue(pht('Move Document'))
->setDisabled($e_block));
$panel = id(new AphrontPanelView())
->setNoBackground()
->setHeader(pht('Move Phriction Document'))
->appendChild($form);
return $this->buildApplicationPage(
array(
$error_view,
$panel,
),
array(
'title' => pht('Move Document'),
'device' => true,
));
}
}
}

View file

@ -66,33 +66,44 @@ final class PhrictionDocumentEditor extends PhabricatorEditor {
return $this->document; return $this->document;
} }
public function delete() { public function moveAway($new_doc_id) {
$actor = $this->requireActor(); return $this->execute(
PhrictionChangeType::CHANGE_MOVE_AWAY, true, $new_doc_id);
}
// TODO: Should we do anything about deleting an already-deleted document? public function moveHere($old_doc_id) {
// We currently allow it. return $this->execute(
PhrictionChangeType::CHANGE_MOVE_HERE, false, $old_doc_id);
}
private function execute(
$change_type, $del_new_content = true, $doc_ref = null) {
$actor = $this->requireActor();
$document = $this->document; $document = $this->document;
$content = $this->content; $content = $this->content;
$new_content = $this->buildContentTemplate($document, $content); $new_content = $this->buildContentTemplate($document, $content);
$new_content->setChangeType($change_type);
$new_content->setChangeType(PhrictionChangeType::CHANGE_DELETE); if ($del_new_content) {
$new_content->setContent(''); $new_content->setContent('');
}
if ($doc_ref) {
$new_content->setChangeRef($doc_ref);
}
return $this->updateDocument($document, $content, $new_content); return $this->updateDocument($document, $content, $new_content);
} }
public function delete() {
return $this->execute(PhrictionChangeType::CHANGE_DELETE, true);
}
private function stub() { private function stub() {
$actor = $this->requireActor(); return $this->execute(PhrictionChangeType::CHANGE_STUB, true);
$document = $this->document;
$content = $this->content;
$new_content = $this->buildContentTemplate($document, $content);
$new_content->setChangeType(PhrictionChangeType::CHANGE_STUB);
$new_content->setContent('');
return $this->updateDocument($document, $content, $new_content);
} }
public function save() { public function save() {
@ -168,6 +179,14 @@ final class PhrictionDocumentEditor extends PhabricatorEditor {
$doc_status = PhrictionDocumentStatus::STATUS_STUB; $doc_status = PhrictionDocumentStatus::STATUS_STUB;
$feed_action = null; $feed_action = null;
break; break;
case PhrictionChangeType::CHANGE_MOVE_AWAY:
$doc_status = PhrictionDocumentStatus::STATUS_MOVED;
$feed_action = PhrictionActionConstants::ACTION_MOVE_AWAY;
break;
case PhrictionChangeType::CHANGE_MOVE_HERE:
$doc_status = PhrictionDocumentStatus::STATUS_EXISTS;
$feed_action = null;
break;
default: default:
throw new Exception( throw new Exception(
"Unsupported content change type '{$change_type}'!"); "Unsupported content change type '{$change_type}'!");