@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1<?php
2
3final class PhabricatorVersionedDraft extends PhabricatorDraftDAO {
4
5 const KEY_VERSION = 'draft.version';
6
7 protected $objectPHID;
8 protected $authorPHID;
9 protected $version;
10 protected $properties = array();
11
12 protected function getConfiguration() {
13 return array(
14 self::CONFIG_SERIALIZATION => array(
15 'properties' => self::SERIALIZATION_JSON,
16 ),
17 self::CONFIG_COLUMN_SCHEMA => array(
18 'version' => 'uint32',
19 ),
20 self::CONFIG_KEY_SCHEMA => array(
21 'key_object' => array(
22 'columns' => array('objectPHID', 'authorPHID', 'version'),
23 'unique' => true,
24 ),
25 ),
26 ) + parent::getConfiguration();
27 }
28
29 public function setProperty($key, $value) {
30 $this->properties[$key] = $value;
31 return $this;
32 }
33
34 public function getProperty($key, $default = null) {
35 return idx($this->properties, $key, $default);
36 }
37
38 public static function loadDrafts(
39 array $object_phids,
40 $viewer_phid) {
41
42 $rows = id(new self())->loadAllWhere(
43 'objectPHID IN (%Ls) AND authorPHID = %s ORDER BY version ASC',
44 $object_phids,
45 $viewer_phid);
46
47 $map = array();
48 foreach ($rows as $row) {
49 $map[$row->getObjectPHID()] = $row;
50 }
51
52 return $map;
53 }
54
55 public static function loadDraft(
56 $object_phid,
57 $viewer_phid) {
58
59 return id(new PhabricatorVersionedDraft())->loadOneWhere(
60 'objectPHID = %s AND authorPHID = %s ORDER BY version DESC LIMIT 1',
61 $object_phid,
62 $viewer_phid);
63 }
64
65 public static function loadOrCreateDraft(
66 $object_phid,
67 $viewer_phid,
68 $version) {
69
70 $draft = self::loadDraft($object_phid, $viewer_phid);
71 if ($draft) {
72 return $draft;
73 }
74
75 try {
76 return id(new self())
77 ->setObjectPHID($object_phid)
78 ->setAuthorPHID($viewer_phid)
79 ->setVersion((int)$version)
80 ->save();
81 } catch (AphrontDuplicateKeyQueryException $ex) {
82 $duplicate_exception = $ex;
83 }
84
85 // In rare cases we can race ourselves, and at one point there was a bug
86 // which caused the browser to submit two preview requests at exactly
87 // the same time. If the insert failed with a duplicate key exception,
88 // try to load the colliding row to recover from it.
89
90 $draft = self::loadDraft($object_phid, $viewer_phid);
91 if ($draft) {
92 return $draft;
93 }
94
95 throw $duplicate_exception;
96 }
97
98 public static function purgeDrafts(
99 $object_phid,
100 $viewer_phid) {
101
102 $draft = new PhabricatorVersionedDraft();
103 $conn_w = $draft->establishConnection('w');
104
105 queryfx(
106 $conn_w,
107 'DELETE FROM %T WHERE objectPHID = %s AND authorPHID = %s',
108 $draft->getTableName(),
109 $object_phid,
110 $viewer_phid);
111 }
112
113}