Phabricator file upload application.

This commit is contained in:
epriestley
2011-01-22 18:33:00 -08:00
parent 2c7e71cac5
commit 0eac30d80d
25 changed files with 848 additions and 16 deletions

View File

@@ -0,0 +1,34 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
abstract class PhabricatorFileController extends PhabricatorController {
public function buildStandardPageResponse($view, array $data) {
$page = new PhabricatorStandardPageView();
$page->setApplicationName('Files');
$page->setBaseURI('/file/');
$page->setTitle(idx($data, 'title'));
$page->setGlyph("\xE2\x87\xAA");
$page->appendChild($view);
$response = new AphrontWebpageResponse();
return $response->setContent($page->render());
}
}

View File

@@ -0,0 +1,16 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'aphront/response/webpage');
phutil_require_module('phabricator', 'applications/base/controller/base');
phutil_require_module('phabricator', 'view/page/standard');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorFileController.php');

View File

@@ -0,0 +1,85 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class PhabricatorFileListController extends PhabricatorFileController {
public function processRequest() {
$files = id(new PhabricatorFile())->loadAllWhere(
'1 = 1 ORDER BY id DESC LIMIT 100');
$rows = array();
foreach ($files as $file) {
$rows[] = array(
phutil_escape_html($file->getPHID()),
phutil_escape_html($file->getName()),
phutil_escape_html($file->getByteSize()),
phutil_render_tag(
'a',
array(
'class' => 'small button grey',
'href' => '/file/info/'.$file->getPHID().'/',
),
'Info'),
phutil_render_tag(
'a',
array(
'class' => 'small button grey',
'href' => '/file/view/'.$file->getPHID().'/',
),
'View'),
phutil_render_tag(
'a',
array(
'class' => 'small button grey',
'href' => '/file/download/'.$file->getPHID().'/',
),
'Download'),
);
}
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
'PHID',
'Name',
'Size',
'',
'',
'',
));
$table->setColumnClasses(
array(
null,
'wide',
null,
'action',
'action',
'action',
));
$panel = new AphrontPanelView();
$panel->appendChild($table);
$panel->setHeader('Files');
$panel->setCreateButton('Upload File', '/file/upload/');
return $this->buildStandardPageResponse($panel, array(
'title' => 'Files',
'tab' => 'files',
));
}
}

View File

@@ -0,0 +1,18 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/files/controller/base');
phutil_require_module('phabricator', 'applications/files/storage/file');
phutil_require_module('phabricator', 'view/control/table');
phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorFileListController.php');

View File

@@ -0,0 +1,68 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class PhabricatorFileUploadController extends PhabricatorFileController {
public function processRequest() {
$request = $this->getRequest();
if ($request->isFormPost()) {
$file = PhabricatorFile::newFromPHPUpload(
idx($_FILES, 'file'),
array(
'name' => $request->getStr('name'),
));
return id(new AphrontRedirectResponse())
->setURI('/file/info/'.phutil_escape_uri($file->getPHID()).'/');
}
$form = new AphrontFormView();
$form->setAction('/file/upload/');
$form
->setEncType('multipart/form-data')
->appendChild(
id(new AphrontFormFileControl())
->setLabel('File')
->setName('file')
->setError(true))
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Name')
->setName('name')
->setCaption('Optional file display name.'))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Upload')
->addCancelButton('/file/'));
$panel = new AphrontPanelView();
$panel->setHeader('Upload File');
$panel->appendChild($form);
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
return $this->buildStandardPageResponse(
array($panel),
array(
'title' => 'Upload File',
));
}
}

View File

@@ -0,0 +1,20 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'aphront/response/redirect');
phutil_require_module('phabricator', 'applications/files/controller/base');
phutil_require_module('phabricator', 'applications/files/storage/file');
phutil_require_module('phabricator', 'view/form/base');
phutil_require_module('phabricator', 'view/form/control/submit');
phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorFileUploadController.php');

View File

@@ -0,0 +1,112 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class PhabricatorFileViewController extends PhabricatorFileController {
private $phid;
private $view;
public function willProcessRequest(array $data) {
$this->phid = $data['phid'];
$this->view = $data['view'];
}
public function processRequest() {
$file = id(new PhabricatorFile())->loadOneWhere(
'phid = %s',
$this->phid);
if (!$file) {
return new Aphront404Response();
}
switch ($this->view) {
case 'download':
case 'view':
$data = $file->loadFileData();
$response = new AphrontFileResponse();
$response->setContent($data);
$response->setMimeType($file->getMimeType());
if ($this->view == 'download') {
$response->setDownload($file->getName());
}
return $response;
default:
break;
}
$form = new AphrontFormView();
$form->setAction('/file/view/'.$file->getPHID().'/');
$form
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('Name')
->setName('name')
->setValue($file->getName()))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('PHID')
->setName('phid')
->setValue($file->getPHID()))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('Created')
->setName('created')
->setValue(date('Y-m-d g:i:s A', $file->getDateCreated())))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('Mime Type')
->setName('mime')
->setValue($file->getMimeType()))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('Size')
->setName('size')
->setValue($file->getByteSize().' bytes'))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('Engine')
->setName('storageEngine')
->setValue($file->getStorageEngine()))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('Format')
->setName('storageFormat')
->setValue($file->getStorageFormat()))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('Handle')
->setName('storageHandle')
->setValue($file->getStorageHandle()))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('View File'));
$panel = new AphrontPanelView();
$panel->setHeader('File Info - '.$file->getName());
$panel->appendChild($form);
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
return $this->buildStandardPageResponse(
array($panel),
array(
'title' => 'File Info - '.$file->getName(),
));
}
}

