Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2896595
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
15 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
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)
Attached To
Mode
rP Phorge
Attached
Detach File
Event Timeline
Log In to Comment