Page MenuHomePhorge

No OneTemporary

diff --git a/src/applications/doorkeeper/remarkup/DoorkeeperRemarkupRuleAsana.php b/src/applications/doorkeeper/remarkup/DoorkeeperRemarkupRuleAsana.php
index ea63971ce5..7307d0e8f3 100644
--- a/src/applications/doorkeeper/remarkup/DoorkeeperRemarkupRuleAsana.php
+++ b/src/applications/doorkeeper/remarkup/DoorkeeperRemarkupRuleAsana.php
@@ -1,79 +1,83 @@
<?php
final class DoorkeeperRemarkupRuleAsana
extends PhutilRemarkupRule {
const KEY_TAGS = 'doorkeeper.tags';
public function getPriority() {
return 350.0;
}
public function apply($text) {
return preg_replace_callback(
'@https://app\\.asana\\.com/0/(\\d+)/(\\d+)@',
array($this, 'markupAsanaLink'),
$text);
}
public function markupAsanaLink($matches) {
$key = self::KEY_TAGS;
$engine = $this->getEngine();
$token = $engine->storeText('AsanaDoorkeeper');
$tags = $engine->getTextMetadata($key, array());
$tags[] = array(
'token' => $token,
'href' => $matches[0],
'tag' => array(
'ref' => array(
DoorkeeperBridgeAsana::APPTYPE_ASANA,
DoorkeeperBridgeAsana::APPDOMAIN_ASANA,
DoorkeeperBridgeAsana::OBJTYPE_TASK,
$matches[2],
),
'extra' => array(
'asana.context' => $matches[1],
),
),
);
$engine->setTextMetadata($key, $tags);
return $token;
}
public function didMarkupText() {
$key = self::KEY_TAGS;
$engine = $this->getEngine();
$tags = $engine->getTextMetadata($key, array());
if (!$tags) {
return;
}
$refs = array();
foreach ($tags as $spec) {
$tag_id = celerity_generate_unique_node_id();
$refs[] = array(
'id' => $tag_id,
) + $spec['tag'];
- $view = id(new PhabricatorTagView())
- ->setID($tag_id)
- ->setName($spec['href'])
- ->setHref($spec['href'])
- ->setType(PhabricatorTagView::TYPE_OBJECT)
- ->setExternal(true);
+ if ($this->getEngine()->isTextMode()) {
+ $view = $spec['href'];
+ } else {
+ $view = id(new PhabricatorTagView())
+ ->setID($tag_id)
+ ->setName($spec['href'])
+ ->setHref($spec['href'])
+ ->setType(PhabricatorTagView::TYPE_OBJECT)
+ ->setExternal(true);
+ }
$engine->overwriteStoredText($spec['token'], $view);
}
Javelin::initBehavior('doorkeeper-tag', array('tags' => $refs));
$engine->setTextMetadata($key, array());
}
}
diff --git a/src/applications/phid/PhabricatorObjectHandle.php b/src/applications/phid/PhabricatorObjectHandle.php
index d9bfcda361..ace10a1986 100644
--- a/src/applications/phid/PhabricatorObjectHandle.php
+++ b/src/applications/phid/PhabricatorObjectHandle.php
@@ -1,237 +1,239 @@
<?php
final class PhabricatorObjectHandle
implements PhabricatorPolicyInterface {
private $uri;
private $phid;
private $type;
private $name;
private $fullName;
private $title;
private $imageURI;
private $timestamp;
private $status = PhabricatorObjectHandleStatus::STATUS_OPEN;
private $complete;
private $disabled;
public function setURI($uri) {
$this->uri = $uri;
return $this;
}
public function getURI() {
return $this->uri;
}
public function setPHID($phid) {
$this->phid = $phid;
return $this;
}
public function getPHID() {
return $this->phid;
}
public function setName($name) {
$this->name = $name;
return $this;
}
public function getName() {
if ($this->name === null) {
return pht('Unknown Object (%s)', $this->getTypeName());
}
return $this->name;
}
public function setStatus($status) {
$this->status = $status;
return $this;
}
public function getStatus() {
return $this->status;
}
public function setFullName($full_name) {
$this->fullName = $full_name;
return $this;
}
public function getFullName() {
if ($this->fullName !== null) {
return $this->fullName;
}
return $this->getName();
}
public function setTitle($title) {
$this->title = $title;
return $this;
}
public function getTitle() {
return $this->title;
}
public function setType($type) {
$this->type = $type;
return $this;
}
public function getType() {
return $this->type;
}
public function setImageURI($uri) {
$this->imageURI = $uri;
return $this;
}
public function getImageURI() {
return $this->imageURI;
}
public function setTimestamp($timestamp) {
$this->timestamp = $timestamp;
return $this;
}
public function getTimestamp() {
return $this->timestamp;
}
public function getTypeName() {
if ($this->getPHIDType()) {
return $this->getPHIDType()->getTypeName();
}
return $this->getType();
}
/**
* Set whether or not the underlying object is complete. See
* @{method:isComplete} for an explanation of what it means to be complete.
*
* @param bool True if the handle represents a complete object.
* @return this
*/
public function setComplete($complete) {
$this->complete = $complete;
return $this;
}
/**
* Determine if the handle represents an object which was completely loaded
* (i.e., the underlying object exists) vs an object which could not be
* completely loaded (e.g., the type or data for the PHID could not be
* identified or located).
*
* Basically, @{class:PhabricatorObjectHandleData} gives you back a handle for
* any PHID you give it, but it gives you a complete handle only for valid
* PHIDs.
*
* @return bool True if the handle represents a complete object.
*/
public function isComplete() {
return $this->complete;
}
/**
* Set whether or not the underlying object is disabled. See
* @{method:isDisabled} for an explanation of what it means to be disabled.
*
* @param bool True if the handle represents a disabled object.
* @return this
*/
public function setDisabled($disabled) {
$this->disabled = $disabled;
return $this;
}
/**
* Determine if the handle represents an object which has been disabled --
* for example, disabled users, archived projects, etc. These objects are
* complete and exist, but should be excluded from some system interactions
* (for instance, they usually should not appear in typeaheads, and should
* not have mail/notifications delivered to or about them).
*
* @return bool True if the handle represents a disabled object.
*/
public function isDisabled() {
return $this->disabled;
}
public function renderLink($name = null) {
if ($name === null) {
$name = $this->getLinkName();
}
$classes = array();
$title = $this->title;
if ($this->status != PhabricatorObjectHandleStatus::STATUS_OPEN) {
$classes[] = 'handle-status-'.$this->status;
$title = $title ? $title : $this->status;
}
if ($this->disabled) {
$classes[] = 'handle-disabled';
$title = 'disabled'; // Overwrite status.
}
if ($this->getType() == PhabricatorPeoplePHIDTypeUser::TYPECONST) {
$classes[] = 'phui-link-person';
}
return phutil_tag(
'a',
array(
'href' => $this->getURI(),
'class' => implode(' ', $classes),
'title' => $title,
),
$name);
}
public function getLinkName() {
switch ($this->getType()) {
case PhabricatorPeoplePHIDTypeUser::TYPECONST:
$name = $this->getName();
break;
default:
$name = $this->getFullName();
break;
}
return $name;
}
protected function getPHIDType() {
$types = PhabricatorPHIDType::getAllTypes();
return idx($types, $this->getType());
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
);
}
public function getPolicy($capability) {
return PhabricatorPolicies::POLICY_PUBLIC;
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
- return false;
+ // NOTE: Handles are always visible, they just don't get populated with
+ // data if the user can't see the underlying object.
+ return true;
}
}
diff --git a/src/infrastructure/markup/rule/PhabricatorRemarkupRuleObject.php b/src/infrastructure/markup/rule/PhabricatorRemarkupRuleObject.php
index 57f52b3651..35e95ee4ba 100644
--- a/src/infrastructure/markup/rule/PhabricatorRemarkupRuleObject.php
+++ b/src/infrastructure/markup/rule/PhabricatorRemarkupRuleObject.php
@@ -1,218 +1,218 @@
<?php
/**
* @group markup
*/
abstract class PhabricatorRemarkupRuleObject
extends PhutilRemarkupRule {
const KEY_RULE_OBJECT = 'rule.object';
abstract protected function getObjectNamePrefix();
abstract protected function loadObjects(array $ids);
protected function getObjectNamePrefixBeginsWithWordCharacter() {
$prefix = $this->getObjectNamePrefix();
return preg_match('/^\w/', $prefix);
}
protected function getObjectIDPattern() {
return '[1-9]\d*';
}
protected function shouldMarkupObject(array $params) {
return true;
}
protected function loadHandles(array $objects) {
$phids = mpull($objects, 'getPHID');
- $query = new PhabricatorObjectHandleData($phids);
- $viewer = $this->getEngine()->getConfig('viewer');
- $query->setViewer($viewer);
- $handles = $query->loadHandles();
+ $handles = id(new PhabricatorHandleQuery($phids))
+ ->withPHIDs($phids)
+ ->setViewer($this->getEngine()->getConfig('viewer'))
+ ->execute();
$result = array();
foreach ($objects as $id => $object) {
$result[$id] = $handles[$object->getPHID()];
}
return $result;
}
protected function renderObjectRef($object, $handle, $anchor, $id) {
$href = $handle->getURI();
$text = $this->getObjectNamePrefix().$id;
if ($anchor) {
$matches = null;
if (preg_match('@^(?:comment-)?(\d{1,7})$@', $anchor, $matches)) {
// Maximum length is 7 because 12345678 could be a file hash in
// Differential.
$href = $href.'#comment-'.$matches[1];
$text = $text.'#'.$matches[1];
} else {
$href = $href.'#'.$anchor;
$text = $text.'#'.$anchor;
}
}
if ($this->getEngine()->isTextMode()) {
return PhabricatorEnv::getProductionURI($href);
}
$status_closed = PhabricatorObjectHandleStatus::STATUS_CLOSED;
$attr = array(
'phid' => $handle->getPHID(),
'closed' => ($handle->getStatus() == $status_closed),
);
return $this->renderHovertag($text, $href, $attr);
}
protected function renderObjectEmbed($object, $handle, $options) {
$name = $handle->getFullName();
$href = $handle->getURI();
if ($this->getEngine()->isTextMode()) {
return $name.' <'.PhabricatorEnv::getProductionURI($href).'>';
}
$attr = array(
'phid' => $handle->getPHID(),
);
return $this->renderHovertag($name, $href, $attr);
}
protected function renderHovertag($name, $href, array $attr = array()) {
return id(new PhabricatorTagView())
->setName($name)
->setHref($href)
->setType(PhabricatorTagView::TYPE_OBJECT)
->setPHID(idx($attr, 'phid'))
->setClosed(idx($attr, 'closed'))
->render();
}
public function apply($text) {
$prefix = $this->getObjectNamePrefix();
$prefix = preg_quote($prefix, '@');
$id = $this->getObjectIDPattern();
$text = preg_replace_callback(
'@\B{'.$prefix.'('.$id.')((?:[^}\\\\]|\\\\.)*)}\B@',
array($this, 'markupObjectEmbed'),
$text);
// If the prefix starts with a word character (like "D"), we want to
// require a word boundary so that we don't match "XD1" as "D1". If the
// prefix does not start with a word character, we want to require no word
// boundary for the same reasons. Test if the prefix starts with a word
// character.
if ($this->getObjectNamePrefixBeginsWithWordCharacter()) {
$boundary = '\\b';
} else {
$boundary = '\\B';
}
// NOTE: The "(?<!#)" prevents us from linking "#abcdef" or similar. The
// "\b" allows us to link "(abcdef)" or similar without linking things
// in the middle of words.
$text = preg_replace_callback(
'((?<!#)'.$boundary.$prefix.'('.$id.')(?:#([-\w\d]+))?\b)',
array($this, 'markupObjectReference'),
$text);
return $text;
}
public function markupObjectEmbed($matches) {
return $this->markupObject(array(
'type' => 'embed',
'id' => $matches[1],
'options' => idx($matches, 2),
'original' => $matches[0],
));
}
public function markupObjectReference($matches) {
return $this->markupObject(array(
'type' => 'ref',
'id' => $matches[1],
'anchor' => idx($matches, 2),
'original' => $matches[0],
));
}
private function markupObject(array $params) {
if (!$this->shouldMarkupObject($params)) {
return $params['original'];
}
$engine = $this->getEngine();
$token = $engine->storeText('x');
$metadata_key = self::KEY_RULE_OBJECT.'.'.$this->getObjectNamePrefix();
$metadata = $engine->getTextMetadata($metadata_key, array());
$metadata[] = array(
'token' => $token,
) + $params;
$engine->setTextMetadata($metadata_key, $metadata);
return $token;
}
public function didMarkupText() {
$engine = $this->getEngine();
$metadata_key = self::KEY_RULE_OBJECT.'.'.$this->getObjectNamePrefix();
$metadata = $engine->getTextMetadata($metadata_key, array());
if (!$metadata) {
return;
}
$ids = ipull($metadata, 'id');
$objects = $this->loadObjects($ids);
// For objects that are invalid or which the user can't see, just render
// the original text.
// TODO: We should probably distinguish between these cases and render a
// "you can't see this" state for nonvisible objects.
foreach ($metadata as $key => $spec) {
if (empty($objects[$spec['id']])) {
$engine->overwriteStoredText(
$spec['token'],
$spec['original']);
unset($metadata[$key]);
}
}
$handles = $this->loadHandles($objects);
foreach ($metadata as $key => $spec) {
$handle = $handles[$spec['id']];
$object = $objects[$spec['id']];
switch ($spec['type']) {
case 'ref':
$view = $this->renderObjectRef(
$object,
$handle,
$spec['anchor'],
$spec['id']);
break;
case 'embed':
$view = $this->renderObjectEmbed($object, $handle, $spec['options']);
break;
}
$engine->overwriteStoredText($spec['token'], $view);
}
$engine->setTextMetadata($metadata_key, array());
}
}

File Metadata

Mime Type
text/x-diff
Expires
Jan 19 2025, 23:18 (6 w, 4 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1129818
Default Alt Text
(15 KB)

Event Timeline