Page MenuHomePhabricator

D12063.id29040.diff
No OneTemporary

D12063.id29040.diff

diff --git a/resources/sql/autopatches/20150312.filechunk.2.sql b/resources/sql/autopatches/20150312.filechunk.2.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20150312.filechunk.2.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {$NAMESPACE}_file.file
+ ADD isPartial BOOL NOT NULL DEFAULT 0;
diff --git a/resources/sql/autopatches/20150312.filechunk.3.sql b/resources/sql/autopatches/20150312.filechunk.3.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20150312.filechunk.3.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {$NAMESPACE}_file.file
+ ADD KEY `key_partial` (authorPHID, isPartial);
diff --git a/src/applications/files/conduit/FileAllocateConduitAPIMethod.php b/src/applications/files/conduit/FileAllocateConduitAPIMethod.php
--- a/src/applications/files/conduit/FileAllocateConduitAPIMethod.php
+++ b/src/applications/files/conduit/FileAllocateConduitAPIMethod.php
@@ -53,7 +53,7 @@
$hash,
$properties);
- if ($file && !$force_chunking) {
+ if ($file) {
return array(
'upload' => false,
'filePHID' => $file->getPHID(),
@@ -70,7 +70,7 @@
if ($file) {
return array(
- 'upload' => $file->isPartial(),
+ 'upload' => (bool)$file->getIsPartial(),
'filePHID' => $file->getPHID(),
);
}
@@ -103,7 +103,7 @@
// Otherwise, this is a large file and we need to perform a chunked
// upload.
- $chunk_properties = array();
+ $chunk_properties = $properties;
if ($hash) {
$chunk_properties += array(
diff --git a/src/applications/files/conduit/FileConduitAPIMethod.php b/src/applications/files/conduit/FileConduitAPIMethod.php
--- a/src/applications/files/conduit/FileConduitAPIMethod.php
+++ b/src/applications/files/conduit/FileConduitAPIMethod.php
@@ -89,6 +89,16 @@
return $data;
}
+ protected function loadAnyMissingChunk(
+ PhabricatorUser $viewer,
+ PhabricatorFile $file) {
+
+ return $this->newChunkQuery($viewer, $file)
+ ->withIsComplete(false)
+ ->setLimit(1)
+ ->execute();
+ }
+
private function newChunkQuery(
PhabricatorUser $viewer,
PhabricatorFile $file) {
@@ -106,4 +116,5 @@
->withChunkHandles(array($file->getStorageHandle()));
}
+
}
diff --git a/src/applications/files/conduit/FileUploadChunkConduitAPIMethod.php b/src/applications/files/conduit/FileUploadChunkConduitAPIMethod.php
--- a/src/applications/files/conduit/FileUploadChunkConduitAPIMethod.php
+++ b/src/applications/files/conduit/FileUploadChunkConduitAPIMethod.php
@@ -57,16 +57,19 @@
// NOTE: These files have a view policy which prevents normal access. They
// are only accessed through the storage engine.
- $file = PhabricatorFile::newFromFileData(
+ $chunk_data = PhabricatorFile::newFromFileData(
$data,
array(
'name' => $file->getMonogram().'.chunk-'.$chunk->getID(),
'viewPolicy' => PhabricatorPolicies::POLICY_NOONE,
));
- $chunk->setDataFilePHID($file->getPHID())->save();
+ $chunk->setDataFilePHID($chunk_data->getPHID())->save();
- // TODO: If all chunks are up, mark the file as complete.
+ $missing = $this->loadAnyMissingChunk($viewer, $file);
+ if (!$missing) {
+ $file->setIsPartial(0)->save();
+ }
return null;
}
diff --git a/src/applications/files/controller/PhabricatorFileDataController.php b/src/applications/files/controller/PhabricatorFileDataController.php
--- a/src/applications/files/controller/PhabricatorFileDataController.php
+++ b/src/applications/files/controller/PhabricatorFileDataController.php
@@ -5,6 +5,7 @@
private $phid;
private $key;
private $token;
+ private $file;
public function willProcessRequest(array $data) {
$this->phid = $data['phid'];
@@ -16,20 +17,9 @@
return false;
}
- protected function checkFileAndToken($file) {
- if (!$file) {
- return new Aphront404Response();
- }
-
- if (!$file->validateSecretKey($this->key)) {
- return new Aphront403Response();
- }
-
- return null;
- }
-
public function processRequest() {
$request = $this->getRequest();
+ $viewer = $this->getViewer();
$alt = PhabricatorEnv::getEnvConfig('security.alternate-file-domain');
$base_uri = PhabricatorEnv::getEnvConfig('phabricator.base-uri');
@@ -44,32 +34,22 @@
// Alternate files domain isn't configured or it's set
// to the same as the default domain
- // load the file with permissions checks;
- $file = id(new PhabricatorFileQuery())
- ->setViewer($request->getUser())
- ->withPHIDs(array($this->phid))
- ->executeOne();
-
- $error_response = $this->checkFileAndToken($file);
- if ($error_response) {
- return $error_response;
+ $response = $this->loadFile($viewer);
+ if ($response) {
+ return $response;
}
+ $file = $this->getFile();
// when the file is not CDNable, don't allow cache
$cache_response = $file->getCanCDN();
} else if ($req_domain != $alt_domain) {
// Alternate domain is configured but this request isn't using it
- // load the file with permissions checks;
- $file = id(new PhabricatorFileQuery())
- ->setViewer($request->getUser())
- ->withPHIDs(array($this->phid))
- ->executeOne();
-
- $error_response = $this->checkFileAndToken($file);
- if ($error_response) {
- return $error_response;
+ $response = $this->loadFile($viewer);
+ if ($response) {
+ return $response;
}
+ $file = $this->getFile();
// if the user can see the file, generate a token;
// redirect to the alt domain with the token;
@@ -82,18 +62,15 @@
->setURI($token_uri);
} else {
- // We are using the alternate domain
-
- // load the file, bypassing permission checks;
- $file = id(new PhabricatorFileQuery())
- ->setViewer(PhabricatorUser::getOmnipotentUser())
- ->withPHIDs(array($this->phid))
- ->executeOne();
+ // We are using the alternate domain. We don't have authentication
+ // on this domain, so we bypass policy checks when loading the file.
- $error_response = $this->checkFileAndToken($file);
- if ($error_response) {
- return $error_response;
+ $bypass_policies = PhabricatorUser::getOmnipotentUser();
+ $response = $this->loadFile($bypass_policies);
+ if ($response) {
+ return $response;
}
+ $file = $this->getFile();
$acquire_token_uri = id(new PhutilURI($file->getViewURI()))
->setDomain($main_domain);
@@ -205,4 +182,44 @@
return $uri;
}
+ private function loadFile(PhabricatorUser $viewer) {
+ $file = id(new PhabricatorFileQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($this->phid))
+ ->executeOne();
+
+ if (!$file) {
+ return new Aphront404Response();
+ }
+
+ if (!$file->validateSecretKey($this->key)) {
+ return new Aphront403Response();
+ }
+
+ if ($file->getIsPartial()) {
+ // We may be on the CDN domain, so we need to use a fully-qualified URI
+ // here to make sure we end up back on the main domain.
+ $info_uri = PhabricatorEnv::getURI($file->getInfoURI());
+
+ return $this->newDialog()
+ ->setTitle(pht('Partial Upload'))
+ ->appendParagraph(
+ pht(
+ 'This file has only been partially uploaded. It must be '.
+ 'uploaded completely before you can download it.'))
+ ->addCancelButton($info_uri);
+ }
+
+ $this->file = $file;
+
+ return null;
+ }
+
+ private function getFile() {
+ if (!$this->file) {
+ throw new Exception(pht('Call loadFile() before getFile()!'));
+ }
+ return $this->file;
+ }
+
}
diff --git a/src/applications/files/controller/PhabricatorFileInfoController.php b/src/applications/files/controller/PhabricatorFileInfoController.php
--- a/src/applications/files/controller/PhabricatorFileInfoController.php
+++ b/src/applications/files/controller/PhabricatorFileInfoController.php
@@ -52,11 +52,21 @@
$ttl = $file->getTTL();
if ($ttl !== null) {
$ttl_tag = id(new PHUITagView())
- ->setType(PHUITagView::TYPE_OBJECT)
+ ->setType(PHUITagView::TYPE_STATE)
+ ->setBackgroundColor(PHUITagView::COLOR_YELLOW)
->setName(pht('Temporary'));
$header->addTag($ttl_tag);
}
+ $partial = $file->getIsPartial();
+ if ($partial) {
+ $partial_tag = id(new PHUITagView())
+ ->setType(PHUITagView::TYPE_STATE)
+ ->setBackgroundColor(PHUITagView::COLOR_ORANGE)
+ ->setName(pht('Partial Upload'));
+ $header->addTag($partial_tag);
+ }
+
$actions = $this->buildActionView($file);
$timeline = $this->buildTransactionView($file);
$crumbs = $this->buildApplicationCrumbs();
@@ -126,21 +136,27 @@
->setObjectURI($this->getRequest()->getRequestURI())
->setObject($file);
+ $can_download = !$file->getIsPartial();
+
if ($file->isViewableInBrowser()) {
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('View File'))
->setIcon('fa-file-o')
- ->setHref($file->getViewURI()));
+ ->setHref($file->getViewURI())
+ ->setDisabled(!$can_download)
+ ->setWorkflow(!$can_download));
} else {
$view->addAction(
id(new PhabricatorActionView())
->setUser($viewer)
- ->setRenderAsForm(true)
- ->setDownload(true)
+ ->setRenderAsForm($can_download)
+ ->setDownload($can_download)
->setName(pht('Download File'))
->setIcon('fa-download')
- ->setHref($file->getViewURI()));
+ ->setHref($file->getViewURI())
+ ->setDisabled(!$can_download)
+ ->setWorkflow(!$can_download));
}
$view->addAction(
diff --git a/src/applications/files/query/PhabricatorFileChunkQuery.php b/src/applications/files/query/PhabricatorFileChunkQuery.php
--- a/src/applications/files/query/PhabricatorFileChunkQuery.php
+++ b/src/applications/files/query/PhabricatorFileChunkQuery.php
@@ -6,6 +6,7 @@
private $chunkHandles;
private $rangeStart;
private $rangeEnd;
+ private $isComplete;
private $needDataFiles;
public function withChunkHandles(array $handles) {
@@ -19,6 +20,11 @@
return $this;
}
+ public function withIsComplete($complete) {
+ $this->isComplete = $complete;
+ return $this;
+ }
+
public function needDataFiles($need) {
$this->needDataFiles = $need;
return $this;
@@ -104,6 +110,18 @@
$this->rangeEnd);
}
+ if ($this->isComplete !== null) {
+ if ($this->isComplete) {
+ $where[] = qsprintf(
+ $conn_r,
+ 'dataFilePHID IS NOT NULL');
+ } else {
+ $where[] = qsprintf(
+ $conn_r,
+ 'dataFilePHID IS NULL');
+ }
+ }
+
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
diff --git a/src/applications/files/storage/PhabricatorFile.php b/src/applications/files/storage/PhabricatorFile.php
--- a/src/applications/files/storage/PhabricatorFile.php
+++ b/src/applications/files/storage/PhabricatorFile.php
@@ -51,6 +51,7 @@
protected $ttl;
protected $isExplicitUpload = 1;
protected $viewPolicy = PhabricatorPolicies::POLICY_USER;
+ protected $isPartial = 0;
private $objects = self::ATTACHABLE;
private $objectPHIDs = self::ATTACHABLE;
@@ -67,6 +68,7 @@
return id(new PhabricatorFile())
->setViewPolicy($view_policy)
+ ->setIsPartial(0)
->attachOriginalFile(null)
->attachObjects(array())
->attachObjectPHIDs(array());
@@ -91,6 +93,7 @@
'ttl' => 'epoch?',
'isExplicitUpload' => 'bool?',
'mailKey' => 'bytes20',
+ 'isPartial' => 'bool',
),
self::CONFIG_KEY_SCHEMA => array(
'key_phid' => null,
@@ -110,6 +113,9 @@
'key_dateCreated' => array(
'columns' => array('dateCreated'),
),
+ 'key_partial' => array(
+ 'columns' => array('authorPHID', 'isPartial'),
+ ),
),
) + parent::getConfiguration();
}
@@ -294,6 +300,7 @@
$file->setStorageEngine($engine->getEngineIdentifier());
$file->setStorageHandle(PhabricatorFileChunk::newChunkHandle());
$file->setStorageFormat(self::STORAGE_FORMAT_RAW);
+ $file->setIsPartial(1);
$file->readPropertiesFromParameters($params);
@@ -628,9 +635,16 @@
}
$parts[] = $name;
- $path = implode('/', $parts);
+ $path = '/'.implode('/', $parts);
- return PhabricatorEnv::getCDNURI($path);
+ // If this file is only partially uploaded, we're just going to return a
+ // local URI to make sure that Ajax works, since the page is inevitably
+ // going to give us an error back.
+ if ($this->getIsPartial()) {
+ return PhabricatorEnv::getURI($path);
+ } else {
+ return PhabricatorEnv::getCDNURI($path);
+ }
}
/**
@@ -1170,11 +1184,6 @@
->setURI($uri);
}
- public function isPartial() {
- // TODO: Placeholder for resumable uploads.
- return false;
- }
-
/* -( PhabricatorApplicationTransactionInterface )------------------------- */

File Metadata

Mime Type
text/plain
Expires
Mon, Oct 21, 9:33 AM (4 w, 7 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6716298
Default Alt Text
D12063.id29040.diff (13 KB)

Event Timeline