@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
fork

Configure Feed

Select the types of activity you want to include in your feed.

Implement "Download ZIP" controller for Phragment

Summary: Depends on D7727. This adds support for downloading a fragment and all it's children as a ZIP file. Fragments that have children automatically become directories in the ZIP file.

Test Plan: Downloaded a fragment as a ZIP and was able to extract the contents successfully.

Reviewers: epriestley, #blessed_reviewers

Reviewed By: epriestley

CC: Korvin, epriestley, aran

Maniphest Tasks: T4205

Differential Revision: https://secure.phabricator.com/D7728

+120
+2
src/__phutil_library_map__.php
··· 2187 2187 'PhragmentPHIDTypeFragmentVersion' => 'applications/phragment/phid/PhragmentPHIDTypeFragmentVersion.php', 2188 2188 'PhragmentPatchUtil' => 'applications/phragment/util/PhragmentPatchUtil.php', 2189 2189 'PhragmentUpdateController' => 'applications/phragment/controller/PhragmentUpdateController.php', 2190 + 'PhragmentZIPController' => 'applications/phragment/controller/PhragmentZIPController.php', 2190 2191 'PhrequentController' => 'applications/phrequent/controller/PhrequentController.php', 2191 2192 'PhrequentDAO' => 'applications/phrequent/storage/PhrequentDAO.php', 2192 2193 'PhrequentListController' => 'applications/phrequent/controller/PhrequentListController.php', ··· 4785 4786 'PhragmentPHIDTypeFragmentVersion' => 'PhabricatorPHIDType', 4786 4787 'PhragmentPatchUtil' => 'Phobject', 4787 4788 'PhragmentUpdateController' => 'PhragmentController', 4789 + 'PhragmentZIPController' => 'PhragmentController', 4788 4790 'PhrequentController' => 'PhabricatorController', 4789 4791 'PhrequentDAO' => 'PhabricatorLiskDAO', 4790 4792 'PhrequentListController' =>
+1
src/applications/phragment/application/PhabricatorApplicationPhragment.php
··· 38 38 'create/(?P<dblob>.*)' => 'PhragmentCreateController', 39 39 'update/(?P<dblob>.*)' => 'PhragmentUpdateController', 40 40 'history/(?P<dblob>.*)' => 'PhragmentHistoryController', 41 + 'zip/(?P<dblob>.*)' => 'PhragmentZIPController', 41 42 ), 42 43 ); 43 44 }
+6
src/applications/phragment/controller/PhragmentController.php
··· 92 92 ->setIcon('download')); 93 93 $actions->addAction( 94 94 id(new PhabricatorActionView()) 95 + ->setName(pht('Download Contents as ZIP')) 96 + ->setHref($this->getApplicationURI("zip/".$fragment->getPath())) 97 + ->setDisabled(false) // TODO: Policy 98 + ->setIcon('zip')); 99 + $actions->addAction( 100 + id(new PhabricatorActionView()) 95 101 ->setName(pht('Update Fragment')) 96 102 ->setHref($this->getApplicationURI("update/".$fragment->getPath())) 97 103 ->setDisabled(false) // TODO: Policy
+111
src/applications/phragment/controller/PhragmentZIPController.php
··· 1 + <?php 2 + 3 + final class PhragmentZIPController extends PhragmentController { 4 + 5 + private $dblob; 6 + 7 + public function willProcessRequest(array $data) { 8 + $this->dblob = idx($data, "dblob", ""); 9 + } 10 + 11 + public function processRequest() { 12 + $request = $this->getRequest(); 13 + $viewer = $request->getUser(); 14 + 15 + $parents = $this->loadParentFragments($this->dblob); 16 + if ($parents === null) { 17 + return new Aphront404Response(); 18 + } 19 + $fragment = idx($parents, count($parents) - 1, null); 20 + 21 + $temp = new TempFile(); 22 + 23 + $zip = null; 24 + try { 25 + $zip = new ZipArchive(); 26 + } catch (Exception $e) { 27 + $dialog = new AphrontDialogView(); 28 + $dialog->setUser($viewer); 29 + 30 + $inst = pht( 31 + 'This system does not have the ZIP PHP extension installed. This '. 32 + 'is required to download ZIPs from Phragment.'); 33 + 34 + $dialog->setTitle(pht('ZIP Extension Not Installed')); 35 + $dialog->appendParagraph($inst); 36 + 37 + $dialog->addCancelButton('/phragment/browse/'.$this->dblob); 38 + return id(new AphrontDialogResponse())->setDialog($dialog); 39 + } 40 + 41 + if (!$zip->open((string)$temp, ZipArchive::CREATE)) { 42 + throw new Exception("Unable to create ZIP archive!"); 43 + } 44 + 45 + $mappings = $this->getFragmentMappings($fragment, $fragment->getPath()); 46 + 47 + $phids = array(); 48 + foreach ($mappings as $path => $file_phid) { 49 + $phids[] = $file_phid; 50 + } 51 + 52 + $files = id(new PhabricatorFileQuery()) 53 + ->setViewer($viewer) 54 + ->withPHIDs($phids) 55 + ->execute(); 56 + $files = mpull($files, null, 'getPHID'); 57 + foreach ($mappings as $path => $file_phid) { 58 + if (!isset($files[$file_phid])) { 59 + unset($mappings[$path]); 60 + } 61 + $mappings[$path] = $files[$file_phid]; 62 + } 63 + 64 + foreach ($mappings as $path => $file) { 65 + $zip->addFromString($path, $file->loadFileData()); 66 + } 67 + $zip->close(); 68 + 69 + $zip_name = $fragment->getName(); 70 + if (substr($zip_name, -4) !== '.zip') { 71 + $zip_name .= '.zip'; 72 + } 73 + 74 + $data = Filesystem::readFile((string)$temp); 75 + $file = PhabricatorFile::buildFromFileDataOrHash( 76 + $data, 77 + array( 78 + 'name' => $zip_name, 79 + 'ttl' => time() + 60 * 60 * 24, 80 + )); 81 + return id(new AphrontRedirectResponse()) 82 + ->setURI($file->getBestURI()); 83 + } 84 + 85 + /** 86 + * Returns a list of mappings like array('some/path.txt' => 'file PHID'); 87 + */ 88 + private function getFragmentMappings(PhragmentFragment $current, $base_path) { 89 + $children = id(new PhragmentFragmentQuery()) 90 + ->setViewer($this->getRequest()->getUser()) 91 + ->needLatestVersion(true) 92 + ->withLeadingPath($current->getPath().'/') 93 + ->withDepths(array($current->getDepth() + 1)) 94 + ->execute(); 95 + 96 + if (count($children) === 0) { 97 + $path = substr($current->getPath(), strlen($base_path) + 1); 98 + return array($path => $current->getLatestVersion()->getFilePHID()); 99 + } else { 100 + $mappings = array(); 101 + foreach ($children as $child) { 102 + $child_mappings = $this->getFragmentMappings($child, $base_path); 103 + foreach ($child_mappings as $key => $value) { 104 + $mappings[$key] = $value; 105 + } 106 + } 107 + return $mappings; 108 + } 109 + } 110 + 111 + }