Page MenuHomePhorge

No OneTemporary

diff --git a/src/controller/DiagramController.php b/src/controller/DiagramController.php
index ac466e8..f068dcd 100644
--- a/src/controller/DiagramController.php
+++ b/src/controller/DiagramController.php
@@ -1,509 +1,546 @@
<?php
final class DiagramController extends PhabricatorController {
/**
* Processes incoming HTTP requests from Diagram application
*/
public function handleRequest(AphrontRequest $request) {
$this->setRequest($request);
// detetermine if GET or POST HTTP call
if ($request->isHTTPPost()) {
// process POST calls (like save action)
return $this->handleHttpPostCall($request);
}
// determine type of URL by means of DiagramApplication route parameters
$diagramphid = $request->getURIData('diagramphid');
$diagramid = $request->getURIData('diagramid');
$versioneddiagramid = $request->getURIData('versioneddiagramid');
$version = $request->getURIData('version');
$route = $request->getURIData('route');
$versioninfoDiagramID = $request->getURIData('versioninfodiagram');
$versioninfoPage = $request->getURIData('versioninfopage');
if (isset($diagramphid) && !empty(trim($diagramphid))) {
// return PNG image data
$diagram = id(new DiagramVersion())->loadByDiagramPHID($diagramphid);
if ($diagram !== null) {
$response = new AphrontFileResponse();
$response->setMimeType('image/png');
$response->setContent($diagram->getData());
return $response;
}
}
if (isset($versioninfoDiagramID) && !empty(trim($versioninfoDiagramID))) {
// return diagram version info
if (!isset($versioninfoPage) || empty(trim($versioninfoPage))) {
// versioninfoPage was dismissed -> initialize to 1
$versioninfoPage = "1";
}
$diagramVersions = id(new DiagramVersion())->loadByDiagramID($versioninfoDiagramID);
if ($diagramVersions !== null) {
$result = [];
$viewer = $request->getViewer();
// determine total count of versions
$totalcount = count($diagramVersions);
// filter out some of the versions we want to show
$pageSize = 10;
- $diagramVersions = array_slice($diagramVersions,
+ $diagramVersions = array_slice($diagramVersions,
($versioninfoPage - 1) * $pageSize,
$pageSize
);
// calculate number of pages
$totalpages = ceil($totalcount / $pageSize);
// create menu-items
foreach ($diagramVersions as $diagramVersion) {
$author = $diagramVersion->getAuthorPHID();
$user = id(new PhabricatorPeopleQuery())
->setViewer($viewer)
->withPHIDs(array(
$author
))
->executeOne();
$dateModified = $diagramVersion->getDateModified();
$result[] = array(
"id" => $diagramVersion->getVersion(),
"datetime" => phabricator_datetime($dateModified, $viewer),
"author" => $user->getUsername()
);
}
// reply back
$response = id(new AphrontJSONResponse())->setAddJSONShield(false)
->setContent(array(
"data" => $result,
"pagecount" => $totalpages,
"nopager" => $totalcount <= $pageSize
));
return $response;
} else {
// version info requested for inexistant diagram
$response = id(new AphrontJSONResponse())->setAddJSONShield(false)
->setContent(array(
"data" => array(),
"pagecount" => 0
));
return $response;
}
}
$root = '';
$file = rtrim($request->getPath(), '/');
$root = dirname(phutil_get_library_root('diagram'));
// determine from which application area the file should be loaded:
// 1) Phorge extension source
// or 2) drawio source
if ($route == 'iframe') {
// load from drawio source
if ($file == '/diagram/iframe')
$file .= '/index.html';
if ($versioneddiagramid != null && $version != null) {
$file = preg_replace("/^\/diagram\/" . $versioneddiagramid . "\/" . $version ."iframe\//",
"data/drawio/src/main/webapp/",
$file);
} else {
$file = preg_replace("/^\/diagram\/(" . $diagramid . "\/?)?iframe\//",
"data/drawio/src/main/webapp/",
$file);
}
} else {
// load from extension source
if (rtrim($file, '/') == '/diagram') {
return $this->showApplication($request);
}
if ($versioneddiagramid !== null && $version !== null) {
- $file = preg_replace('/^\/diagram\/' . $versioneddiagramid . '\/'. $version . '\/?/',
- 'data/',
+ $file = preg_replace('/^\/diagram\/' . $versioneddiagramid . '\/'. $version . '\/?/',
+ 'data/',
$file
);
$file = rtrim($file, '/') . '/' . $versioneddiagramid;
} else {
- $file = preg_replace('/^\/diagram\/(' . $diagramid . '\/)?/',
- 'data/',
+ $file = preg_replace('/^\/diagram\/(' . $diagramid . '\/)?/',
+ 'data/',
$file
);
}
}
// determine full path
$path = $root . '/' . $file;
if (file_exists($path) == false || is_readable($path) == false) {
if (preg_match('/^data\/DIAG(\d+)$/', $file, $matches)) {
$diagram_id = (int) $matches[1];
if ($version === null) {
$diagram = id(new DiagramVersion())->loadLatestByDiagramID($diagram_id);
} else {
$diagram = id(new DiagramVersion())->loadByVersionedDiagramID($diagram_id, $version);
}
if ($diagram) {
$data = $diagram->getData();
$base64_data = base64_encode($data);
return $this->showApplication(
$request,
'DIAG' . $diagram_id,
- $version ?? "",
+ $version ?? "",
$base64_data
);
}
}
// Invalid URL
$response = id(new Aphront404Response());
return $response;
} else {
// process Iframe content
switch (pathinfo($file, PATHINFO_EXTENSION)) {
case 'html':
$response = id(new PlainHtmlWebpageResponse())
->setFrameable(true)
->setContent(file_get_contents($path));
break;
case 'js':
$response = new AphrontFileResponse();
$response->setMimeType('application/javascript');
break;
case 'css':
$response = new AphrontFileResponse();
$response->setMimeType('text/css');
break;
case 'txt':
$response = new AphrontFileResponse();
$response->setMimeType('text/plain');
break;
case 'png':
$response = new AphrontFileResponse();
$response->setMimeType('image/png');
break;
case 'gif':
$response = new AphrontFileResponse();
$response->setMimeType('image/gif');
break;
case 'jpg':
case 'jpeg':
$response = new AphrontFileResponse();
$response->setMimeType('image/jpeg');
break;
default:
$response = new AphrontFileResponse();
$response->setMimeType('application/octet-stream');
break;
}
try {
$response->setContent(file_get_contents($path));
} catch (Exception $e) {
$response->setContent($route);
}
return $response;
}
}
/**
* Compares the draw.io tEXt metadata from 2 PNG base64 strings.
* The content looks like this:
* <mxfile host="..." modified="..." agent="..." etag="..." version="..."...>
* <diagram id="O253AQcHVgjdl_wygdBA" name="Page-1">
* <mxGraphModel ...>
* <root>
* ...
* </mxCell>
* </root>
* </mxGraphModel>
* </diagram>
* </mxfile>
- *
+ *
* The modified and etag attributes of mxfile will always be different.
* They are cut out before the 2 strings are compared.
*/
- private function equalPngMetaData($base64_1, $base64_2) {
+ public static function equalPngMetaData($base64_1, $base64_2) {
$base64 = array($base64_1, $base64_2);
$textData = array();
for ($i = 0; $i < 2; $i++) {
$data = base64_decode($base64[$i]);
$fp = fopen('data://text/plain;base64,' . base64_encode($data), 'rb');
$sig = fread($fp, 8);
if ($sig != "\x89PNG\x0d\x0a\x1a\x0a") {
fclose($fp);
return false;
}
$textData[$i] = array();
while (!feof($fp)) {
- $chunk = unpack('Nlength/a4type', fread($fp, 8));
+ try {
+ $chunk = unpack('Nlength/a4type', fread($fp, 8));
+ } catch (Exception $e) {
+ // invalid base64 data
+ return false;
+ }
if ($chunk['type'] == 'IEND') break;
if ($chunk['type'] == 'tEXt') {
list($key, $val) = explode("\0", fread($fp, $chunk['length']));
if ($key == 'mxfile') {
// Decode the URL-encoded XML data
$decodedVal = urldecode($val);
// Load the XML and remove the modified and etag attributes
$xml = simplexml_load_string($decodedVal);
unset($xml->attributes()->modified);
unset($xml->attributes()->etag);
// Save the modified XML as the value
$val = $xml->asXML();
}
$textData[$i][$key] = $val;
fseek($fp, 4, SEEK_CUR);
} else {
fseek($fp, $chunk['length'] + 4, SEEK_CUR);
}
}
fclose($fp);
}
if (isset($textData[0]['mxfile']) && isset($textData[1]['mxfile'])) {
// Both arrays contain the mxfile key, compare their values
return $textData[0]['mxfile'] == $textData[1]['mxfile'];
} else {
// At least one of the arrays does not contain the mxfile key, return false
return false;
}
}
/**
* Processes HTTP POST calls from Diagram application, like 'Save' action
*/
private function handleHttpPostCall(AphrontRequest $request) {
$base64_data = $request->getStr("data");
$diagram_id = $request->getStr("diagramID");
// cut off "data:image/png;base64,"
$base64_data = substr($base64_data, strpos($base64_data, ',') + 1);
if ($diagram_id != "") {
// check if we are trying to save the same data as the current data
$diagram = id(new DiagramVersion())->loadLatestByDiagramID($diagram_id);
if ($diagram !== null) {
$data = $diagram->getData();
- $old_base64_data = base64_encode($data);
+ $old_data = base64_encode($data);
- if ($this->equalPngMetaData($base64_data, $old_base64_data)) {
+ if (DiagramController::equalPngMetaData($base64_data, $old_data)) {
// data hasn't been modified
// => do not create new version
$response = id(new AphrontJSONResponse())->setAddJSONShield(false)
->setContent(array(
"Status" => "OK",
"DiagramID" => $diagram->getDiagramID(),
"Version" => $diagram->getVersion()
));
return $response;
}
}
}
-
+
// Set the options for the new file
$options = array(
'name' => 'diagram.png',
'viewPolicy' => PhabricatorPolicies::POLICY_USER,
'mime-type' => 'image/png',
'actor' => $this->getViewer(),
'diagramID' => $diagram_id
);
try {
// Create the new file object
$diagram = DiagramVersion::newFromFileData($base64_data, $options);
$response = id(new AphrontJSONResponse())->setAddJSONShield(false)
->setContent(array(
"Status" => "OK",
"DiagramID" => $diagram->getDiagramID(),
"Version" => $diagram->getVersion()
));
return $response;
} catch (Exception $e) {
$response = id(new AphrontJSONResponse())->setAddJSONShield(false)
->setContent(array(
"Status" => "ERROR",
"Error" => $e->getMessage(),
));
return $response;
}
}
+ /**
+ * Verifies if the given base64 data is draw.io compatible
+ */
+ public static function isDrawioPngBase64($base64) {
+ $data = base64_decode($base64);
+ $fp = fopen('data://text/plain;base64,' . base64_encode($data), 'rb');
+ $sig = fread($fp, 8);
+ if ($sig != "\x89PNG\x0d\x0a\x1a\x0a") {
+ fclose($fp);
+ return false;
+ }
+ while (!feof($fp)) {
+ try {
+ $chunk = unpack('Nlength/a4type', fread($fp, 8));
+ } catch (Exception $e) {
+ // invalid base64 data
+ return false;
+ }
+ if ($chunk['type'] == 'IEND') break;
+ if ($chunk['type'] == 'tEXt') {
+ list($key, $val) = explode("\0", fread($fp, $chunk['length']));
+ if ($key == 'mxfile') {
+ fclose($fp);
+ return true;
+ }
+ fseek($fp, 4, SEEK_CUR);
+ } else {
+ fseek($fp, $chunk['length'] + 4, SEEK_CUR);
+ }
+ }
+ fclose($fp);
+ return false;
+ }
+
/**
* Shows the draw.io application integrated in Phorge's layout
*/
private function showApplication(
AphrontRequest $request,
string $diagramName = null,
string $diagramVersion = null,
string $diagramBase64 = null
) {
$content = phutil_tag(
'div',
array(),
array(
phutil_tag(
'div',
array(
'id' => 'mainScreen',
)),
phutil_tag('div',
array(),
array(
phutil_tag(
'img',
array(
'class' => 'drawio',
)),
phutil_tag('div',
array(
'id' => 'loadingtext',
'class' => 'geBlock',
'style' => 'margin-top:80px;'
. 'text-align:center;'
. 'min-width:50%;'
. 'height:100vh;',
),
array(
phutil_tag('h1',
array(),
'Flowchart Maker and Online Diagram Software'
),
phutil_tag('p',
array(
'style' => 'width: 800px;'
. 'position: sticky;'
. 'left: calc(50% - 400px);',
),
'draw.io is free online diagram software. '
. 'You can use it as a flowchart maker, network diagram '
. 'software, to create UML online, as an ER diagram tool, '
. 'to design database schema, to build BPMN online, as a '
. 'circuit diagram maker, and more. draw.io can import '
. '.vsdx, Gliffy™ and Lucidchart™ files . '
),
phutil_tag(
'h2',
array(
'id' => 'geStatus',
),
'Loading...'
),
phutil_tag(
'div',
array(
'id' => 'spinnerLoading',
)),
phutil_tag(
'script',
array(),
'var spinnerOpts = {'
. 'hwaccel: false,'
. 'length: 24,'
. 'radius: 12,'
. 'shadow: false,'
. 'speed: 1.5,'
. 'trail: 60,'
. 'width: 8};'
)
))
)),
phutil_tag(
'script',
array(
'src' => 'phorge_extension.js'
),
''
),
phutil_tag(
'script',
array(),
phutil_safe_html('loadJsExtension("'
. $diagramName
. '", "'
. $diagramVersion
. '", "'
. $diagramBase64
. '");')
),
phutil_tag(
'div',
array(
'class' => 'crumbs',
'style' => 'top:48px;'
. 'margin-left: 4px;'
. 'position: fixed;'
. 'font-weight: bold;'
),
array(
phutil_tag(
'a',
array(
'href' => '.'
),
array(
phutil_tag(
'span',
array(
'class' => 'phui-font-fa fa-sitemap',
'style' => 'padding-right:5px;'
))
)),
phutil_tag(
'a',
array(
'href' => '.'
),
'Diagram'
),
phutil_tag(
'span',
array(
'class' => 'diagramName',
'style' => 'display:none'
),
array(
phutil_tag(
'span',
array(
'style' => 'margin: 5px;'
. 'opacity: .5;'
),
'>'
),
phutil_tag(
'a',
array(),
''
),
phutil_tag(
'span',
array(
'class' => 'version',
'style' => 'margin-left: 8px;'
- . 'color: #999;'
- ),
- '',
- ),
+ . 'color: #999;'),
+ ''),
))
))
));
$view = id(new PhabricatorStandardPageView())
->setRequest($request)
->setController($this)
->setDeviceReady(true)
->setTitle("Diagrams")
->appendChild($content);
$response = id(new AphrontWebpageResponse())
->setContent($view->render());
return $response;
}
-}
\ No newline at end of file
+}
diff --git a/src/query/PhabricatorDiagramQuery.php b/src/query/PhabricatorDiagramQuery.php
index 8302219..c3627c3 100644
--- a/src/query/PhabricatorDiagramQuery.php
+++ b/src/query/PhabricatorDiagramQuery.php
@@ -1,73 +1,108 @@
<?php
final class PhabricatorDiagramQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
protected $diagramIDs;
+ protected $modifiedAfter;
+ protected $modifiedBefore;
public function withDiagramIDs(array $diagram_ids) {
$this->diagramIDs = $diagram_ids;
return $this;
}
+ public function withIDs(array $diagram_ids) {
+ return $this->withDiagramIDs($diagram_ids);
+ }
+
+ public function withModifiedAfter($datetime) {
+ $this->modifiedAfter = $datetime;
+ return $this;
+ }
+
+ public function withModifiedBefore($datetime) {
+ $this->modifiedBefore = $datetime;
+ return $this;
+ }
+
protected function loadPage() {
$table = new DiagramVersion();
$conn_r = $table->establishConnection('r');
// we return a DiagramVersion object which has a different id
// than the one we mention in the Remarkup code.
// E.g. {DIAG1} may point to the 2nd version of the object.
// Diagram's id is 1, but DiagramVersion's id is 2.
// Because of this we abuse the id in the resultset a little bit
$data = queryfx_all(
$conn_r,
- "SELECT result.diagramID AS id, /* abuse */
- result.diagramID,
- result.version,
- result.phid,
- result.authorPHID,
- result.dateCreated,
- result.dateModified,
- result.byteSize,
- result.data,
- result.viewPolicy,
- result.editPolicy
+ 'SELECT *
FROM (
- SELECT data.*
- FROM %T data
- INNER JOIN (
- SELECT MAX(id) AS id,
- diagramid
- FROM %T
- GROUP BY diagramid HAVING MAX(id)
- ) filter
- ON data.id = filter.id
- ) result %Q %Q %Q",
+ SELECT result.diagramID AS id, /* abuse */
+ result.diagramID,
+ result.version,
+ result.phid,
+ result.authorPHID,
+ result.dateCreated,
+ result.dateModified,
+ result.byteSize,
+ result.data,
+ result.viewPolicy,
+ result.editPolicy
+ FROM (
+ SELECT data.*
+ FROM %T data
+ INNER JOIN (
+ SELECT MAX(id) AS id,
+ diagramid
+ FROM %T
+ GROUP BY diagramid HAVING MAX(id)
+ ) filter
+ ON data.id = filter.id
+ ) result
+ ) r %Q %Q %Q',
$table->getTableName(),
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
}
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
+ $where[] = $this->buildPagingClause($conn_r);
+
if ($this->diagramIDs !== null) {
$where[] = qsprintf(
$conn_r,
'diagramID IN (%Ld)',
$this->diagramIDs);
}
+ if ($this->modifiedAfter !== null) {
+ $where[] = qsprintf(
+ $conn_r,
+ 'dateModified >= %d',
+ $this->modifiedAfter);
+ }
+
+ if ($this->modifiedBefore !== null) {
+ $where[] = qsprintf(
+ $conn_r,
+ 'dateModified <= %d',
+ $this->modifiedBefore);
+ }
+
return $this->formatWhereClause($conn_r, $where);
}
public function getQueryApplicationClass() {
return DiagramApplication::class;
}
}
diff --git a/src/storage/DiagramVersion.php b/src/storage/DiagramVersion.php
index be98448..e896141 100644
--- a/src/storage/DiagramVersion.php
+++ b/src/storage/DiagramVersion.php
@@ -1,342 +1,371 @@
<?php
final class DiagramVersion extends DiagramDAO implements
PhabricatorDestructibleInterface,
PhabricatorPolicyInterface {
/**
* List of properties mapped to database table columns
*/
protected $phid;
protected $diagramID;
protected $version;
protected $authorPHID;
protected $byteSize;
protected $data;
protected $viewPolicy;
protected $editPolicy;
+ /**
+ * returns base64 of $data
+ */
+ public function getBase64Data() {
+ if ($this->data === null) {
+ return null;
+ }
+ return base64_encode($this->data);
+ }
+
+ /**
+ * returns the URL which links to the diagram PNG data
+ */
+ public function getDataURI() {
+ return PhabricatorEnv::getCDNURI(
+ '/diagram/data/'
+ .$this->getPHID());
+ }
+
/**
* Return an array of capabilities that this object type supports.
* See PhabricatorPolicyCapability for a list of available capabilities.
- *
+ *
* Interface: PhabricatorPolicyInterface
*/
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
/**
* Return the policy for the given capability.
- *
+ *
* Interface: PhabricatorPolicyInterface
*/
public function getPolicy($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return $this->viewPolicy;
case PhabricatorPolicyCapability::CAN_EDIT:
return $this->editPolicy;
default:
return PhabricatorPolicies::POLICY_NOONE;
}
}
/**
* Return the URL which links to the diagram PNG image
*/
public function getViewURI() {
if (!$this->getPHID()) {
throw new Exception(
pht('You must save a diagram before you can generate a view URI.')
);
}
$uri = '/diagram/data/'
- . $this->getPHID();
+ .$this->getPHID();
return $uri;
}
/**
* Configures application-wide storage settings.
* This creates a mapping of the corresponding database table.
*/
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_COLUMN_SCHEMA => array(
'diagramID' => 'uint32',
'version' => 'uint32',
'authorPHID' => 'phid',
'byteSize' => 'uint64',
'data' => 'bytes',
'viewPolicy' => 'policy',
'editPolicy' => 'policy',
),
self::CONFIG_KEY_SCHEMA => array(
'key_phid' => null,
'key_diagramID_version' => array(
'columns' => array('diagramID', 'version'),
'unique' => true,
),
'key_authorPHID' => array(
'columns' => array('authorPHID'),
),
),
) +
array(
self::CONFIG_IDS => self::IDS_AUTOINCREMENT,
self::CONFIG_TIMESTAMPS => true,
);
}
/**
* Return the name of the database table that is represented by this class
*/
public function getTableName() {
return 'diagram_version';
}
/**
* Return a string that uniquely identifies the PHID type for this object
* type. This is used by the PHID system to generate and manage PHIDs for
* this object type.
*/
public function getPHIDType() {
return 'DGVN';
}
/**
* Return true if the given user has the given capability automatically,
* without needing to check the object's policy. For example, you might
* return true here if the user is an administrator or if they own the
* object.
- *
+ *
* Interface: PhabricatorPolicyInterface
*/
public function hasAutomaticCapability(
$capability,
- PhabricatorUser $viewer
- ) {
+ PhabricatorUser $viewer) {
return false;
}
+ /**
+ * Creates and initializes a new DiagramVersion object
+ */
public static function initializeNewDiagram(PhabricatorUser $actor) {
- return id(new DiagramVersion())
+ return id(new self())
->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy())
->setEditPolicy($actor->getPHID())
->setAuthorPHID($actor->getPHID())
->setVersion(1)
->setDateCreated(time())
->setDateModified(time());
}
+ /**
+ * Creates a new DiagramVersion object and loads the given base64 data in it
+ */
public static function newFromFileData(
$base64_data,
- array $params = array()
- ) {
+ array $params = array()) {
$actor = idx($params, 'actor');
if (!$actor) {
throw new Exception(pht('Missing required actor for new file data.'));
}
$diagramID = idx($params, 'diagramID');
if (!is_numeric($diagramID)) {
$diagramID = null;
}
$data = base64_decode($base64_data);
$diagram = self::initializeNewDiagram($actor);
$diagram->setByteSize(strlen($data));
$diagram->setData($data);
$diagram->setDiagramID($diagramID);
$diagram->save();
return $diagram;
}
- public function attachProjectPHIDs(array $phids) {
- // Attach an array of project PHIDs to this object. This is used by the
- // project system to manage project membership and visibility for this
- // object.
- }
-
+ /**
+ * Permanently destroy this object. This is used by the destructible
+ * interface to allow administrators to permanently delete objects from
+ * the system.
+ */
public function destroyObjectPermanently(
- PhabricatorDestructionEngine $engine
- ) {
- // Permanently destroy this object. This is used by the destructible
- // interface to allow administrators to permanently delete objects from
- // the system.
+ PhabricatorDestructionEngine $engine) {
$this->openTransaction();
$this->delete();
$this->saveTransaction();
}
+ /**
+ * Returns an ID which can be used for a newly created Diagram object
+ */
public function generateDiagramID() {
$conn_r = $this->establishConnection('r');
$table_name = $this->getTableName();
$max_diagram_id = queryfx_one(
$conn_r,
'SELECT MAX(diagramID) max_diagram_id FROM %T',
- $table_name
- )['max_diagram_id'];
- return (int) $max_diagram_id + 1;
+ $table_name)['max_diagram_id'];
+ return (int)$max_diagram_id + 1;
}
+ /**
+ * Returns all DiagramVersion objects for a given diagram
+ */
public function loadByDiagramID($diagramID) {
if (is_object($diagramID)) {
- $diagramID = (string) $diagramID;
+ $diagramID = (string)$diagramID;
}
if (!$diagramID || (!is_int($diagramID) && !ctype_digit($diagramID))) {
return null;
}
return $this->loadAllWhere(
'diagramID = %d ORDER BY version DESC',
- $diagramID
- );
+ $diagramID);
}
+ /**
+ * Returns the latest version of the given diagram
+ */
public function loadLatestByDiagramID($diagramID) {
if (is_object($diagramID)) {
- $diagramID = (string) $diagramID;
+ $diagramID = (string)$diagramID;
}
if (!$diagramID || (!is_int($diagramID) && !ctype_digit($diagramID))) {
return null;
}
return $this->loadOneWhere(
'diagramID = %d ORDER BY version DESC LIMIT 1',
- $diagramID
- );
+ $diagramID);
}
- public function loadByDiagramPHID($diagramPHID) {
- if (is_object($diagramPHID)) {
- $diagramPHID = (string) $diagramPHID;
+ /**
+ * Returns a specific DiagramVersion object
+ */
+ public function loadByDiagramPHID($diagramVersionPHID) {
+ if (is_object($diagramVersionPHID)) {
+ $diagramVersionPHID = (string)$diagramVersionPHID;
}
return $this->loadOneWhere(
'phid = %s ORDER BY version DESC LIMIT 1',
- $diagramPHID
- );
+ $diagramVersionPHID);
}
+ /**
+ * Returns a specific DiagramVersion object for a given diagram and
+ * version number
+ */
public function loadByVersionedDiagramID($diagramID, $version) {
if (is_object($diagramID)) {
- $diagramID = (string) $diagramID;
+ $diagramID = (string)$diagramID;
}
if (is_object($version)) {
- $version = (string) $version;
+ $version = (string)$version;
}
if (!$diagramID || (!is_int($diagramID) && !ctype_digit($diagramID))) {
return null;
}
if (!$version || (!is_int($version) && !ctype_digit($version))) {
return null;
}
return $this->loadOneWhere(
'diagramID = %d AND version = %d',
$diagramID,
- $version
- );
+ $version);
}
+ /**
+ * Stores a new diagram (version)
+ */
public function save() {
// Load the last record with the same PHID.
$last_record = null;
if ($this->getDiagramID() !== null) {
$last_record = id(new PhabricatorDiagramQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withDiagramIDs(array($this->getDiagramID()))
->setLimit(1)
->executeOne();
}
if ($last_record === null) {
// If there is no last record, this is a new diagram object.
$this->setVersion(1);
$newDiagram = new Diagram();
$newDiagram->createNewDiagram();
$this->setDiagramID($newDiagram->getID());
} else {
// If there is a last record, this is a new version of an existing
// diagram object.
$this->setVersion($last_record->getVersion() + 1);
$this->setDateCreated($last_record->getDateCreated());
}
// Check if a row with the same PHID and version already exists
$existing_record = null;
if ($this->getPHID() !== null && $this->getVersion() !== null) {
$existing_record = id(new PhabricatorDiagramQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withPHIDs(array($this->getPHID()))
->withWhere(
array(
array('version', '=', $this->getVersion()),
- )
- )
+ ))
->setLimit(1)
->executeOne();
}
if ($existing_record === null) {
// If there is no existing record, create a new row in the table.
$conn_w = $this->establishConnection('w');
$table_name = $this->getTableName();
$this->phid = $this->generatePHID();
if ($this->diagramID === null) {
$this->diagramID = $this->generateDiagramID();
}
$record = array(
'phid' => $this->getPHID(),
'diagramID' => $this->getDiagramID(),
'version' => $this->getVersion(),
'authorPHID' => $this->getAuthorPHID(),
'dateCreated' => $this->getDateCreated(),
'dateModified' => $this->getDateModified(),
'byteSize' => $this->getByteSize(),
'viewPolicy' => $this->getViewPolicy(),
'editPolicy' => $this->getEditPolicy(),
'data' => $this->getData(),
);
if ($this->getID() !== null) {
// If the ID property is set, include it in the data to insert.
$record['id'] = $this->getID();
}
queryfx(
$conn_w,
'INSERT INTO %T (%Q) VALUES (%Ls, %B)',
$table_name,
implode(', ', array_keys($record)),
array_values(array_slice($record, 0, -1)),
- end($record)
- );
+ end($record));
$this->id = $conn_w->getInsertID();
} else {
// If there is an existing record, throw an exception.
throw new Exception(
pht('A diagram with PHID "%s" and version "%s" already exists.',
$this->getPHID(),
- $this->getVersion()
- )
+ $this->getVersion())
);
}
return $this;
}
-}
\ No newline at end of file
+}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Dec 19, 16:28 (18 h, 52 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1014820
Default Alt Text
(33 KB)

Event Timeline