Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F88443
D7729.id17463.diff
All Users
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
14 KB
Referenced Files
None
Subscribers
None
D7729.id17463.diff
View Options
diff --git a/resources/sql/patches/20131206.phragmentnull.sql b/resources/sql/patches/20131206.phragmentnull.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/patches/20131206.phragmentnull.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {$NAMESPACE}_phragment.phragment_fragment
+MODIFY latestVersionPHID VARCHAR(64) NULL;
diff --git a/src/applications/phragment/controller/PhragmentBrowseController.php b/src/applications/phragment/controller/PhragmentBrowseController.php
--- a/src/applications/phragment/controller/PhragmentBrowseController.php
+++ b/src/applications/phragment/controller/PhragmentBrowseController.php
@@ -57,11 +57,15 @@
$item = id(new PHUIObjectItemView());
$item->setHeader($fragment->getName());
$item->setHref($this->getApplicationURI('/browse/'.$fragment->getPath()));
- $item->addAttribute('Last Updated '.phabricator_datetime(
- $fragment->getLatestVersion()->getDateCreated(),
- $viewer));
- $item->addAttribute(
- 'Latest Version '.$fragment->getLatestVersion()->getSequence());
+ if (!$fragment->isDirectory()) {
+ $item->addAttribute('Last Updated '.phabricator_datetime(
+ $fragment->getLatestVersion()->getDateCreated(),
+ $viewer));
+ $item->addAttribute(
+ 'Latest Version '.$fragment->getLatestVersion()->getSequence());
+ } else {
+ $item->addAttribute('Directory');
+ }
$list->addItem($item);
}
diff --git a/src/applications/phragment/controller/PhragmentController.php b/src/applications/phragment/controller/PhragmentController.php
--- a/src/applications/phragment/controller/PhragmentController.php
+++ b/src/applications/phragment/controller/PhragmentController.php
@@ -66,13 +66,16 @@
$this->loadHandles($phids);
- $file = id(new PhabricatorFileQuery())
- ->setViewer($viewer)
- ->withPHIDs(array($fragment->getLatestVersion()->getPHID()))
- ->executeOne();
+ $file = null;
$file_uri = null;
- if ($file !== null) {
- $file_uri = $file->getBestURI();
+ if (!$fragment->isDirectory()) {
+ $file = id(new PhabricatorFileQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($fragment->getLatestVersion()->getFilePHID()))
+ ->executeOne();
+ if ($file !== null) {
+ $file_uri = $file->getBestURI();
+ }
}
$header = id(new PHUIHeaderView())
@@ -96,12 +99,21 @@
->setHref($this->getApplicationURI("zip/".$fragment->getPath()))
->setDisabled(false) // TODO: Policy
->setIcon('zip'));
- $actions->addAction(
- id(new PhabricatorActionView())
- ->setName(pht('Update Fragment'))
- ->setHref($this->getApplicationURI("update/".$fragment->getPath()))
- ->setDisabled(false) // TODO: Policy
- ->setIcon('edit'));
+ if (!$fragment->isDirectory()) {
+ $actions->addAction(
+ id(new PhabricatorActionView())
+ ->setName(pht('Update Fragment'))
+ ->setHref($this->getApplicationURI("update/".$fragment->getPath()))
+ ->setDisabled(false) // TODO: Policy
+ ->setIcon('edit'));
+ } else {
+ $actions->addAction(
+ id(new PhabricatorActionView())
+ ->setName(pht('Convert to File'))
+ ->setHref($this->getApplicationURI("update/".$fragment->getPath()))
+ ->setDisabled(false) // TODO: Policy
+ ->setIcon('edit'));
+ }
if ($is_history_view) {
$actions->addAction(
id(new PhabricatorActionView())
@@ -121,9 +133,18 @@
->setObject($fragment)
->setActionList($actions);
- $properties->addProperty(
- pht('Latest Version'),
- $this->renderHandlesForPHIDs(array($fragment->getLatestVersionPHID())));
+ if (!$fragment->isDirectory()) {
+ $properties->addProperty(
+ pht('Type'),
+ pht('File'));
+ $properties->addProperty(
+ pht('Latest Version'),
+ $this->renderHandlesForPHIDs(array($fragment->getLatestVersionPHID())));
+ } else {
+ $properties->addProperty(
+ pht('Type'),
+ pht('Directory'));
+ }
return id(new PHUIObjectBoxView())
->setHeader($header)
diff --git a/src/applications/phragment/controller/PhragmentCreateController.php b/src/applications/phragment/controller/PhragmentCreateController.php
--- a/src/applications/phragment/controller/PhragmentCreateController.php
+++ b/src/applications/phragment/controller/PhragmentCreateController.php
@@ -54,22 +54,12 @@
$depth = $parent->getDepth() + 1;
}
- $version = id(new PhragmentFragmentVersion());
- $version->setSequence(0);
- $version->setFragmentPHID(''); // Can't set this yet...
- $version->setFilePHID($file->getPHID());
- $version->save();
-
- $fragment->setPath(trim($parent_path.'/'.$v_name, '/'));
- $fragment->setDepth($depth);
- $fragment->setOwnerPHID($viewer->getPHID());
- $fragment->setLatestVersionPHID($version->getPHID());
- $fragment->setViewPolicy($v_viewpolicy);
- $fragment->setEditPolicy($v_editpolicy);
- $fragment->save();
-
- $version->setFragmentPHID($fragment->getPHID());
- $version->save();
+ PhragmentFragment::createFromFile(
+ $viewer,
+ $file,
+ trim($parent_path.'/'.$v_name, '/'),
+ $v_viewpolicy,
+ $v_editpolicy);
return id(new AphrontRedirectResponse())
->setURI('/phragment/browse/'.trim($parent_path.'/'.$v_name, '/'));
diff --git a/src/applications/phragment/controller/PhragmentUpdateController.php b/src/applications/phragment/controller/PhragmentUpdateController.php
--- a/src/applications/phragment/controller/PhragmentUpdateController.php
+++ b/src/applications/phragment/controller/PhragmentUpdateController.php
@@ -31,20 +31,14 @@
}
if (!count($errors)) {
- $existing = id(new PhragmentFragmentVersionQuery())
- ->setViewer($viewer)
- ->withFragmentPHIDs(array($fragment->getPHID()))
- ->execute();
- $sequence = count($existing);
-
- $version = id(new PhragmentFragmentVersion());
- $version->setSequence($sequence);
- $version->setFragmentPHID($fragment->getPHID()); // Can't set this yet
- $version->setFilePHID($file->getPHID());
- $version->save();
-
- $fragment->setLatestVersionPHID($version->getPHID());
- $fragment->save();
+ // If the file is a ZIP archive (has application/zip mimetype)
+ // then we extract the zip and apply versions for each of the
+ // individual fragments, creating and deleting files as needed.
+ if ($file->getMimeType() === "application/zip") {
+ $fragment->updateFromZIP($viewer, $file);
+ } else {
+ $fragment->updateFromFile($viewer, $file);
+ }
return id(new AphrontRedirectResponse())
->setURI('/phragment/browse/'.$fragment->getPath());
diff --git a/src/applications/phragment/query/PhragmentFragmentQuery.php b/src/applications/phragment/query/PhragmentFragmentQuery.php
--- a/src/applications/phragment/query/PhragmentFragmentQuery.php
+++ b/src/applications/phragment/query/PhragmentFragmentQuery.php
@@ -115,7 +115,6 @@
foreach ($page as $key => $fragment) {
$version_phid = $fragment->getLatestVersionPHID();
if (empty($versions[$version_phid])) {
- unset($page[$key]);
continue;
}
$fragment->attachLatestVersion($versions[$version_phid]);
diff --git a/src/applications/phragment/storage/PhragmentFragment.php b/src/applications/phragment/storage/PhragmentFragment.php
--- a/src/applications/phragment/storage/PhragmentFragment.php
+++ b/src/applications/phragment/storage/PhragmentFragment.php
@@ -40,14 +40,212 @@
return $this->file = $file;
}
+ public function isDirectory() {
+ return $this->latestVersionPHID === null;
+ }
+
public function getLatestVersion() {
+ if ($this->latestVersionPHID === null) {
+ return null;
+ }
return $this->assertAttached($this->latestVersion);
}
public function attachLatestVersion(PhragmentFragmentVersion $version) {
return $this->latestVersion = $version;
}
+
+/* -( Updating ) --------------------------------------------------------- */
+
+
+ /**
+ * Create a new fragment from a file.
+ */
+ public static function createFromFile(
+ PhabricatorUser $viewer,
+ PhabricatorFile $file = null,
+ $path,
+ $view_policy,
+ $edit_policy) {
+
+ $fragment = id(new PhragmentFragment());
+ $fragment->setPath($path);
+ $fragment->setDepth(count(explode('/', $path)));
+ $fragment->setOwnerPHID($viewer->getPHID());
+ $fragment->setLatestVersionPHID(null);
+ $fragment->setViewPolicy($view_policy);
+ $fragment->setEditPolicy($edit_policy);
+ $fragment->save();
+
+ // Directory fragments have no versions associated with them, so we
+ // just return the fragment at this point.
+ if ($file === null) {
+ return $fragment;
+ }
+
+ if ($file->getMimeType() === "application/zip") {
+ $fragment->updateFromZIP($viewer, $file);
+ } else {
+ $fragment->updateFromFile($viewer, $file);
+ }
+
+ return $fragment;
+ }
+
+
+ /**
+ * Set the specified file as the next version for the fragment.
+ */
+ public function updateFromFile(
+ PhabricatorUser $viewer,
+ PhabricatorFile $file) {
+
+ $existing = id(new PhragmentFragmentVersionQuery())
+ ->setViewer($viewer)
+ ->withFragmentPHIDs(array($this->getPHID()))
+ ->execute();
+ $sequence = count($existing);
+
+ $version = id(new PhragmentFragmentVersion());
+ $version->setSequence($sequence);
+ $version->setFragmentPHID($this->getPHID());
+ $version->setFilePHID($file->getPHID());
+ $version->save();
+
+ $this->setLatestVersionPHID($version->getPHID());
+ $this->save();
+ }
+
+ /**
+ * Apply the specified ZIP archive onto the fragment, removing
+ * and creating fragments as needed.
+ */
+ public function updateFromZIP(
+ PhabricatorUser $viewer,
+ PhabricatorFile $file) {
+
+ if ($file->getMimeType() !== "application/zip") {
+ throw new Exception("File must have mimetype 'application/zip'");
+ }
+
+ // First apply the ZIP as normal.
+ $this->updateFromFile($viewer, $file);
+
+ // Ensure we have ZIP support.
+ $zip = null;
+ try {
+ $zip = new ZipArchive();
+ } catch (Exception $e) {
+ // The server doesn't have php5-zip, so we can't do recursive updates.
+ return;
+ }
+
+ $temp = new TempFile();
+ Filesystem::writeFile($temp, $file->loadFileData());
+ if (!$zip->open($temp)) {
+ throw new Exception("Unable to open ZIP");
+ }
+
+ // Get all of the paths and their data from the ZIP.
+ $mappings = array();
+ for ($i = 0; $i < $zip->numFiles; $i++) {
+ $path = trim($zip->getNameIndex($i), '/');
+ $stream = $zip->getStream($path);
+ $data = null;
+ if ($stream !== false) {
+ $data = stream_get_contents($stream);
+ fclose($stream);
+ }
+ $mappings[$path] = $data;
+ }
+
+ // Adjust the paths relative to this fragment so we can look existing
+ // fragments up in the DB.
+ $base_path = $this->getPath();
+ $paths = array();
+ foreach ($mappings as $p => $data) {
+ $paths[] = $base_path.'/'.$p;
+ }
+
+ // FIXME: What happens when a child exists, but the current user
+ // can't see it. We're going to create a new child with the exact
+ // same path and then bad things will happen.
+ $children = id(new PhragmentFragmentQuery())
+ ->setViewer($viewer)
+ ->needLatestVersion(true)
+ ->withPaths($paths)
+ ->execute();
+ $children = mpull($children, null, 'getPath');
+
+ // Iterate over the existing fragments.
+ foreach ($children as $full_path => $child) {
+ $path = substr($full_path, strlen($base_path) + 1);
+ if (array_key_exists($path, $mappings)) {
+ if ($child->isDirectory()) {
+ // We don't convert directories to files when
+ // importing via ZIP. Users must explicitly
+ // convert a fragment to have a file associated
+ // with it.
+ continue;
+ }
+
+ // The file is being updated.
+ $file = PhabricatorFile::newFromFileData(
+ $mappings[$path],
+ array('name' => basename($path)));
+ $child->updateFromFile($viewer, $file);
+ } else {
+ // The file is being deleted.
+ $child->deleteFile($viewer);
+ }
+ }
+
+ // Iterate over the mappings to find new files.
+ foreach ($mappings as $path => $data) {
+ if (!array_key_exists($base_path.'/'.$path, $children)) {
+ // The file is being created. If the data is null,
+ // then this is explicitly a directory being created.
+ $file = null;
+ if ($mappings[$path] !== null) {
+ $file = PhabricatorFile::newFromFileData(
+ $mappings[$path],
+ array('name' => basename($path)));
+ }
+ PhragmentFragment::createFromFile(
+ $viewer,
+ $file,
+ $base_path.'/'.$path,
+ $this->getViewPolicy(),
+ $this->getEditPolicy());
+ }
+ }
+ }
+
+ /**
+ * Delete the contents of the specified fragment.
+ */
+ public function deleteFile(PhabricatorUser $viewer) {
+ $existing = id(new PhragmentFragmentVersionQuery())
+ ->setViewer($viewer)
+ ->withFragmentPHIDs(array($this->getPHID()))
+ ->execute();
+ $sequence = count($existing);
+
+ $version = id(new PhragmentFragmentVersion());
+ $version->setSequence($sequence);
+ $version->setFragmentPHID($this->getPHID());
+ $version->setFilePHID(null);
+ $version->save();
+
+ $this->setLatestVersionPHID($version->getPHID());
+ $this->save();
+ }
+
+
+/* -( Policy Interface )--------------------------------------------------- */
+
+
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
diff --git a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
--- a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
+++ b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
@@ -1820,6 +1820,10 @@
'type' => 'sql',
'name' => $this->getPatchPath('20131206.phragment.sql'),
),
+ '20131206.phragmentnull.sql' => array(
+ 'type' => 'sql',
+ 'name' => $this->getPatchPath('20131206.phragmentnull.sql'),
+ ),
);
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Storage Engine
amazon-s3
Storage Format
Raw Data
Storage Handle
phabricator/pt/oy/cy6eq2jxfoypux44
Default Alt Text
D7729.id17463.diff (14 KB)
Attached To
Mode
D7729: Implement support for creating and updating fragments from ZIPs
Attached
Detach File
Event Timeline
Log In to Comment