View File

@@ -0,0 +1,20 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'aphront/response/404');
phutil_require_module('phabricator', 'aphront/response/file');
phutil_require_module('phabricator', 'applications/files/controller/base');
phutil_require_module('phabricator', 'applications/files/storage/file');
phutil_require_module('phabricator', 'view/form/base');
phutil_require_module('phabricator', 'view/form/control/submit');
phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorFileViewController.php');

View File

@@ -0,0 +1,25 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class PhabricatorFileDAO extends PhabricatorLiskDAO {
public function getApplicationName() {
return 'file';
}
}

View File

@@ -0,0 +1,12 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/base/storage/lisk');
phutil_require_source('PhabricatorFileDAO.php');

View File

@@ -0,0 +1,170 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class PhabricatorFile extends PhabricatorFileDAO {
const STORAGE_ENGINE_BLOB = 'blob';
const STORAGE_FORMAT_RAW = 'raw';
const PHID_TYPE = 'FILE';
// TODO: We need to reconcile this with MySQL packet size.
const FILE_SIZE_BYTE_LIMIT = 12582912;
protected $phid;
protected $name;
protected $mimeType;
protected $byteSize;
protected $storageEngine;
protected $storageFormat;
protected $storageHandle;
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(self::PHID_TYPE);
}
public static function newFromPHPUpload($spec, array $params = array()) {
if (!$spec) {
throw new Exception("No file was uploaded!");
}
$err = idx($spec, 'error');
if ($err) {
throw new Exception("File upload failed with error '{$err}'.");
}
$tmp_name = idx($spec, 'tmp_name');
$is_valid = @is_uploaded_file($tmp_name);
if (!$is_valid) {
throw new Exception("File is not an uploaded file.");
}
$file_data = Filesystem::readFile($tmp_name);
$file_size = idx($spec, 'size');
if (strlen($file_data) != $file_size) {
throw new Exception("File size disagrees with uploaded size.");
}
$file_name = nonempty(
idx($params, 'name'),
idx($spec, 'name'));
$params = array(
'name' => $file_name,
) + $params;
return self::newFromFileData($file_data, $params);
}
public static function newFromFileData($data, array $params = array()) {
$file_size = strlen($data);
if ($file_size > self::FILE_SIZE_BYTE_LIMIT) {
throw new Exception("File is too large to store.");
}
$file_name = idx($params, 'name');
$file_name = self::normalizeFileName($file_name);
$file = new PhabricatorFile();
$file->setName($file_name);
$file->setByteSize(strlen($data));
$blob = new PhabricatorFileStorageBlob();
$blob->setData($data);
$blob->save();
// TODO: This stuff is almost certainly YAGNI, but we could imagine having
// an alternate disk store and gzipping or encrypting things or something
// crazy like that and this isn't toooo much extra code.
$file->setStorageEngine(self::STORAGE_ENGINE_BLOB);
$file->setStorageFormat(self::STORAGE_FORMAT_RAW);
$file->setStorageHandle($blob->getID());
try {
$tmp = new TempFile();
Filesystem::writeFile($tmp, $data);
list($stdout) = execx('file -b --mime %s', $tmp);
$file->setMimeType($stdout);
} catch (Exception $ex) {
// Be robust here since we don't really care that much about mime types.
}
$file->save();
return $file;
}
public static function normalizeFileName($file_name) {
return preg_replace('/[^a-zA-Z0-9.~_-]/', '_', $file_name);
}
public function delete() {
$this->openTransaction();
switch ($this->getStorageEngine()) {
case self::STORAGE_ENGINE_BLOB:
$handle = $this->getStorageHandle();
$blob = id(new PhabricatorFileStorageBlob())->load($handle);
$blob->delete();
break;
default:
throw new Exception("Unknown storage engine!");
}
$ret = parent::delete();
$this->saveTransaction();
return $ret;
}
public function loadFileData() {
$handle = $this->getStorageHandle();
$data = null;
switch ($this->getStorageEngine()) {
case self::STORAGE_ENGINE_BLOB:
$blob = id(new PhabricatorFileStorageBlob())->load($handle);
if (!$blob) {
throw new Exception("Failed to load file blob data.");
}
$data = $blob->getData();
break;
default:
throw new Exception("Unknown storage engine.");
}
switch ($this->getStorageFormat()) {
case self::STORAGE_FORMAT_RAW:
$data = $data;
break;
default:
throw new Exception("Unknown storage format.");
}
return $data;
}
}

View File

@@ -0,0 +1,19 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/files/storage/base');
phutil_require_module('phabricator', 'applications/files/storage/storageblob');
phutil_require_module('phabricator', 'applications/phid/storage/phid');
phutil_require_module('phutil', 'filesystem');
phutil_require_module('phutil', 'filesystem/tempfile');
phutil_require_module('phutil', 'future/exec');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorFile.php');

View File

@@ -0,0 +1,23 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class PhabricatorFileStorageBlob extends PhabricatorFileDAO {
protected $data;
}

View File

@@ -0,0 +1,12 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/files/storage/base');
phutil_require_source('PhabricatorFileStorageBlob.php');