Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/phriction/controller/PhrictionDiffController.php b/src/applications/phriction/controller/PhrictionDiffController.php
index b66a3b3094..f443ad657b 100644
--- a/src/applications/phriction/controller/PhrictionDiffController.php
+++ b/src/applications/phriction/controller/PhrictionDiffController.php
@@ -1,257 +1,258 @@
<?php
final class PhrictionDiffController extends PhrictionController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$document = id(new PhrictionDocumentQuery())
->setViewer($viewer)
->withIDs(array($id))
->needContent(true)
->executeOne();
if (!$document) {
return new Aphront404Response();
}
$current = $document->getContent();
$l = $request->getInt('l');
$r = $request->getInt('r');
$ref = $request->getStr('ref');
if ($ref) {
list($l, $r) = explode(',', $ref);
}
- $content = id(new PhrictionContent())->loadAllWhere(
- 'documentID = %d AND version IN (%Ld)',
- $document->getID(),
- array($l, $r));
+ $content = id(new PhrictionContentQuery())
+ ->setViewer($viewer)
+ ->withDocumentPHIDs(array($document->getPHID()))
+ ->withVersions(array($l, $r))
+ ->execute();
$content = mpull($content, null, 'getVersion');
$content_l = idx($content, $l, null);
$content_r = idx($content, $r, null);
if (!$content_l || !$content_r) {
return new Aphront404Response();
}
$text_l = $content_l->getContent();
$text_r = $content_r->getContent();
$diff_view = id(new PhabricatorApplicationTransactionTextDiffDetailView())
->setOldText($text_l)
->setNewText($text_r);
$changes = id(new PHUIObjectBoxView())
->setHeaderText(pht('Content Changes'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild(
phutil_tag(
'div',
array(
'class' => 'prose-diff-frame',
),
$diff_view));
require_celerity_resource('phriction-document-css');
$slug = $document->getSlug();
$revert_l = $this->renderRevertButton($content_l, $current);
$revert_r = $this->renderRevertButton($content_r, $current);
$crumbs = $this->buildApplicationCrumbs();
$crumb_views = $this->renderBreadcrumbs($slug);
foreach ($crumb_views as $view) {
$crumbs->addCrumb($view);
}
$crumbs->addTextCrumb(
pht('History'),
PhrictionDocument::getSlugURI($slug, 'history'));
$title = pht('Version %s vs %s', $l, $r);
$header = id(new PHUIHeaderView())
->setHeader($title)
->setHeaderIcon('fa-history');
$crumbs->addTextCrumb($title, $request->getRequestURI());
$comparison_table = $this->renderComparisonTable(
array(
$content_r,
$content_l,
));
$navigation_table = null;
if ($l + 1 == $r) {
$nav_l = ($l > 1);
$nav_r = ($r != $current->getVersion());
$uri = $request->getRequestURI();
if ($nav_l) {
$link_l = phutil_tag(
'a',
array(
'href' => $uri->alter('l', $l - 1)->alter('r', $r - 1),
'class' => 'button button-grey',
),
pht("\xC2\xAB Previous Change"));
} else {
$link_l = phutil_tag(
'a',
array(
'href' => '#',
'class' => 'button button-grey disabled',
),
pht('Original Change'));
}
$link_r = null;
if ($nav_r) {
$link_r = phutil_tag(
'a',
array(
'href' => $uri->alter('l', $l + 1)->alter('r', $r + 1),
'class' => 'button button-grey',
),
pht("Next Change \xC2\xBB"));
} else {
$link_r = phutil_tag(
'a',
array(
'href' => '#',
'class' => 'button button-grey disabled',
),
pht('Most Recent Change'));
}
$navigation_table = phutil_tag(
'table',
array('class' => 'phriction-history-nav-table'),
phutil_tag('tr', array(), array(
phutil_tag('td', array('class' => 'nav-prev'), $link_l),
phutil_tag('td', array('class' => 'nav-next'), $link_r),
)));
}
$output = hsprintf(
'<div class="phriction-document-history-diff">'.
'%s%s'.
'<table class="phriction-revert-table">'.
'<tr><td>%s</td><td>%s</td>'.
'</table>'.
'</div>',
$comparison_table->render(),
$navigation_table,
$revert_l,
$revert_r);
$object_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Edits'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($output);
$crumbs->setBorder(true);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(array(
$object_box,
$changes,
));
return $this->newPage()
->setTitle(pht('Document History'))
->setCrumbs($crumbs)
->appendChild($view);
}
private function renderRevertButton(
PhrictionContent $content,
PhrictionContent $current) {
$document_id = $content->getDocumentID();
$version = $content->getVersion();
$hidden_statuses = array(
PhrictionChangeType::CHANGE_DELETE => true, // Silly
PhrictionChangeType::CHANGE_MOVE_AWAY => true, // Plain silly
PhrictionChangeType::CHANGE_STUB => true, // Utterly silly
);
if (isset($hidden_statuses[$content->getChangeType()])) {
// Don't show an edit/revert button for changes which deleted, moved or
// stubbed the content since it's silly.
return null;
}
if ($content->getID() == $current->getID()) {
return phutil_tag(
'a',
array(
'href' => '/phriction/edit/'.$document_id.'/',
'class' => 'button button-grey',
),
pht('Edit Current Version'));
}
return phutil_tag(
'a',
array(
'href' => '/phriction/edit/'.$document_id.'/?revert='.$version,
'class' => 'button button-grey',
),
pht('Revert to Version %s...', $version));
}
private function renderComparisonTable(array $content) {
assert_instances_of($content, 'PhrictionContent');
$viewer = $this->getViewer();
$phids = mpull($content, 'getAuthorPHID');
$handles = $this->loadViewerHandles($phids);
$list = new PHUIObjectItemListView();
$first = true;
foreach ($content as $c) {
$author = $handles[$c->getAuthorPHID()]->renderLink();
$item = id(new PHUIObjectItemView())
->setHeader(pht('%s by %s, %s',
PhrictionChangeType::getChangeTypeLabel($c->getChangeType()),
$author,
pht('Version %s', $c->getVersion())))
->addAttribute(pht('%s %s',
phabricator_date($c->getDateCreated(), $viewer),
phabricator_time($c->getDateCreated(), $viewer)));
if ($c->getDescription()) {
$item->addAttribute($c->getDescription());
}
if ($first == true) {
$item->setStatusIcon('fa-file green');
$first = false;
} else {
$item->setStatusIcon('fa-file red');
}
$list->addItem($item);
}
return $list;
}
}
diff --git a/src/applications/phriction/controller/PhrictionDocumentController.php b/src/applications/phriction/controller/PhrictionDocumentController.php
index a5189cc72c..8377cf7d29 100644
--- a/src/applications/phriction/controller/PhrictionDocumentController.php
+++ b/src/applications/phriction/controller/PhrictionDocumentController.php
@@ -1,495 +1,498 @@
<?php
final class PhrictionDocumentController
extends PhrictionController {
private $slug;
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$this->slug = $request->getURIData('slug');
$slug = PhabricatorSlug::normalize($this->slug);
if ($slug != $this->slug) {
$uri = PhrictionDocument::getSlugURI($slug);
// Canonicalize pages to their one true URI.
return id(new AphrontRedirectResponse())->setURI($uri);
}
require_celerity_resource('phriction-document-css');
- $document = id(new PhrictionDocumentQuery())
- ->setViewer($viewer)
- ->withSlugs(array($slug))
- ->executeOne();
-
$version_note = null;
$core_content = '';
$move_notice = '';
$properties = null;
$content = null;
$toc = null;
+ $document = id(new PhrictionDocumentQuery())
+ ->setViewer($viewer)
+ ->withSlugs(array($slug))
+ ->needContent(true)
+ ->executeOne();
if (!$document) {
$document = PhrictionDocument::initializeNewDocument($viewer, $slug);
if ($slug == '/') {
$title = pht('Welcome to Phriction');
$subtitle = pht('Phriction is a simple and easy to use wiki for '.
'keeping track of documents and their changes.');
$page_title = pht('Welcome');
$create_text = pht('Edit this Document');
} else {
$title = pht('No Document Here');
$subtitle = pht('There is no document here, but you may create it.');
$page_title = pht('Page Not Found');
$create_text = pht('Create this Document');
}
$create_uri = '/phriction/edit/?slug='.$slug;
$create_button = id(new PHUIButtonView())
->setTag('a')
->setText($create_text)
->setHref($create_uri)
->setColor(PHUIButtonView::GREEN);
$core_content = id(new PHUIBigInfoView())
->setIcon('fa-book')
->setTitle($title)
->setDescription($subtitle)
->addAction($create_button);
} else {
$version = $request->getInt('v');
if ($version) {
- $content = id(new PhrictionContent())->loadOneWhere(
- 'documentID = %d AND version = %d',
- $document->getID(),
- $version);
+ $content = id(new PhrictionContentQuery())
+ ->setViewer($viewer)
+ ->withDocumentPHIDs(array($document->getPHID()))
+ ->withVersions(array($version))
+ ->executeOne();
if (!$content) {
return new Aphront404Response();
}
if ($content->getID() != $document->getContentID()) {
- $vdate = phabricator_datetime($content->getDateCreated(), $viewer);
- $version_note = new PHUIInfoView();
- $version_note->setSeverity(PHUIInfoView::SEVERITY_NOTICE);
- $version_note->appendChild(
- pht('You are viewing an older version of this document, as it '.
- 'appeared on %s.', $vdate));
+ $version_note = id(new PHUIInfoView())
+ ->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
+ ->appendChild(
+ pht(
+ 'You are viewing an older version of this document, as it '.
+ 'appeared on %s.',
+ phabricator_datetime($content->getDateCreated(), $viewer)));
}
} else {
- $content = id(new PhrictionContent())->load($document->getContentID());
+ $content = $document->getContent();
}
+
$page_title = $content->getTitle();
$properties = $this
->buildPropertyListView($document, $content, $slug);
$doc_status = $document->getStatus();
$current_status = $content->getChangeType();
if ($current_status == PhrictionChangeType::CHANGE_EDIT ||
$current_status == PhrictionChangeType::CHANGE_MOVE_HERE) {
$remarkup_view = $content->newRemarkupView($viewer);
$core_content = $remarkup_view->render();
$toc = $remarkup_view->getTableOfContents();
$toc = $this->getToc($toc);
} else if ($current_status == PhrictionChangeType::CHANGE_DELETE) {
$notice = new PHUIInfoView();
$notice->setSeverity(PHUIInfoView::SEVERITY_NOTICE);
$notice->setTitle(pht('Document Deleted'));
$notice->appendChild(
pht('This document has been deleted. You can edit it to put new '.
'content here, or use history to revert to an earlier version.'));
$core_content = $notice->render();
} else if ($current_status == PhrictionChangeType::CHANGE_STUB) {
$notice = new PHUIInfoView();
$notice->setSeverity(PHUIInfoView::SEVERITY_NOTICE);
$notice->setTitle(pht('Empty Document'));
$notice->appendChild(
pht('This document is empty. You can edit it to put some proper '.
'content here.'));
$core_content = $notice->render();
} else if ($current_status == PhrictionChangeType::CHANGE_MOVE_AWAY) {
$new_doc_id = $content->getChangeRef();
$slug_uri = null;
// If the new document exists and the viewer can see it, provide a link
// to it. Otherwise, render a generic message.
$new_docs = id(new PhrictionDocumentQuery())
->setViewer($viewer)
->withIDs(array($new_doc_id))
->execute();
if ($new_docs) {
$new_doc = head($new_docs);
$slug_uri = PhrictionDocument::getSlugURI($new_doc->getSlug());
}
$notice = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_NOTICE);
if ($slug_uri) {
$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))));
} else {
$notice->appendChild(
phutil_tag(
'p',
array(),
pht(
'This document has been moved. You can edit it to put new '.
'content here, or use history to revert to an earlier '.
'version.')));
}
$core_content = $notice->render();
} else {
throw new Exception(pht("Unknown document status '%s'!", $doc_status));
}
$move_notice = null;
if ($current_status == PhrictionChangeType::CHANGE_MOVE_HERE) {
$from_doc_id = $content->getChangeRef();
$slug_uri = null;
// If the old document exists and is visible, provide a link to it.
$from_docs = id(new PhrictionDocumentQuery())
->setViewer($viewer)
->withIDs(array($from_doc_id))
->execute();
if ($from_docs) {
$from_doc = head($from_docs);
$slug_uri = PhrictionDocument::getSlugURI($from_doc->getSlug());
}
$move_notice = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_NOTICE);
if ($slug_uri) {
$move_notice->appendChild(
pht(
'This document was moved from %s.',
phutil_tag('a', array('href' => $slug_uri), $slug_uri)));
} else {
// Render this for consistency, even though it's a bit silly.
$move_notice->appendChild(
pht('This document was moved from elsewhere.'));
}
}
}
$children = $this->renderDocumentChildren($slug);
$actions = $this->buildActionView($viewer, $document);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->setBorder(true);
$crumb_views = $this->renderBreadcrumbs($slug);
foreach ($crumb_views as $view) {
$crumbs->addCrumb($view);
}
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setPolicyObject($document)
->setHeader($page_title)
->setActionList($actions);
if ($content) {
$header->setEpoch($content->getDateCreated());
}
$prop_list = null;
if ($properties) {
$prop_list = new PHUIPropertyGroupView();
$prop_list->addPropertyList($properties);
}
$prop_list = phutil_tag_div('phui-document-view-pro-box', $prop_list);
$page_content = id(new PHUIDocumentViewPro())
->setHeader($header)
->setToc($toc)
->appendChild(
array(
$version_note,
$move_notice,
$core_content,
));
return $this->newPage()
->setTitle($page_title)
->setCrumbs($crumbs)
->setPageObjectPHIDs(array($document->getPHID()))
->appendChild(array(
$page_content,
$prop_list,
$children,
));
}
private function buildPropertyListView(
PhrictionDocument $document,
PhrictionContent $content,
$slug) {
$viewer = $this->getRequest()->getUser();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($document);
$view->addProperty(
pht('Last Author'),
$viewer->renderHandle($content->getAuthorPHID()));
return $view;
}
private function buildActionView(
PhabricatorUser $viewer,
PhrictionDocument $document) {
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$document,
PhabricatorPolicyCapability::CAN_EDIT);
$slug = PhabricatorSlug::normalize($this->slug);
$action_view = id(new PhabricatorActionListView())
->setUser($viewer)
->setObject($document);
if (!$document->getID()) {
return $action_view->addAction(
id(new PhabricatorActionView())
->setName(pht('Create This Document'))
->setIcon('fa-plus-square')
->setHref('/phriction/edit/?slug='.$slug));
}
$action_view->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Document'))
->setDisabled(!$can_edit)
->setIcon('fa-pencil')
->setHref('/phriction/edit/'.$document->getID().'/'));
if ($document->getStatus() == PhrictionDocumentStatus::STATUS_EXISTS) {
$action_view->addAction(
id(new PhabricatorActionView())
->setName(pht('Move Document'))
->setDisabled(!$can_edit)
->setIcon('fa-arrows')
->setHref('/phriction/move/'.$document->getID().'/')
->setWorkflow(true));
$action_view->addAction(
id(new PhabricatorActionView())
->setName(pht('Delete Document'))
->setDisabled(!$can_edit)
->setIcon('fa-times')
->setHref('/phriction/delete/'.$document->getID().'/')
->setWorkflow(true));
}
$action_view->addAction(
id(new PhabricatorActionView())
->setName(pht('View History'))
->setIcon('fa-list')
->setHref(PhrictionDocument::getSlugURI($slug, 'history')));
$print_uri = PhrictionDocument::getSlugURI($slug).'?__print__=1';
$action_view->addAction(
id(new PhabricatorActionView())
->setName(pht('Printable Page'))
->setIcon('fa-print')
->setOpenInNewWindow(true)
->setHref($print_uri));
return $action_view;
}
private function renderDocumentChildren($slug) {
$d_child = PhabricatorSlug::getDepth($slug) + 1;
$d_grandchild = PhabricatorSlug::getDepth($slug) + 2;
$limit = 250;
$query = id(new PhrictionDocumentQuery())
->setViewer($this->getRequest()->getUser())
->withDepths(array($d_child, $d_grandchild))
->withSlugPrefix($slug == '/' ? '' : $slug)
->withStatuses(array(
PhrictionDocumentStatus::STATUS_EXISTS,
PhrictionDocumentStatus::STATUS_STUB,
))
->setLimit($limit)
->setOrder(PhrictionDocumentQuery::ORDER_HIERARCHY)
->needContent(true);
$children = $query->execute();
if (!$children) {
return;
}
// We're going to render in one of three modes to try to accommodate
// different information scales:
//
// - If we found fewer than $limit rows, we know we have all the children
// and grandchildren and there aren't all that many. We can just render
// everything.
// - If we found $limit rows but the results included some grandchildren,
// we just throw them out and render only the children, as we know we
// have them all.
// - If we found $limit rows and the results have no grandchildren, we
// have a ton of children. Render them and then let the user know that
// this is not an exhaustive list.
if (count($children) == $limit) {
$more_children = true;
foreach ($children as $child) {
if ($child->getDepth() == $d_grandchild) {
$more_children = false;
}
}
$show_grandchildren = false;
} else {
$show_grandchildren = true;
$more_children = false;
}
$children_dicts = array();
$grandchildren_dicts = array();
foreach ($children as $key => $child) {
$child_dict = array(
'slug' => $child->getSlug(),
'depth' => $child->getDepth(),
'title' => $child->getContent()->getTitle(),
);
if ($child->getDepth() == $d_child) {
$children_dicts[] = $child_dict;
continue;
} else {
unset($children[$key]);
if ($show_grandchildren) {
$ancestors = PhabricatorSlug::getAncestry($child->getSlug());
$grandchildren_dicts[end($ancestors)][] = $child_dict;
}
}
}
// Fill in any missing children.
$known_slugs = mpull($children, null, 'getSlug');
foreach ($grandchildren_dicts as $slug => $ignored) {
if (empty($known_slugs[$slug])) {
$children_dicts[] = array(
'slug' => $slug,
'depth' => $d_child,
'title' => PhabricatorSlug::getDefaultTitle($slug),
'empty' => true,
);
}
}
$children_dicts = isort($children_dicts, 'title');
$list = array();
foreach ($children_dicts as $child) {
$list[] = hsprintf('<li class="remarkup-list-item">');
$list[] = $this->renderChildDocumentLink($child);
$grand = idx($grandchildren_dicts, $child['slug'], array());
if ($grand) {
$list[] = hsprintf('<ul class="remarkup-list">');
foreach ($grand as $grandchild) {
$list[] = hsprintf('<li class="remarkup-list-item">');
$list[] = $this->renderChildDocumentLink($grandchild);
$list[] = hsprintf('</li>');
}
$list[] = hsprintf('</ul>');
}
$list[] = hsprintf('</li>');
}
if ($more_children) {
$list[] = phutil_tag(
'li',
array(
'class' => 'remarkup-list-item',
),
pht('More...'));
}
$header = id(new PHUIHeaderView())
->setHeader(pht('Document Hierarchy'));
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild(phutil_tag(
'div',
array(
'class' => 'phabricator-remarkup mlt mlb',
),
phutil_tag(
'ul',
array(
'class' => 'remarkup-list',
),
$list)));
return phutil_tag_div('phui-document-view-pro-box', $box);
}
private function renderChildDocumentLink(array $info) {
$title = nonempty($info['title'], pht('(Untitled Document)'));
$item = phutil_tag(
'a',
array(
'href' => PhrictionDocument::getSlugURI($info['slug']),
),
$title);
if (isset($info['empty'])) {
$item = phutil_tag('em', array(), $item);
}
return $item;
}
protected function getDocumentSlug() {
return $this->slug;
}
protected function getToc($toc) {
if ($toc) {
$toc = phutil_tag_div('phui-document-toc-content', array(
phutil_tag_div(
'phui-document-toc-header',
pht('Contents')),
$toc,
));
}
return $toc;
}
}
diff --git a/src/applications/phriction/controller/PhrictionEditController.php b/src/applications/phriction/controller/PhrictionEditController.php
index 0ac3fc35bb..e7e2b40d87 100644
--- a/src/applications/phriction/controller/PhrictionEditController.php
+++ b/src/applications/phriction/controller/PhrictionEditController.php
@@ -1,324 +1,325 @@
<?php
final class PhrictionEditController
extends PhrictionController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$current_version = null;
if ($id) {
$is_new = false;
$document = id(new PhrictionDocumentQuery())
->setViewer($viewer)
->withIDs(array($id))
->needContent(true)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$document) {
return new Aphront404Response();
}
$current_version = $document->getContent()->getVersion();
$revert = $request->getInt('revert');
if ($revert) {
- $content = id(new PhrictionContent())->loadOneWhere(
- 'documentID = %d AND version = %d',
- $document->getID(),
- $revert);
+ $content = id(new PhrictionContentQuery())
+ ->setViewer($viewer)
+ ->withDocumentPHIDs(array($document->getPHID()))
+ ->withVersions(array($revert))
+ ->executeOne();
if (!$content) {
return new Aphront404Response();
}
} else {
$content = $document->getContent();
}
} else {
$slug = $request->getStr('slug');
$slug = PhabricatorSlug::normalize($slug);
if (!$slug) {
return new Aphront404Response();
}
$document = id(new PhrictionDocumentQuery())
->setViewer($viewer)
->withSlugs(array($slug))
->needContent(true)
->executeOne();
if ($document) {
$content = $document->getContent();
$current_version = $content->getVersion();
$is_new = false;
} else {
$document = PhrictionDocument::initializeNewDocument($viewer, $slug);
$content = $document->getContent();
$is_new = true;
}
}
if ($request->getBool('nodraft')) {
$draft = null;
$draft_key = null;
} else {
if ($document->getPHID()) {
$draft_key = $document->getPHID().':'.$content->getVersion();
} else {
$draft_key = 'phriction:'.$content->getSlug();
}
$draft = id(new PhabricatorDraft())->loadOneWhere(
'authorPHID = %s AND draftKey = %s',
$viewer->getPHID(),
$draft_key);
}
if ($draft &&
strlen($draft->getDraft()) &&
($draft->getDraft() != $content->getContent())) {
$content_text = $draft->getDraft();
$discard = phutil_tag(
'a',
array(
'href' => $request->getRequestURI()->alter('nodraft', true),
),
pht('discard this draft'));
$draft_note = new PHUIInfoView();
$draft_note->setSeverity(PHUIInfoView::SEVERITY_NOTICE);
$draft_note->setTitle(pht('Recovered Draft'));
$draft_note->appendChild(
pht('Showing a saved draft of your edits, you can %s.', $discard));
} else {
$content_text = $content->getContent();
$draft_note = null;
}
require_celerity_resource('phriction-document-css');
$e_title = true;
$e_content = true;
$validation_exception = null;
$notes = null;
$title = $content->getTitle();
$overwrite = false;
$v_cc = PhabricatorSubscribersQuery::loadSubscribersForPHID(
$document->getPHID());
if ($is_new) {
$v_projects = array();
} else {
$v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs(
$document->getPHID(),
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
$v_projects = array_reverse($v_projects);
}
if ($request->isFormPost()) {
$title = $request->getStr('title');
$content_text = $request->getStr('content');
$notes = $request->getStr('description');
$current_version = $request->getInt('contentVersion');
$v_view = $request->getStr('viewPolicy');
$v_edit = $request->getStr('editPolicy');
$v_cc = $request->getArr('cc');
$v_projects = $request->getArr('projects');
$xactions = array();
$xactions[] = id(new PhrictionTransaction())
->setTransactionType(PhrictionDocumentTitleTransaction::TRANSACTIONTYPE)
->setNewValue($title);
$xactions[] = id(new PhrictionTransaction())
->setTransactionType(
PhrictionDocumentContentTransaction::TRANSACTIONTYPE)
->setNewValue($content_text);
$xactions[] = id(new PhrictionTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY)
->setNewValue($v_view);
$xactions[] = id(new PhrictionTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY)
->setNewValue($v_edit);
$xactions[] = id(new PhrictionTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS)
->setNewValue(array('=' => $v_cc));
$proj_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
$xactions[] = id(new PhrictionTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue('edge:type', $proj_edge_type)
->setNewValue(array('=' => array_fuse($v_projects)));
$editor = id(new PhrictionTransactionEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true)
->setDescription($notes)
->setProcessContentVersionError(!$request->getBool('overwrite'))
->setContentVersion($current_version);
try {
$editor->applyTransactions($document, $xactions);
if ($draft) {
$draft->delete();
}
$uri = PhrictionDocument::getSlugURI($document->getSlug());
return id(new AphrontRedirectResponse())->setURI($uri);
} catch (PhabricatorApplicationTransactionValidationException $ex) {
$validation_exception = $ex;
$e_title = nonempty(
$ex->getShortMessage(
PhrictionDocumentTitleTransaction::TRANSACTIONTYPE),
true);
$e_content = nonempty(
$ex->getShortMessage(
PhrictionDocumentContentTransaction::TRANSACTIONTYPE),
true);
// if we're not supposed to process the content version error, then
// overwrite that content...!
if (!$editor->getProcessContentVersionError()) {
$overwrite = true;
}
$document->setViewPolicy($v_view);
$document->setEditPolicy($v_edit);
}
}
if ($document->getID()) {
$page_title = pht('Edit Document: %s', $content->getTitle());
if ($overwrite) {
$submit_button = pht('Overwrite Changes');
} else {
$submit_button = pht('Save Changes');
}
} else {
$submit_button = pht('Create Document');
$page_title = pht('Create Document');
}
$uri = $document->getSlug();
$uri = PhrictionDocument::getSlugURI($uri);
$uri = PhabricatorEnv::getProductionURI($uri);
$cancel_uri = PhrictionDocument::getSlugURI($document->getSlug());
$policies = id(new PhabricatorPolicyQuery())
->setViewer($viewer)
->setObject($document)
->execute();
$view_capability = PhabricatorPolicyCapability::CAN_VIEW;
$edit_capability = PhabricatorPolicyCapability::CAN_EDIT;
$form = id(new AphrontFormView())
->setUser($viewer)
->addHiddenInput('slug', $document->getSlug())
->addHiddenInput('nodraft', $request->getBool('nodraft'))
->addHiddenInput('contentVersion', $current_version)
->addHiddenInput('overwrite', $overwrite)
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Title'))
->setValue($title)
->setError($e_title)
->setName('title'))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel(pht('URI'))
->setValue($uri))
->appendChild(
id(new PhabricatorRemarkupControl())
->setLabel(pht('Content'))
->setValue($content_text)
->setError($e_content)
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL)
->setName('content')
->setID('document-textarea')
->setUser($viewer))
->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Tags'))
->setName('projects')
->setValue($v_projects)
->setDatasource(new PhabricatorProjectDatasource()))
->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Subscribers'))
->setName('cc')
->setValue($v_cc)
->setUser($viewer)
->setDatasource(new PhabricatorMetaMTAMailableDatasource()))
->appendChild(
id(new AphrontFormPolicyControl())
->setName('viewPolicy')
->setPolicyObject($document)
->setCapability($view_capability)
->setPolicies($policies)
->setCaption(
$document->describeAutomaticCapability($view_capability)))
->appendChild(
id(new AphrontFormPolicyControl())
->setName('editPolicy')
->setPolicyObject($document)
->setCapability($edit_capability)
->setPolicies($policies)
->setCaption(
$document->describeAutomaticCapability($edit_capability)))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Edit Notes'))
->setValue($notes)
->setError(null)
->setName('description'))
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($cancel_uri)
->setValue($submit_button));
$form_box = id(new PHUIObjectBoxView())
->setHeaderText($page_title)
->setValidationException($validation_exception)
->setBackground(PHUIObjectBoxView::WHITE_CONFIG)
->setForm($form);
$preview = id(new PHUIRemarkupPreviewPanel())
->setHeader($content->getTitle())
->setPreviewURI('/phriction/preview/'.$document->getSlug())
->setControlID('document-textarea')
->setPreviewType(PHUIRemarkupPreviewPanel::DOCUMENT);
$crumbs = $this->buildApplicationCrumbs();
if ($document->getID()) {
$crumbs->addTextCrumb(
$content->getTitle(),
PhrictionDocument::getSlugURI($document->getSlug()));
$crumbs->addTextCrumb(pht('Edit'));
} else {
$crumbs->addTextCrumb(pht('Create'));
}
$crumbs->setBorder(true);
$view = id(new PHUITwoColumnView())
->setFooter(array(
$draft_note,
$form_box,
$preview,
));
return $this->newPage()
->setTitle($page_title)
->setCrumbs($crumbs)
->appendChild($view);
}
}
diff --git a/src/applications/phriction/query/PhrictionContentQuery.php b/src/applications/phriction/query/PhrictionContentQuery.php
index 0ab132abdd..053f40cf9c 100644
--- a/src/applications/phriction/query/PhrictionContentQuery.php
+++ b/src/applications/phriction/query/PhrictionContentQuery.php
@@ -1,115 +1,128 @@
<?php
final class PhrictionContentQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $documentPHIDs;
+ private $versions;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withDocumentPHIDs(array $phids) {
$this->documentPHIDs = $phids;
return $this;
}
+ public function withVersions(array $versions) {
+ $this->versions = $versions;
+ return $this;
+ }
+
public function newResultObject() {
return new PhrictionContent();
}
protected function loadPage() {
return $this->loadStandardPage($this->newResultObject());
}
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
if ($this->ids !== null) {
$where[] = qsprintf(
$conn,
'c.id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn,
'c.phid IN (%Ls)',
$this->phids);
}
+ if ($this->versions !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'version IN (%Ld)',
+ $this->versions);
+ }
+
if ($this->documentPHIDs !== null) {
$where[] = qsprintf(
$conn,
'd.phid IN (%Ls)',
$this->documentPHIDs);
}
return $where;
}
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
$joins = parent::buildJoinClauseParts($conn);
if ($this->shouldJoinDocumentTable()) {
$joins[] = qsprintf(
$conn,
'JOIN %T d ON d.id = c.documentID',
id(new PhrictionDocument())->getTableName());
}
return $joins;
}
protected function willFilterPage(array $contents) {
$document_ids = mpull($contents, 'getDocumentID');
$documents = id(new PhrictionDocumentQuery())
->setViewer($this->getViewer())
->setParentQuery($this)
->withIDs($document_ids)
->execute();
$documents = mpull($documents, null, 'getID');
foreach ($contents as $key => $content) {
$document_id = $content->getDocumentID();
$document = idx($documents, $document_id);
if (!$document) {
unset($contents[$key]);
$this->didRejectResult($content);
continue;
}
$content->attachDocument($document);
}
return $contents;
}
private function shouldJoinDocumentTable() {
if ($this->documentPHIDs !== null) {
return true;
}
return false;
}
protected function getPrimaryTableAlias() {
return 'c';
}
public function getQueryApplicationClass() {
return 'PhabricatorPhrictionApplication';
}
}
diff --git a/src/applications/phriction/query/PhrictionDocumentQuery.php b/src/applications/phriction/query/PhrictionDocumentQuery.php
index 2736d825b5..b5118146d3 100644
--- a/src/applications/phriction/query/PhrictionDocumentQuery.php
+++ b/src/applications/phriction/query/PhrictionDocumentQuery.php
@@ -1,322 +1,325 @@
<?php
final class PhrictionDocumentQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $slugs;
private $depths;
private $slugPrefix;
private $statuses;
private $needContent;
private $status = 'status-any';
const STATUS_ANY = 'status-any';
const STATUS_OPEN = 'status-open';
const STATUS_NONSTUB = 'status-nonstub';
const ORDER_HIERARCHY = 'order-hierarchy';
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withSlugs(array $slugs) {
$this->slugs = $slugs;
return $this;
}
public function withDepths(array $depths) {
$this->depths = $depths;
return $this;
}
public function withSlugPrefix($slug_prefix) {
$this->slugPrefix = $slug_prefix;
return $this;
}
public function withStatuses(array $statuses) {
$this->statuses = $statuses;
return $this;
}
public function withStatus($status) {
$this->status = $status;
return $this;
}
public function needContent($need_content) {
$this->needContent = $need_content;
return $this;
}
protected function loadPage() {
return $this->loadStandardPage($this->newResultObject());
}
public function newResultObject() {
return new PhrictionDocument();
}
protected function willFilterPage(array $documents) {
if ($documents) {
$ancestor_slugs = array();
foreach ($documents as $key => $document) {
$document_slug = $document->getSlug();
foreach (PhabricatorSlug::getAncestry($document_slug) as $ancestor) {
$ancestor_slugs[$ancestor][] = $key;
}
}
if ($ancestor_slugs) {
$table = new PhrictionDocument();
$conn_r = $table->establishConnection('r');
$ancestors = queryfx_all(
$conn_r,
'SELECT * FROM %T WHERE slug IN (%Ls)',
$document->getTableName(),
array_keys($ancestor_slugs));
$ancestors = $table->loadAllFromArray($ancestors);
$ancestors = mpull($ancestors, null, 'getSlug');
foreach ($ancestor_slugs as $ancestor_slug => $document_keys) {
$ancestor = idx($ancestors, $ancestor_slug);
foreach ($document_keys as $document_key) {
$documents[$document_key]->attachAncestor(
$ancestor_slug,
$ancestor);
}
}
}
}
// To view a Phriction document, you must also be able to view all of the
// ancestor documents. Filter out documents which have ancestors that are
// not visible.
$document_map = array();
foreach ($documents as $document) {
$document_map[$document->getSlug()] = $document;
foreach ($document->getAncestors() as $key => $ancestor) {
if ($ancestor) {
$document_map[$key] = $ancestor;
}
}
}
$filtered_map = $this->applyPolicyFilter(
$document_map,
array(PhabricatorPolicyCapability::CAN_VIEW));
// Filter all of the documents where a parent is not visible.
foreach ($documents as $document_key => $document) {
// If the document itself is not visible, filter it.
if (!isset($filtered_map[$document->getSlug()])) {
$this->didRejectResult($documents[$document_key]);
unset($documents[$document_key]);
continue;
}
// If an ancestor exists but is not visible, filter the document.
foreach ($document->getAncestors() as $ancestor_key => $ancestor) {
if (!$ancestor) {
continue;
}
if (!isset($filtered_map[$ancestor_key])) {
$this->didRejectResult($documents[$document_key]);
unset($documents[$document_key]);
break;
}
}
}
if (!$documents) {
return $documents;
}
if ($this->needContent) {
- $contents = id(new PhrictionContent())->loadAllWhere(
- 'id IN (%Ld)',
- mpull($documents, 'getContentID'));
+ $contents = id(new PhrictionContentQuery())
+ ->setViewer($this->getViewer())
+ ->setParentQuery($this)
+ ->withIDs(mpull($documents, 'getContentID'))
+ ->execute();
+ $contents = mpull($contents, null, 'getID');
foreach ($documents as $key => $document) {
$content_id = $document->getContentID();
if (empty($contents[$content_id])) {
unset($documents[$key]);
continue;
}
$document->attachContent($contents[$content_id]);
}
}
return $documents;
}
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
$joins = parent::buildJoinClauseParts($conn);
if ($this->getOrderVector()->containsKey('updated')) {
$content_dao = new PhrictionContent();
$joins[] = qsprintf(
$conn,
'JOIN %T c ON d.contentID = c.id',
$content_dao->getTableName());
}
return $joins;
}
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
if ($this->ids !== null) {
$where[] = qsprintf(
$conn,
'd.id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn,
'd.phid IN (%Ls)',
$this->phids);
}
if ($this->slugs !== null) {
$where[] = qsprintf(
$conn,
'd.slug IN (%Ls)',
$this->slugs);
}
if ($this->statuses !== null) {
$where[] = qsprintf(
$conn,
'd.status IN (%Ld)',
$this->statuses);
}
if ($this->slugPrefix !== null) {
$where[] = qsprintf(
$conn,
'd.slug LIKE %>',
$this->slugPrefix);
}
if ($this->depths !== null) {
$where[] = qsprintf(
$conn,
'd.depth IN (%Ld)',
$this->depths);
}
switch ($this->status) {
case self::STATUS_OPEN:
$where[] = qsprintf(
$conn,
'd.status NOT IN (%Ld)',
array(
PhrictionDocumentStatus::STATUS_DELETED,
PhrictionDocumentStatus::STATUS_MOVED,
PhrictionDocumentStatus::STATUS_STUB,
));
break;
case self::STATUS_NONSTUB:
$where[] = qsprintf(
$conn,
'd.status NOT IN (%Ld)',
array(
PhrictionDocumentStatus::STATUS_MOVED,
PhrictionDocumentStatus::STATUS_STUB,
));
break;
case self::STATUS_ANY:
break;
default:
throw new Exception(pht("Unknown status '%s'!", $this->status));
}
return $where;
}
public function getBuiltinOrders() {
return array(
self::ORDER_HIERARCHY => array(
'vector' => array('depth', 'title', 'updated'),
'name' => pht('Hierarchy'),
),
) + parent::getBuiltinOrders();
}
public function getOrderableColumns() {
return parent::getOrderableColumns() + array(
'depth' => array(
'table' => 'd',
'column' => 'depth',
'reverse' => true,
'type' => 'int',
),
'title' => array(
'table' => 'c',
'column' => 'title',
'reverse' => true,
'type' => 'string',
),
'updated' => array(
'table' => 'd',
'column' => 'contentID',
'type' => 'int',
'unique' => true,
),
);
}
protected function getPagingValueMap($cursor, array $keys) {
$document = $this->loadCursorObject($cursor);
$map = array(
'id' => $document->getID(),
'depth' => $document->getDepth(),
'updated' => $document->getContentID(),
);
foreach ($keys as $key) {
switch ($key) {
case 'title':
$map[$key] = $document->getContent()->getTitle();
break;
}
}
return $map;
}
protected function willExecuteCursorQuery(
PhabricatorCursorPagedPolicyAwareQuery $query) {
$vector = $this->getOrderVector();
if ($vector->containsKey('title')) {
$query->needContent(true);
}
}
protected function getPrimaryTableAlias() {
return 'd';
}
public function getQueryApplicationClass() {
return 'PhabricatorPhrictionApplication';
}
}
diff --git a/src/applications/phriction/storage/PhrictionDocument.php b/src/applications/phriction/storage/PhrictionDocument.php
index 01e85d4954..0a9f09afdd 100644
--- a/src/applications/phriction/storage/PhrictionDocument.php
+++ b/src/applications/phriction/storage/PhrictionDocument.php
@@ -1,268 +1,268 @@
<?php
final class PhrictionDocument extends PhrictionDAO
implements
PhabricatorPolicyInterface,
PhabricatorSubscribableInterface,
PhabricatorFlaggableInterface,
PhabricatorTokenReceiverInterface,
PhabricatorDestructibleInterface,
PhabricatorFulltextInterface,
PhabricatorFerretInterface,
PhabricatorProjectInterface,
PhabricatorApplicationTransactionInterface {
protected $slug;
protected $depth;
protected $contentID;
protected $status;
protected $mailKey;
protected $viewPolicy;
protected $editPolicy;
private $contentObject = self::ATTACHABLE;
private $ancestors = array();
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_TIMESTAMPS => false,
self::CONFIG_COLUMN_SCHEMA => array(
'slug' => 'sort128',
'depth' => 'uint32',
'contentID' => 'id?',
'status' => 'uint32',
'mailKey' => 'bytes20',
),
self::CONFIG_KEY_SCHEMA => array(
'key_phid' => null,
'phid' => array(
'columns' => array('phid'),
'unique' => true,
),
'slug' => array(
'columns' => array('slug'),
'unique' => true,
),
'depth' => array(
'columns' => array('depth', 'slug'),
'unique' => true,
),
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhrictionDocumentPHIDType::TYPECONST);
}
public static function initializeNewDocument(PhabricatorUser $actor, $slug) {
$document = new PhrictionDocument();
$document->setSlug($slug);
- $content = new PhrictionContent();
+ $content = new PhrictionContent();
$content->setSlug($slug);
$default_title = PhabricatorSlug::getDefaultTitle($slug);
$content->setTitle($default_title);
$document->attachContent($content);
$parent_doc = null;
$ancestral_slugs = PhabricatorSlug::getAncestry($slug);
if ($ancestral_slugs) {
$parent = end($ancestral_slugs);
$parent_doc = id(new PhrictionDocumentQuery())
->setViewer($actor)
->withSlugs(array($parent))
->executeOne();
}
if ($parent_doc) {
$document->setViewPolicy($parent_doc->getViewPolicy());
$document->setEditPolicy($parent_doc->getEditPolicy());
} else {
$default_view_policy = PhabricatorPolicies::getMostOpenPolicy();
$document->setViewPolicy($default_view_policy);
$document->setEditPolicy(PhabricatorPolicies::POLICY_USER);
}
return $document;
}
public function save() {
if (!$this->getMailKey()) {
$this->setMailKey(Filesystem::readRandomCharacters(20));
}
return parent::save();
}
public static function getSlugURI($slug, $type = 'document') {
static $types = array(
'document' => '/w/',
'history' => '/phriction/history/',
);
if (empty($types[$type])) {
throw new Exception(pht("Unknown URI type '%s'!", $type));
}
$prefix = $types[$type];
if ($slug == '/') {
return $prefix;
} else {
// NOTE: The effect here is to escape non-latin characters, since modern
// browsers deal with escaped UTF8 characters in a reasonable way (showing
// the user a readable URI) but older programs may not.
$slug = phutil_escape_uri($slug);
return $prefix.$slug;
}
}
public function setSlug($slug) {
$this->slug = PhabricatorSlug::normalize($slug);
$this->depth = PhabricatorSlug::getDepth($slug);
return $this;
}
public function attachContent(PhrictionContent $content) {
$this->contentObject = $content;
return $this;
}
public function getContent() {
return $this->assertAttached($this->contentObject);
}
public function getAncestors() {
return $this->ancestors;
}
public function getAncestor($slug) {
return $this->assertAttachedKey($this->ancestors, $slug);
}
public function attachAncestor($slug, $ancestor) {
$this->ancestors[$slug] = $ancestor;
return $this;
}
/* -( 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 $user) {
return false;
}
public function describeAutomaticCapability($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return pht(
'To view a wiki document, you must also be able to view all '.
'of its parents.');
case PhabricatorPolicyCapability::CAN_EDIT:
return pht(
'To edit a wiki document, you must also be able to view all '.
'of its parents.');
}
return null;
}
/* -( PhabricatorSubscribableInterface )----------------------------------- */
public function isAutomaticallySubscribed($phid) {
return false;
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new PhrictionTransactionEditor();
}
public function getApplicationTransactionObject() {
return $this;
}
public function getApplicationTransactionTemplate() {
return new PhrictionTransaction();
}
public function willRenderTimeline(
PhabricatorApplicationTransactionView $timeline,
AphrontRequest $request) {
return $timeline;
}
/* -( PhabricatorTokenReceiverInterface )---------------------------------- */
public function getUsersToNotifyOfTokenGiven() {
return PhabricatorSubscribersQuery::loadSubscribersForPHID($this->phid);
}
/* -( PhabricatorDestructibleInterface )----------------------------------- */
public function destroyObjectPermanently(
PhabricatorDestructionEngine $engine) {
$this->openTransaction();
$contents = id(new PhrictionContentQuery())
->setViewer($engine->getViewer())
->withDocumentPHIDs(array($this->getPHID()))
->execute();
foreach ($contents as $content) {
$engine->destroyObject($content);
}
$this->delete();
$this->saveTransaction();
}
/* -( PhabricatorFulltextInterface )--------------------------------------- */
public function newFulltextEngine() {
return new PhrictionDocumentFulltextEngine();
}
/* -( PhabricatorFerretInterface )----------------------------------------- */
public function newFerretEngine() {
return new PhrictionDocumentFerretEngine();
}
}

File Metadata

Mime Type
text/x-diff
Expires
Fri, Mar 14, 7:43 AM (4 h, 46 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
71687
Default Alt Text
(54 KB)

Event Timeline