Page MenuHomestyx hydra

No OneTemporary

diff --git a/conf/aphlict/README b/conf/aphlict/README
index 2786ea5658..0704bf6068 100644
--- a/conf/aphlict/README
+++ b/conf/aphlict/README
@@ -1,16 +1,16 @@
To customize this configuration, you have two options: create a custom
configuration file in this directory, or specify a path to a configuration file
explicitly when starting Aphlict.
To create a custom configuration file, copy `aphlict.default.json` in this
directory and rename it `aphlict.custom.json`. If this file exists, it will
be read by default.
To specify a path when starting Aphlict, use the `--config` flag:
- phabricator/ $ ./bin/aphlict start --config path/to/config.json
+ phorge/ $ ./bin/aphlict start --config path/to/config.json
Specifying a configuration file explicitly overrides default configuration.
For more information about configuring notifications, see the article
"Notifications User Guide: Setup and Configuration" in the documentation.
diff --git a/scripts/init/lib.php b/scripts/init/lib.php
index 4c544da9d0..a12c394f3b 100644
--- a/scripts/init/lib.php
+++ b/scripts/init/lib.php
@@ -1,27 +1,27 @@
<?php
function init_phabricator_script(array $options) {
error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', 1);
$include_path = ini_get('include_path');
ini_set(
'include_path',
$include_path.PATH_SEPARATOR.dirname(__FILE__).'/../../../');
$ok = @include_once 'arcanist/support/init/init-script.php';
if (!$ok) {
echo
'FATAL ERROR: Unable to load the "Arcanist" library. '.
- 'Put "arcanist/" next to "phabricator/" on disk.';
+ 'Put "arcanist/" next to "phorge/" on disk.';
echo "\n";
exit(1);
}
phutil_load_library('arcanist/src');
phutil_load_library(dirname(__FILE__).'/../../src/');
$config_optional = $options['config.optional'];
PhabricatorEnv::initializeScriptEnvironment($config_optional);
}
diff --git a/src/applications/config/option/PhabricatorConfigOption.php b/src/applications/config/option/PhabricatorConfigOption.php
index 6b38bbb6d6..ee79005d5d 100644
--- a/src/applications/config/option/PhabricatorConfigOption.php
+++ b/src/applications/config/option/PhabricatorConfigOption.php
@@ -1,226 +1,226 @@
<?php
final class PhabricatorConfigOption
extends Phobject {
private $key;
private $default;
private $summary;
private $description;
private $type;
private $boolOptions;
private $enumOptions;
private $group;
private $examples;
private $locked;
private $lockedMessage;
private $hidden;
private $baseClass;
private $customData;
private $customObject;
public function setBaseClass($base_class) {
$this->baseClass = $base_class;
return $this;
}
public function getBaseClass() {
return $this->baseClass;
}
public function setHidden($hidden) {
$this->hidden = $hidden;
return $this;
}
public function getHidden() {
if ($this->hidden) {
return true;
}
return idx(
PhabricatorEnv::getEnvConfig('config.hide'),
$this->getKey(),
false);
}
public function setLocked($locked) {
$this->locked = $locked;
return $this;
}
public function getLocked() {
if ($this->locked) {
return true;
}
if ($this->getHidden()) {
return true;
}
return idx(
PhabricatorEnv::getEnvConfig('config.lock'),
$this->getKey(),
false);
}
public function setLockedMessage($message) {
$this->lockedMessage = $message;
return $this;
}
public function getLockedMessage() {
if ($this->lockedMessage !== null) {
return $this->lockedMessage;
}
return pht(
'This configuration is locked and can not be edited from the web '.
'interface. Use %s in %s to edit it.',
phutil_tag('tt', array(), './bin/config'),
- phutil_tag('tt', array(), 'phabricator/'));
+ phutil_tag('tt', array(), PlatformSymbols::getPlatformServerPath()));
}
public function addExample($value, $description) {
$this->examples[] = array($value, $description);
return $this;
}
public function getExamples() {
return $this->examples;
}
public function setGroup(PhabricatorApplicationConfigOptions $group) {
$this->group = $group;
return $this;
}
public function getGroup() {
return $this->group;
}
public function setBoolOptions(array $options) {
$this->boolOptions = $options;
return $this;
}
public function getBoolOptions() {
if ($this->boolOptions) {
return $this->boolOptions;
}
return array(
pht('True'),
pht('False'),
);
}
public function setEnumOptions(array $options) {
$this->enumOptions = $options;
return $this;
}
public function getEnumOptions() {
if ($this->enumOptions) {
return $this->enumOptions;
}
throw new PhutilInvalidStateException('setEnumOptions');
}
public function setKey($key) {
$this->key = $key;
return $this;
}
public function getKey() {
return $this->key;
}
public function setDefault($default) {
$this->default = $default;
return $this;
}
public function getDefault() {
return $this->default;
}
public function setSummary($summary) {
$this->summary = $summary;
return $this;
}
public function getSummary() {
if (empty($this->summary)) {
return $this->getDescription();
}
return $this->summary;
}
/**
* Set the human Description of this Config
*
* @param string|null $description Description as raw Remarkup
* @return self
*/
public function setDescription($description) {
$this->description = $description;
return $this;
}
/**
* Get the human Description of this Config
*
* @return string|null Description as raw Remarkup
*/
public function getDescription() {
return $this->description;
}
public function setType($type) {
$this->type = $type;
return $this;
}
public function getType() {
return $this->type;
}
public function newOptionType() {
$type_key = $this->getType();
$type_map = PhabricatorConfigType::getAllTypes();
return idx($type_map, $type_key);
}
public function isCustomType() {
return !strncmp($this->getType(), 'custom:', 7);
}
public function getCustomObject() {
if (!$this->customObject) {
if (!$this->isCustomType()) {
throw new Exception(pht('This option does not have a custom type!'));
}
$this->customObject = newv(substr($this->getType(), 7), array());
}
return $this->customObject;
}
public function getCustomData() {
return $this->customData;
}
public function setCustomData($data) {
$this->customData = $data;
return $this;
}
public function newDescriptionRemarkupView(PhabricatorUser $viewer) {
$description = $this->getDescription();
if (!phutil_nonempty_string($description)) {
return null;
}
return new PHUIRemarkupView($viewer, $description);
}
}
diff --git a/src/applications/metamta/management/PhabricatorMailManagementReceiveTestWorkflow.php b/src/applications/metamta/management/PhabricatorMailManagementReceiveTestWorkflow.php
index 73917a2d84..a3c556e6ac 100644
--- a/src/applications/metamta/management/PhabricatorMailManagementReceiveTestWorkflow.php
+++ b/src/applications/metamta/management/PhabricatorMailManagementReceiveTestWorkflow.php
@@ -1,189 +1,190 @@
<?php
final class PhabricatorMailManagementReceiveTestWorkflow
extends PhabricatorMailManagementWorkflow {
protected function didConstruct() {
$this
->setName('receive-test')
->setSynopsis(
pht(
'Simulate receiving mail. This is primarily useful if you are '.
'developing new mail receivers.'))
->setExamples(
'**receive-test** --as alincoln --to D123 < body.txt')
->setArguments(
array(
array(
'name' => 'as',
'param' => 'user',
'help' => pht('Act as the specified user.'),
),
array(
'name' => 'from',
'param' => 'email',
'help' => pht('Simulate mail delivery "From:" the given user.'),
),
array(
'name' => 'to',
'param' => 'object',
'help' => pht('Simulate mail delivery "To:" the given object.'),
),
array(
'name' => 'cc',
'param' => 'address',
'help' => pht('Simulate a mail delivery "Cc:" address.'),
'repeat' => true,
),
));
}
public function execute(PhutilArgumentParser $args) {
$viewer = $this->getViewer();
$console = PhutilConsole::getConsole();
$to = $args->getArg('to');
if (!$to) {
throw new PhutilArgumentUsageException(
pht(
"Use '%s' to specify the receiving object or email address.",
'--to'));
}
$to_application_email = id(new PhabricatorMetaMTAApplicationEmailQuery())
->setViewer($this->getViewer())
->withAddresses(array($to))
->executeOne();
$as = $args->getArg('as');
if (!$as && $to_application_email) {
$default_phid = $to_application_email->getConfigValue(
PhabricatorMetaMTAApplicationEmail::CONFIG_DEFAULT_AUTHOR);
if ($default_phid) {
$default_user = id(new PhabricatorPeopleQuery())
->setViewer($this->getViewer())
->withPHIDs(array($default_phid))
->executeOne();
if ($default_user) {
$as = $default_user->getUsername();
}
}
}
if (!$as) {
throw new PhutilArgumentUsageException(
pht("Use '--as' to specify the acting user."));
}
$user = id(new PhabricatorPeopleQuery())
->setViewer($this->getViewer())
->withUsernames(array($as))
->executeOne();
if (!$user) {
throw new PhutilArgumentUsageException(
pht("No such user '%s' exists.", $as));
}
$from = $args->getArg('from');
if (!$from) {
$from = $user->loadPrimaryEmail()->getAddress();
}
$cc = $args->getArg('cc');
$console->writeErr("%s\n", pht('Reading message body from stdin...'));
$body = file_get_contents('php://stdin');
$received = new PhabricatorMetaMTAReceivedMail();
$header_content = array(
'Message-ID' => Filesystem::readRandomCharacters(12),
'From' => $from,
'Cc' => implode(', ', $cc),
);
if (preg_match('/.+@.+/', $to)) {
$header_content['to'] = $to;
} else {
// We allow the user to use an object name instead of a real address
// as a convenience. To build the mail, we build a similar message and
// look for a receiver which will accept it.
// In the general case, mail may be processed by multiple receivers,
// but mail to objects only ever has one receiver today.
$pseudohash = PhabricatorObjectMailReceiver::computeMailHash('x', 'y');
$raw_target = $to.'+1+'.$pseudohash;
$target = new PhutilEmailAddress($raw_target.'@local.cli');
$pseudomail = id(new PhabricatorMetaMTAReceivedMail())
->setHeaders(
array(
'to' => $raw_target,
));
$receivers = id(new PhutilClassMapQuery())
->setAncestorClass('PhabricatorMailReceiver')
->setFilterMethod('isEnabled')
->execute();
$receiver = null;
foreach ($receivers as $possible_receiver) {
$possible_receiver = id(clone $possible_receiver)
->setViewer($viewer)
->setSender($user);
if (!$possible_receiver->canAcceptMail($pseudomail, $target)) {
continue;
}
$receiver = $possible_receiver;
break;
}
if (!$receiver) {
throw new Exception(
pht("No configured mail receiver can accept mail to '%s'.", $to));
}
if (!($receiver instanceof PhabricatorObjectMailReceiver)) {
$class = get_class($receiver);
throw new Exception(
pht(
"Receiver '%s' accepts mail to '%s', but is not a ".
"subclass of PhabricatorObjectMailReceiver.",
$class,
$to));
}
$object = $receiver->loadMailReceiverObject($to, $user);
if (!$object) {
throw new Exception(pht("No such object '%s'!", $to));
}
$mail_key = PhabricatorMetaMTAMailProperties::loadMailKey($object);
$hash = PhabricatorObjectMailReceiver::computeMailHash(
$mail_key,
$user->getPHID());
$header_content['to'] = $to.'+'.$user->getID().'+'.$hash.'@test.com';
}
$received->setHeaders($header_content);
$received->setBodies(
array(
'text' => $body,
));
$received->save();
$received->processReceivedMail();
$console->writeErr(
- "%s\n\n phabricator/ $ ./bin/mail show-inbound --id %d\n\n",
+ "%s\n\n %s $ ./bin/mail show-inbound --id %d\n\n",
pht('Mail received! You can view details by running this command:'),
+ PlatformSymbols::getPlatformServerPath(),
$received->getID());
}
}
diff --git a/src/applications/metamta/management/PhabricatorMailManagementSendTestWorkflow.php b/src/applications/metamta/management/PhabricatorMailManagementSendTestWorkflow.php
index 6ea2c9de5a..54c7ff53f6 100644
--- a/src/applications/metamta/management/PhabricatorMailManagementSendTestWorkflow.php
+++ b/src/applications/metamta/management/PhabricatorMailManagementSendTestWorkflow.php
@@ -1,233 +1,234 @@
<?php
final class PhabricatorMailManagementSendTestWorkflow
extends PhabricatorMailManagementWorkflow {
protected function didConstruct() {
$this
->setName('send-test')
->setSynopsis(
pht(
'Simulate sending mail. This may be useful to test your mail '.
'configuration, or while developing new mail adapters.'))
->setExamples('**send-test** --to alincoln --subject hi < body.txt')
->setArguments(
array(
array(
'name' => 'from',
'param' => 'user',
'help' => pht('Send mail from the specified user.'),
),
array(
'name' => 'to',
'param' => 'user',
'help' => pht('Send mail "To:" the specified users.'),
'repeat' => true,
),
array(
'name' => 'cc',
'param' => 'user',
'help' => pht('Send mail which "Cc:"s the specified users.'),
'repeat' => true,
),
array(
'name' => 'subject',
'param' => 'text',
'help' => pht('Use the provided subject.'),
),
array(
'name' => 'tag',
'param' => 'text',
'help' => pht('Add the given mail tags.'),
'repeat' => true,
),
array(
'name' => 'attach',
'param' => 'file',
'help' => pht('Attach a file.'),
'repeat' => true,
),
array(
'name' => 'mailer',
'param' => 'key',
'help' => pht('Send with a specific configured mailer.'),
),
array(
'name' => 'html',
'help' => pht('Send as HTML mail.'),
),
array(
'name' => 'bulk',
'help' => pht('Send with bulk headers.'),
),
array(
'name' => 'type',
'param' => 'message-type',
'default' => PhabricatorMailEmailMessage::MESSAGETYPE,
'help' => pht(
'Send the specified type of message (email, sms, ...).'),
),
));
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$viewer = $this->getViewer();
$type = $args->getArg('type');
$type_map = PhabricatorMailExternalMessage::getAllMessageTypes();
if (!isset($type_map[$type])) {
throw new PhutilArgumentUsageException(
pht(
'Message type "%s" is unknown, supported message types are: %s.',
$type,
implode(', ', array_keys($type_map))));
}
$from = $args->getArg('from');
if ($from) {
$user = id(new PhabricatorPeopleQuery())
->setViewer($viewer)
->withUsernames(array($from))
->executeOne();
if (!$user) {
throw new PhutilArgumentUsageException(
pht("No such user '%s' exists.", $from));
}
$from = $user;
}
$tos = $args->getArg('to');
$ccs = $args->getArg('cc');
if (!$tos && !$ccs) {
throw new PhutilArgumentUsageException(
pht(
'Specify one or more users to send a message to with "--to" and/or '.
'"--cc".'));
}
$names = array_merge($tos, $ccs);
$users = id(new PhabricatorPeopleQuery())
->setViewer($viewer)
->withUsernames($names)
->execute();
$users = mpull($users, null, 'getUsername');
$raw_tos = array();
foreach ($tos as $key => $username) {
// If the recipient has an "@" in any noninitial position, treat this as
// a raw email address.
if (preg_match('/.@/', $username)) {
$raw_tos[] = $username;
unset($tos[$key]);
continue;
}
if (empty($users[$username])) {
throw new PhutilArgumentUsageException(
pht("No such user '%s' exists.", $username));
}
$tos[$key] = $users[$username]->getPHID();
}
foreach ($ccs as $key => $username) {
if (empty($users[$username])) {
throw new PhutilArgumentUsageException(
pht("No such user '%s' exists.", $username));
}
$ccs[$key] = $users[$username]->getPHID();
}
$subject = $args->getArg('subject');
if ($subject === null) {
$subject = pht('No Subject');
}
$tags = $args->getArg('tag');
$attach = $args->getArg('attach');
$is_bulk = $args->getArg('bulk');
$console->writeErr("%s\n", pht('Reading message body from stdin...'));
$body = file_get_contents('php://stdin');
$mail = id(new PhabricatorMetaMTAMail())
->addCCs($ccs)
->setSubject($subject)
->setBody($body)
->setIsBulk($is_bulk)
->setMailTags($tags);
if ($tos) {
$mail->addTos($tos);
}
if ($raw_tos) {
$mail->addRawTos($raw_tos);
}
if ($args->getArg('html')) {
$mail->setBody(
pht(
'(This is a placeholder plaintext email body for a test message '.
'sent with %s.)',
'--html'));
$mail->setHTMLBody($body);
} else {
$mail->setBody($body);
}
if ($from) {
$mail->setFrom($from->getPHID());
}
$mailers = PhabricatorMetaMTAMail::newMailers(
array(
'media' => array($type),
'outbound' => true,
));
$mailers = mpull($mailers, null, 'getKey');
if (!$mailers) {
throw new PhutilArgumentUsageException(
pht(
'No configured mailers support outbound messages of type "%s".',
$type));
}
$mailer_key = $args->getArg('mailer');
if ($mailer_key !== null) {
if (!isset($mailers[$mailer_key])) {
throw new PhutilArgumentUsageException(
pht(
'Mailer key ("%s") is not configured, or does not support '.
'outbound messages of type "%s". Available mailers are: %s.',
$mailer_key,
$type,
implode(', ', array_keys($mailers))));
}
$mail->setTryMailers(array($mailer_key));
}
foreach ($attach as $attachment) {
$data = Filesystem::readFile($attachment);
$name = basename($attachment);
$mime = Filesystem::getMimeType($attachment);
$file = new PhabricatorMailAttachment($data, $name, $mime);
$mail->addAttachment($file);
}
$mail->setMessageType($type);
PhabricatorWorker::setRunAllTasksInProcess(true);
$mail->save();
$console->writeErr(
- "%s\n\n phabricator/ $ ./bin/mail show-outbound --id %d\n\n",
+ "%s\n\n %s $ ./bin/mail show-outbound --id %d\n\n",
pht('Mail sent! You can view details by running this command:'),
+ PlatformSymbols::getPlatformServerPath(),
$mail->getID());
}
}
diff --git a/src/applications/people/controller/PhabricatorPeopleDeleteController.php b/src/applications/people/controller/PhabricatorPeopleDeleteController.php
index 8e6ac91da7..2f45b40d31 100644
--- a/src/applications/people/controller/PhabricatorPeopleDeleteController.php
+++ b/src/applications/people/controller/PhabricatorPeopleDeleteController.php
@@ -1,51 +1,54 @@
<?php
final class PhabricatorPeopleDeleteController
extends PhabricatorPeopleController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getUser();
$id = $request->getURIData('id');
$user = id(new PhabricatorPeopleQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
if (!$user) {
return new Aphront404Response();
}
$manage_uri = $this->getApplicationURI("manage/{$id}/");
$doc_uri = PhabricatorEnv::getDoclink(
'Permanently Destroying Data');
return $this->newDialog()
->setTitle(pht('Delete User'))
->appendParagraph(
pht(
'To permanently destroy this user, run this command from the '.
'command line:'))
->appendCommand(
- csprintf(
- 'phabricator/ $ ./bin/remove destroy %R',
- $user->getMonogram()))
+ hsprintf(
+ '<tt>%s $</tt> %s',
+ PlatformSymbols::getPlatformServerPath(),
+ csprintf(
+ './bin/remove destroy %R',
+ $user->getMonogram())))
->appendParagraph(
pht(
'Unless you have a very good reason to delete this user, consider '.
'disabling them instead.'))
->appendParagraph(
pht(
'Users can not be permanently destroyed from the web interface. '.
'See %s in the documentation for more information.',
phutil_tag(
'a',
array(
'href' => $doc_uri,
'target' => '_blank',
),
pht('Permanently Destroying Data'))))
->addCancelButton($manage_uri, pht('Close'));
}
}
diff --git a/src/applications/transactions/bulk/PhabricatorEditEngineBulkJobType.php b/src/applications/transactions/bulk/PhabricatorEditEngineBulkJobType.php
index 758e7f3439..811b4e1565 100644
--- a/src/applications/transactions/bulk/PhabricatorEditEngineBulkJobType.php
+++ b/src/applications/transactions/bulk/PhabricatorEditEngineBulkJobType.php
@@ -1,129 +1,129 @@
<?php
final class PhabricatorEditEngineBulkJobType
extends PhabricatorWorkerBulkJobType {
public function getBulkJobTypeKey() {
return 'transaction.edit';
}
public function getJobName(PhabricatorWorkerBulkJob $job) {
return pht('Bulk Edit');
}
public function getDescriptionForConfirm(PhabricatorWorkerBulkJob $job) {
$parts = array();
$parts[] = pht(
'You are about to apply a bulk edit which will affect '.
'%s object(s).',
new PhutilNumber($job->getSize()));
if ($job->getIsSilent()) {
$parts[] = pht(
'If you start work now, this edit will be applied silently: it will '.
'not send mail or publish notifications.');
} else {
$parts[] = pht(
'If you start work now, this edit will send mail and publish '.
'notifications normally.');
$parts[] = pht('To silence this edit, run this command:');
$command = csprintf(
- 'phabricator/ $ ./bin/bulk make-silent --id %R',
+ '%s $ ./bin/bulk make-silent --id %R',
+ PlatformSymbols::getPlatformServerPath(),
$job->getID());
- $command = (string)$command;
$parts[] = phutil_tag('tt', array(), $command);
$parts[] = pht(
'After running this command, reload this page to see the new setting.');
}
return $parts;
}
public function getJobSize(PhabricatorWorkerBulkJob $job) {
return count($job->getParameter('objectPHIDs', array()));
}
public function getDoneURI(PhabricatorWorkerBulkJob $job) {
return $job->getParameter('doneURI');
}
public function createTasks(PhabricatorWorkerBulkJob $job) {
$tasks = array();
foreach ($job->getParameter('objectPHIDs', array()) as $phid) {
$tasks[] = PhabricatorWorkerBulkTask::initializeNewTask($job, $phid);
}
return $tasks;
}
public function runTask(
PhabricatorUser $actor,
PhabricatorWorkerBulkJob $job,
PhabricatorWorkerBulkTask $task) {
$object = id(new PhabricatorObjectQuery())
->setViewer($actor)
->withPHIDs(array($task->getObjectPHID()))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$object) {
return;
}
$raw_xactions = $job->getParameter('xactions');
$xactions = $this->buildTransactions($object, $raw_xactions);
$is_silent = $job->getIsSilent();
$editor = $object->getApplicationTransactionEditor()
->setActor($actor)
->setContentSource($job->newContentSource())
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true)
->setIsSilent($is_silent)
->applyTransactions($object, $xactions);
}
private function buildTransactions($object, array $raw_xactions) {
$xactions = array();
foreach ($raw_xactions as $raw_xaction) {
$xaction = $object->getApplicationTransactionTemplate()
->setTransactionType($raw_xaction['type']);
if (isset($raw_xaction['new'])) {
$xaction->setNewValue($raw_xaction['new']);
}
if (isset($raw_xaction['comment'])) {
$comment = $xaction->getApplicationTransactionCommentObject()
->setContent($raw_xaction['comment']);
$xaction->attachComment($comment);
}
if (isset($raw_xaction['metadata'])) {
foreach ($raw_xaction['metadata'] as $meta_key => $meta_value) {
$xaction->setMetadataValue($meta_key, $meta_value);
}
}
if (array_key_exists('old', $raw_xaction)) {
$xaction->setOldValue($raw_xaction['old']);
}
$xactions[] = $xaction;
}
return $xactions;
}
}
diff --git a/src/infrastructure/events/PhabricatorAutoEventListener.php b/src/infrastructure/events/PhabricatorAutoEventListener.php
index 0ed76b3390..a8419f6865 100644
--- a/src/infrastructure/events/PhabricatorAutoEventListener.php
+++ b/src/infrastructure/events/PhabricatorAutoEventListener.php
@@ -1,15 +1,15 @@
<?php
/**
* Event listener which is registered automatically, without requiring
* configuration.
*
* Normally, event listeners must be registered via applications. This is
* appropriate for structured listeners in libraries, but it adds a lot of
* overhead and is cumbersome for one-off listeners.
*
* All concrete subclasses of this class are automatically registered at
* startup. This allows it to be used with custom one-offs that can be dropped
- * into `phabricator/src/extensions/`.
+ * into `phorge/src/extensions/`.
*/
abstract class PhabricatorAutoEventListener extends PhabricatorEventListener {}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Apr 29, 10:23 AM (22 h, 23 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
108286
Default Alt Text
(27 KB)

Event Timeline