Remove product literal strings in "pht()", part 6
[phabricator.git] / src / applications / phragment / conduit / PhragmentGetPatchConduitAPIMethod.php
bloba0301903437b0bf21d3ccdb8bd7a7ed977831257
1 <?php
3 final class PhragmentGetPatchConduitAPIMethod
4 extends PhragmentConduitAPIMethod {
6 public function getAPIMethodName() {
7 return 'phragment.getpatch';
10 public function getMethodStatus() {
11 return self::METHOD_STATUS_UNSTABLE;
14 public function getMethodDescription() {
15 return pht('Retrieve the patches to apply for a given set of files.');
18 protected function defineParamTypes() {
19 return array(
20 'path' => 'required string',
21 'state' => 'required dict<string, string>',
25 protected function defineReturnType() {
26 return 'nonempty dict';
29 protected function defineErrorTypes() {
30 return array(
31 'ERR_BAD_FRAGMENT' => pht('No such fragment exists.'),
35 protected function execute(ConduitAPIRequest $request) {
36 $path = $request->getValue('path');
37 $state = $request->getValue('state');
38 // The state is an array mapping file paths to hashes.
40 $patches = array();
42 // We need to get all of the mappings (like phragment.getstate) first
43 // so that we can detect deletions and creations of files.
44 $fragment = id(new PhragmentFragmentQuery())
45 ->setViewer($request->getUser())
46 ->withPaths(array($path))
47 ->executeOne();
48 if ($fragment === null) {
49 throw new ConduitException('ERR_BAD_FRAGMENT');
52 $mappings = $fragment->getFragmentMappings(
53 $request->getUser(),
54 $fragment->getPath());
56 $file_phids = mpull(mpull($mappings, 'getLatestVersion'), 'getFilePHID');
57 $files = id(new PhabricatorFileQuery())
58 ->setViewer($request->getUser())
59 ->withPHIDs($file_phids)
60 ->execute();
61 $files = mpull($files, null, 'getPHID');
63 // Scan all of the files that the caller currently has and iterate
64 // over that.
65 foreach ($state as $path => $hash) {
66 // If $mappings[$path] exists, then the user has the file and it's
67 // also a fragment.
68 if (array_key_exists($path, $mappings)) {
69 $file_phid = $mappings[$path]->getLatestVersion()->getFilePHID();
70 if ($file_phid !== null) {
71 // If the file PHID is present, then we need to check the
72 // hashes to see if they are the same.
73 $hash_caller = strtolower($state[$path]);
74 $hash_current = $files[$file_phid]->getContentHash();
75 if ($hash_caller === $hash_current) {
76 // The user's version is identical to our version, so
77 // there is no update needed.
78 } else {
79 // The hash differs, and the user needs to update.
80 $patches[] = array(
81 'path' => $path,
82 'fileOld' => null,
83 'fileNew' => $files[$file_phid],
84 'hashOld' => $hash_caller,
85 'hashNew' => $hash_current,
86 'patchURI' => null,
89 } else {
90 // We have a record of this as a file, but there is no file
91 // attached to the latest version, so we consider this to be
92 // a deletion.
93 $patches[] = array(
94 'path' => $path,
95 'fileOld' => null,
96 'fileNew' => null,
97 'hashOld' => $hash_caller,
98 'hashNew' => PhragmentPatchUtil::EMPTY_HASH,
99 'patchURI' => null,
102 } else {
103 // If $mappings[$path] does not exist, then the user has a file,
104 // and we have absolutely no record of it what-so-ever (we haven't
105 // even recorded a deletion). Assuming most applications will store
106 // some form of data near their own files, this is probably a data
107 // file relevant for the application that is not versioned, so we
108 // don't tell the client to do anything with it.
112 // Check the remaining files that we know about but the caller has
113 // not reported.
114 foreach ($mappings as $path => $child) {
115 if (array_key_exists($path, $state)) {
116 // We have already evaluated this above.
117 } else {
118 $file_phid = $mappings[$path]->getLatestVersion()->getFilePHID();
119 if ($file_phid !== null) {
120 // If the file PHID is present, then this is a new file that
121 // we know about, but the caller does not. We need to tell
122 // the caller to create the file.
123 $hash_current = $files[$file_phid]->getContentHash();
124 $patches[] = array(
125 'path' => $path,
126 'fileOld' => null,
127 'fileNew' => $files[$file_phid],
128 'hashOld' => PhragmentPatchUtil::EMPTY_HASH,
129 'hashNew' => $hash_current,
130 'patchURI' => null,
132 } else {
133 // We have a record of deleting this file, and the caller hasn't
134 // reported it, so they've probably deleted it in a previous
135 // update.
140 // Before we can calculate patches, we need to resolve the old versions
141 // of files so we can draw diffs on them.
142 $hashes = array();
143 foreach ($patches as $patch) {
144 if ($patch['hashOld'] !== PhragmentPatchUtil::EMPTY_HASH) {
145 $hashes[] = $patch['hashOld'];
148 $old_files = array();
149 if (count($hashes) !== 0) {
150 $old_files = id(new PhabricatorFileQuery())
151 ->setViewer($request->getUser())
152 ->withContentHashes($hashes)
153 ->execute();
155 $old_files = mpull($old_files, null, 'getContentHash');
156 foreach ($patches as $key => $patch) {
157 if ($patch['hashOld'] !== PhragmentPatchUtil::EMPTY_HASH) {
158 if (array_key_exists($patch['hashOld'], $old_files)) {
159 $patches[$key]['fileOld'] = $old_files[$patch['hashOld']];
160 } else {
161 // We either can't see or can't read the old file.
162 $patches[$key]['hashOld'] = PhragmentPatchUtil::EMPTY_HASH;
163 $patches[$key]['fileOld'] = null;
168 // Now run through all of the patch entries, calculate the patches
169 // and return the results.
170 foreach ($patches as $key => $patch) {
171 $data = PhragmentPatchUtil::calculatePatch(
172 $patches[$key]['fileOld'],
173 $patches[$key]['fileNew']);
174 unset($patches[$key]['fileOld']);
175 unset($patches[$key]['fileNew']);
177 $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
178 $file = PhabricatorFile::newFromFileData(
179 $data,
180 array(
181 'name' => 'patch.dmp',
182 'ttl.relative' => phutil_units('24 hours in seconds'),
184 unset($unguarded);
186 $patches[$key]['patchURI'] = $file->getDownloadURI();
189 return $patches;