[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 <?php 2 3 final class PhragmentFragment extends PhragmentDAO 4 implements PhabricatorPolicyInterface { 5 6 protected $path; 7 protected $depth; 8 protected $latestVersionPHID; 9 protected $viewPolicy; 10 protected $editPolicy; 11 12 private $latestVersion = self::ATTACHABLE; 13 14 public function getConfiguration() { 15 return array( 16 self::CONFIG_AUX_PHID => true, 17 self::CONFIG_COLUMN_SCHEMA => array( 18 'path' => 'text128', 19 'depth' => 'uint32', 20 'latestVersionPHID' => 'phid?', 21 ), 22 self::CONFIG_KEY_SCHEMA => array( 23 'key_path' => array( 24 'columns' => array('path'), 25 'unique' => true, 26 ), 27 ), 28 ) + parent::getConfiguration(); 29 } 30 31 public function generatePHID() { 32 return PhabricatorPHID::generateNewPHID( 33 PhragmentFragmentPHIDType::TYPECONST); 34 } 35 36 public function getURI() { 37 return '/phragment/browse/'.$this->getPath(); 38 } 39 40 public function getName() { 41 return basename($this->path); 42 } 43 44 public function getFile() { 45 return $this->assertAttached($this->file); 46 } 47 48 public function attachFile(PhabricatorFile $file) { 49 return $this->file = $file; 50 } 51 52 public function isDirectory() { 53 return $this->latestVersionPHID === null; 54 } 55 56 public function isDeleted() { 57 return $this->getLatestVersion()->getFilePHID() === null; 58 } 59 60 public function getLatestVersion() { 61 if ($this->latestVersionPHID === null) { 62 return null; 63 } 64 return $this->assertAttached($this->latestVersion); 65 } 66 67 public function attachLatestVersion(PhragmentFragmentVersion $version) { 68 return $this->latestVersion = $version; 69 } 70 71 72 /* -( Updating ) --------------------------------------------------------- */ 73 74 75 /** 76 * Create a new fragment from a file. 77 */ 78 public static function createFromFile( 79 PhabricatorUser $viewer, 80 PhabricatorFile $file = null, 81 $path, 82 $view_policy, 83 $edit_policy) { 84 85 $fragment = id(new PhragmentFragment()); 86 $fragment->setPath($path); 87 $fragment->setDepth(count(explode('/', $path))); 88 $fragment->setLatestVersionPHID(null); 89 $fragment->setViewPolicy($view_policy); 90 $fragment->setEditPolicy($edit_policy); 91 $fragment->save(); 92 93 // Directory fragments have no versions associated with them, so we 94 // just return the fragment at this point. 95 if ($file === null) { 96 return $fragment; 97 } 98 99 if ($file->getMimeType() === 'application/zip') { 100 $fragment->updateFromZIP($viewer, $file); 101 } else { 102 $fragment->updateFromFile($viewer, $file); 103 } 104 105 return $fragment; 106 } 107 108 109 /** 110 * Set the specified file as the next version for the fragment. 111 */ 112 public function updateFromFile( 113 PhabricatorUser $viewer, 114 PhabricatorFile $file) { 115 116 $existing = id(new PhragmentFragmentVersionQuery()) 117 ->setViewer($viewer) 118 ->withFragmentPHIDs(array($this->getPHID())) 119 ->execute(); 120 $sequence = count($existing); 121 122 $this->openTransaction(); 123 $version = id(new PhragmentFragmentVersion()); 124 $version->setSequence($sequence); 125 $version->setFragmentPHID($this->getPHID()); 126 $version->setFilePHID($file->getPHID()); 127 $version->save(); 128 129 $this->setLatestVersionPHID($version->getPHID()); 130 $this->save(); 131 $this->saveTransaction(); 132 133 $file->attachToObject($version->getPHID()); 134 } 135 136 /** 137 * Apply the specified ZIP archive onto the fragment, removing 138 * and creating fragments as needed. 139 */ 140 public function updateFromZIP( 141 PhabricatorUser $viewer, 142 PhabricatorFile $file) { 143 144 if ($file->getMimeType() !== 'application/zip') { 145 throw new Exception("File must have mimetype 'application/zip'"); 146 } 147 148 // First apply the ZIP as normal. 149 $this->updateFromFile($viewer, $file); 150 151 // Ensure we have ZIP support. 152 $zip = null; 153 try { 154 $zip = new ZipArchive(); 155 } catch (Exception $e) { 156 // The server doesn't have php5-zip, so we can't do recursive updates. 157 return; 158 } 159 160 $temp = new TempFile(); 161 Filesystem::writeFile($temp, $file->loadFileData()); 162 if (!$zip->open($temp)) { 163 throw new Exception('Unable to open ZIP'); 164 } 165 166 // Get all of the paths and their data from the ZIP. 167 $mappings = array(); 168 for ($i = 0; $i < $zip->numFiles; $i++) { 169 $path = trim($zip->getNameIndex($i), '/'); 170 $stream = $zip->getStream($path); 171 $data = null; 172 // If the stream is false, then it is a directory entry. We leave 173 // $data set to null for directories so we know not to create a 174 // version entry for them. 175 if ($stream !== false) { 176 $data = stream_get_contents($stream); 177 fclose($stream); 178 } 179 $mappings[$path] = $data; 180 } 181 182 // We need to detect any directories that are in the ZIP folder that 183 // aren't explicitly noted in the ZIP. This can happen if the file 184 // entries in the ZIP look like: 185 // 186 // * something/blah.png 187 // * something/other.png 188 // * test.png 189 // 190 // Where there is no explicit "something/" entry. 191 foreach ($mappings as $path_key => $data) { 192 if ($data === null) { 193 continue; 194 } 195 $directory = dirname($path_key); 196 while ($directory !== '.') { 197 if (!array_key_exists($directory, $mappings)) { 198 $mappings[$directory] = null; 199 } 200 if (dirname($directory) === $directory) { 201 // dirname() will not reduce this directory any further; to 202 // prevent infinite loop we just break out here. 203 break; 204 } 205 $directory = dirname($directory); 206 } 207 } 208 209 // Adjust the paths relative to this fragment so we can look existing 210 // fragments up in the DB. 211 $base_path = $this->getPath(); 212 $paths = array(); 213 foreach ($mappings as $p => $data) { 214 $paths[] = $base_path.'/'.$p; 215 } 216 217 // FIXME: What happens when a child exists, but the current user 218 // can't see it. We're going to create a new child with the exact 219 // same path and then bad things will happen. 220 $children = id(new PhragmentFragmentQuery()) 221 ->setViewer($viewer) 222 ->needLatestVersion(true) 223 ->withLeadingPath($this->getPath().'/') 224 ->execute(); 225 $children = mpull($children, null, 'getPath'); 226 227 // Iterate over the existing fragments. 228 foreach ($children as $full_path => $child) { 229 $path = substr($full_path, strlen($base_path) + 1); 230 if (array_key_exists($path, $mappings)) { 231 if ($child->isDirectory() && $mappings[$path] === null) { 232 // Don't create a version entry for a directory 233 // (unless it's been converted into a file). 234 continue; 235 } 236 237 // The file is being updated. 238 $file = PhabricatorFile::newFromFileData( 239 $mappings[$path], 240 array('name' => basename($path))); 241 $child->updateFromFile($viewer, $file); 242 } else { 243 // The file is being deleted. 244 $child->deleteFile($viewer); 245 } 246 } 247 248 // Iterate over the mappings to find new files. 249 foreach ($mappings as $path => $data) { 250 if (!array_key_exists($base_path.'/'.$path, $children)) { 251 // The file is being created. If the data is null, 252 // then this is explicitly a directory being created. 253 $file = null; 254 if ($mappings[$path] !== null) { 255 $file = PhabricatorFile::newFromFileData( 256 $mappings[$path], 257 array('name' => basename($path))); 258 } 259 PhragmentFragment::createFromFile( 260 $viewer, 261 $file, 262 $base_path.'/'.$path, 263 $this->getViewPolicy(), 264 $this->getEditPolicy()); 265 } 266 } 267 } 268 269 /** 270 * Delete the contents of the specified fragment. 271 */ 272 public function deleteFile(PhabricatorUser $viewer) { 273 $existing = id(new PhragmentFragmentVersionQuery()) 274 ->setViewer($viewer) 275 ->withFragmentPHIDs(array($this->getPHID())) 276 ->execute(); 277 $sequence = count($existing); 278 279 $this->openTransaction(); 280 $version = id(new PhragmentFragmentVersion()); 281 $version->setSequence($sequence); 282 $version->setFragmentPHID($this->getPHID()); 283 $version->setFilePHID(null); 284 $version->save(); 285 286 $this->setLatestVersionPHID($version->getPHID()); 287 $this->save(); 288 $this->saveTransaction(); 289 } 290 291 292 /* -( Utility ) ---------------------------------------------------------- */ 293 294 295 public function getFragmentMappings( 296 PhabricatorUser $viewer, 297 $base_path) { 298 299 $children = id(new PhragmentFragmentQuery()) 300 ->setViewer($viewer) 301 ->needLatestVersion(true) 302 ->withLeadingPath($this->getPath().'/') 303 ->withDepths(array($this->getDepth() + 1)) 304 ->execute(); 305 306 if (count($children) === 0) { 307 $path = substr($this->getPath(), strlen($base_path) + 1); 308 return array($path => $this); 309 } else { 310 $mappings = array(); 311 foreach ($children as $child) { 312 $child_mappings = $child->getFragmentMappings( 313 $viewer, 314 $base_path); 315 foreach ($child_mappings as $key => $value) { 316 $mappings[$key] = $value; 317 } 318 } 319 return $mappings; 320 } 321 } 322 323 324 /* -( Policy Interface )--------------------------------------------------- */ 325 326 327 public function getCapabilities() { 328 return array( 329 PhabricatorPolicyCapability::CAN_VIEW, 330 PhabricatorPolicyCapability::CAN_EDIT, 331 ); 332 } 333 334 public function getPolicy($capability) { 335 switch ($capability) { 336 case PhabricatorPolicyCapability::CAN_VIEW: 337 return $this->getViewPolicy(); 338 case PhabricatorPolicyCapability::CAN_EDIT: 339 return $this->getEditPolicy(); 340 } 341 } 342 343 public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { 344 return false; 345 } 346 347 public function describeAutomaticCapability($capability) { 348 return null; 349 } 350 351 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Nov 30 09:20:46 2014 | Cross-referenced by PHPXref 0.7.1 |