Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/conpherence/controller/ConpherenceUpdateController.php b/src/applications/conpherence/controller/ConpherenceUpdateController.php
index 0f4b6927b9..f57e5bdb32 100644
--- a/src/applications/conpherence/controller/ConpherenceUpdateController.php
+++ b/src/applications/conpherence/controller/ConpherenceUpdateController.php
@@ -1,280 +1,287 @@
<?php
/**
* @group conpherence
*/
final class ConpherenceUpdateController extends
ConpherenceController {
private $conpherenceID;
public function setConpherenceID($conpherence_id) {
$this->conpherenceID = $conpherence_id;
return $this;
}
public function getConpherenceID() {
return $this->conpherenceID;
}
public function willProcessRequest(array $data) {
$this->setConpherenceID(idx($data, 'id'));
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$conpherence_id = $this->getConpherenceID();
if (!$conpherence_id) {
return new Aphront404Response();
}
$conpherence = id(new ConpherenceThreadQuery())
->setViewer($user)
->withIDs(array($conpherence_id))
->needOrigPics(true)
->needHeaderPics(true)
->executeOne();
$supported_formats = PhabricatorFile::getTransformableImageFormats();
$action = $request->getStr('action', 'metadata');
$latest_transaction_id = null;
$fancy_ajax_style = true;
$error_view = null;
$e_file = array();
$errors = array();
if ($request->isFormPost()) {
$content_source = PhabricatorContentSource::newForSource(
PhabricatorContentSource::SOURCE_WEB,
array(
'ip' => $request->getRemoteAddr()
));
$editor = id(new ConpherenceEditor())
->setContinueOnNoEffect($request->isContinueRequest())
->setContentSource($content_source)
->setActor($user);
switch ($action) {
case 'message':
$message = $request->getStr('text');
$latest_transaction_id = $request->getInt('latest_transaction_id');
$xactions = $editor->generateTransactionsFromText(
$conpherence,
$message);
break;
case 'metadata':
$xactions = array();
$top = $request->getInt('image_y');
$left = $request->getInt('image_x');
$file_id = $request->getInt('file_id');
$title = $request->getStr('title');
$updated = false;
if ($file_id) {
$orig_file = id(new PhabricatorFileQuery())
->setViewer($user)
->withIDs(array($file_id))
->executeOne();
$okay = $orig_file->isTransformableImage();
if ($okay) {
$xactions[] = id(new ConpherenceTransaction())
->setTransactionType(ConpherenceTransactionType::TYPE_PICTURE)
->setNewValue($orig_file->getPHID());
- // do 2 transformations "crudely"
+ // do a transformation "crudely"
$xformer = new PhabricatorImageTransformer();
$header_file = $xformer->executeConpherenceTransform(
$orig_file,
0,
0,
ConpherenceImageData::HEAD_WIDTH,
ConpherenceImageData::HEAD_HEIGHT);
// this is handled outside the editor for now. no particularly
// good reason to move it inside
$conpherence->setImagePHIDs(
array(
ConpherenceImageData::SIZE_HEAD => $header_file->getPHID(),
));
$conpherence->setImages(
array(
ConpherenceImageData::SIZE_HEAD => $header_file,
));
} else {
$e_file[] = $orig_file;
$errors[] =
pht('This server only supports these image formats: %s.',
implode(', ', $supported_formats));
}
// use the existing title in this image upload case
$title = $conpherence->getTitle();
$updated = true;
$fancy_ajax_style = false;
- } else if ($top !== null || $left !== null) {
+ }
+
+ // all other metadata updates are continue requests
+ if (!$request->isContinueRequest()) {
+ break;
+ }
+
+ if ($top !== null || $left !== null) {
$file = $conpherence->getImage(ConpherenceImageData::SIZE_ORIG);
$xformer = new PhabricatorImageTransformer();
$xformed = $xformer->executeConpherenceTransform(
$file,
$top,
$left,
ConpherenceImageData::HEAD_WIDTH,
ConpherenceImageData::HEAD_HEIGHT);
$image_phid = $xformed->getPHID();
$xactions[] = id(new ConpherenceTransaction())
->setTransactionType(
ConpherenceTransactionType::TYPE_PICTURE_CROP)
->setNewValue($image_phid);
$updated = true;
}
if ($title != $conpherence->getTitle()) {
$xactions[] = id(new ConpherenceTransaction())
->setTransactionType(ConpherenceTransactionType::TYPE_TITLE)
->setNewValue($title);
$updated = true;
}
- if (!$updated && $request->isContinueRequest()) {
+ if (!$updated) {
$errors[] = pht(
'That was a non-update. Try cancel.');
}
break;
default:
throw new Exception('Unknown action: '.$action);
break;
}
if ($xactions) {
try {
$xactions = $editor->applyTransactions($conpherence, $xactions);
if ($fancy_ajax_style) {
$content = $this->loadAndRenderUpdates(
$conpherence_id,
$latest_transaction_id);
return id(new AphrontAjaxResponse())
->setContent($content);
} else {
return id(new AphrontRedirectResponse())
->setURI($this->getApplicationURI($conpherence->getID().'/'));
}
} catch (PhabricatorApplicationTransactionNoEffectException $ex) {
return id(new PhabricatorApplicationTransactionNoEffectResponse())
->setCancelURI($this->getApplicationURI($conpherence_id.'/'))
->setException($ex);
}
}
}
if ($errors) {
$error_view = id(new AphrontErrorView())
->setTitle(pht('Errors editing conpherence.'))
->setInsideDialogue(true)
->setErrors($errors);
}
switch ($action) {
case 'metadata':
default:
$dialogue = $this->renderMetadataDialogue($conpherence, $error_view);
break;
}
return id(new AphrontDialogResponse())
->setDialog($dialogue
->setUser($user)
->setWidth(AphrontDialogView::WIDTH_FORM)
->setSubmitURI($this->getApplicationURI('update/'.$conpherence_id.'/'))
->addSubmitButton()
->addCancelButton($this->getApplicationURI($conpherence->getID().'/')));
}
private function renderMetadataDialogue(
ConpherenceThread $conpherence,
$error_view) {
$form = id(new AphrontFormLayoutView())
->appendChild($error_view)
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Title'))
->setName('title')
->setValue($conpherence->getTitle()));
$image = $conpherence->getImage(ConpherenceImageData::SIZE_ORIG);
if ($image) {
$form
->appendChild(
id(new AphrontFormMarkupControl())
->setLabel(pht('Image'))
->setValue(phutil_tag(
'img',
array(
'src' =>
$conpherence->loadImageURI(ConpherenceImageData::SIZE_HEAD),
))))
->appendChild(
id(new AphrontFormCropControl())
->setLabel(pht('Crop Image'))
->setValue($image)
->setWidth(ConpherenceImageData::HEAD_WIDTH)
->setHeight(ConpherenceImageData::HEAD_HEIGHT))
->appendChild(
id(new ConpherenceFormDragAndDropUploadControl())
->setLabel(pht('Change Image')));
} else {
$form
->appendChild(
id(new ConpherenceFormDragAndDropUploadControl())
->setLabel(pht('Image')));
}
require_celerity_resource('conpherence-update-css');
return id(new AphrontDialogView())
->setTitle(pht('Update Conpherence'))
->addHiddenInput('action', 'metadata')
->addHiddenInput('__continue__', true)
->appendChild($form);
}
private function loadAndRenderUpdates(
$conpherence_id,
$latest_transaction_id) {
$user = $this->getRequest()->getUser();
$conpherence = id(new ConpherenceThreadQuery())
->setViewer($user)
->setAfterID($latest_transaction_id)
->needHeaderPics(true)
->needWidgetData(true)
->withIDs(array($conpherence_id))
->executeOne();
$data = $this->renderConpherenceTransactions($conpherence);
$rendered_transactions = $data['transactions'];
$new_latest_transaction_id = $data['latest_transaction_id'];
$selected = true;
$nav_item = $this->buildConpherenceMenuItem(
$conpherence,
'-nav-item',
$selected);
$menu_item = $this->buildConpherenceMenuItem(
$conpherence,
'-menu-item',
$selected);
$header = $this->buildHeaderPaneContent($conpherence);
$file_widget = id(new ConpherenceFileWidgetView())
->setUser($this->getRequest()->getUser())
->setConpherence($conpherence)
->setUpdateURI(
$this->getApplicationURI('update/'.$conpherence->getID().'/'));
$content = array(
'transactions' => $rendered_transactions,
'latest_transaction_id' => $new_latest_transaction_id,
'menu_item' => $menu_item->render(),
'nav_item' => $nav_item->render(),
'conpherence_phid' => $conpherence->getPHID(),
'header' => $header,
'file_widget' => $file_widget->render()
);
return $content;
}
}
diff --git a/src/applications/conpherence/view/ConpherenceFileWidgetView.php b/src/applications/conpherence/view/ConpherenceFileWidgetView.php
index 97a6b5d829..6d790ea5e2 100644
--- a/src/applications/conpherence/view/ConpherenceFileWidgetView.php
+++ b/src/applications/conpherence/view/ConpherenceFileWidgetView.php
@@ -1,111 +1,111 @@
<?php
final class ConpherenceFileWidgetView extends AphrontView {
private $conpherence;
private $updateURI;
public function setUpdateURI($update_uri) {
$this->updateURI = $update_uri;
return $this;
}
public function getUpdateURI() {
return $this->updateURI;
}
public function setConpherence(ConpherenceThread $conpherence) {
$this->conpherence = $conpherence;
return $this;
}
public function getConpherence() {
return $this->conpherence;
}
public function render() {
require_celerity_resource('sprite-docs-css');
$conpherence = $this->getConpherence();
$widget_data = $conpherence->getWidgetData();
$files = $widget_data['files'];
$files_authors = $widget_data['files_authors'];
$files_html = array();
foreach ($files as $file) {
$icon_class = $file->getDisplayIconForMimeType();
$icon_view = phutil_tag(
'div',
array(
'class' => 'file-icon sprite-docs '.$icon_class
),
'');
$file_view = id(new PhabricatorFileLinkView())
->setFilePHID($file->getPHID())
- ->setFileName($file->getName())
+ ->setFileName(phutil_utf8_shorten($file->getName(), 38))
->setFileViewable($file->isViewableImage())
->setFileViewURI($file->getBestURI())
->setCustomClass('file-title');
$who_done_it_text = '';
// system generated files don't have authors
if ($file->getAuthorPHID()) {
$who_done_it_text = pht(
'by %s ',
$files_authors[$file->getPHID()]->renderLink());
}
$date_text = phabricator_relative_date(
$file->getDateCreated(),
$this->getUser());
$who_done_it = phutil_tag(
'div',
array(
'class' => 'file-uploaded-by'
),
pht('Uploaded %s%s.', $who_done_it_text, $date_text));
$extra = '';
if ($file->isViewableImage()) {
$meta = $file_view->getMetadata();
$extra = javelin_tag(
'a',
array(
'sigil' => 'lightboxable',
'meta' => $meta,
'class' => 'file-extra',
),
phutil_tag(
'img',
array(
'src' => $file->getThumb160x120URI()
),
''));
}
$divider = phutil_tag(
'div',
array(
'class' => 'divider'
),
'');
$files_html[] = phutil_tag(
'div',
array(
'class' => 'file-entry'
),
array(
$icon_view,
$file_view,
$who_done_it,
$extra,
$divider
));
}
return phutil_tag(
'div',
array('class' => 'file-list'),
$files_html);
}
}
diff --git a/src/applications/files/storage/PhabricatorFile.php b/src/applications/files/storage/PhabricatorFile.php
index e746fd1a53..dcbb1d6153 100644
--- a/src/applications/files/storage/PhabricatorFile.php
+++ b/src/applications/files/storage/PhabricatorFile.php
@@ -1,674 +1,689 @@
<?php
final class PhabricatorFile extends PhabricatorFileDAO
implements PhabricatorPolicyInterface {
const STORAGE_FORMAT_RAW = 'raw';
const METADATA_IMAGE_WIDTH = 'width';
const METADATA_IMAGE_HEIGHT = 'height';
protected $phid;
protected $name;
protected $mimeType;
protected $byteSize;
protected $authorPHID;
protected $secretKey;
protected $contentHash;
protected $metadata = array();
protected $storageEngine;
protected $storageFormat;
protected $storageHandle;
protected $ttl;
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_SERIALIZATION => array(
'metadata' => self::SERIALIZATION_JSON,
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhabricatorPHIDConstants::PHID_TYPE_FILE);
}
public static function readUploadedFileData($spec) {
if (!$spec) {
throw new Exception("No file was uploaded!");
}
$err = idx($spec, 'error');
if ($err) {
throw new PhabricatorFileUploadException($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.");
}
self::validateFileSize(strlen($file_data));
return $file_data;
}
public static function newFromPHPUpload($spec, array $params = array()) {
$file_data = self::readUploadedFileData($spec);
$file_name = nonempty(
idx($params, 'name'),
idx($spec, 'name'));
$params = array(
'name' => $file_name,
) + $params;
return self::newFromFileData($file_data, $params);
}
public static function newFromXHRUpload($data, array $params = array()) {
self::validateFileSize(strlen($data));
return self::newFromFileData($data, $params);
}
private static function validateFileSize($size) {
$limit = PhabricatorEnv::getEnvConfig('storage.upload-size-limit');
if (!$limit) {
return;
}
$limit = phabricator_parse_bytes($limit);
if ($size > $limit) {
throw new PhabricatorFileUploadException(-1000);
}
}
/**
* Given a block of data, try to load an existing file with the same content
* if one exists. If it does not, build a new file.
*
* This method is generally used when we have some piece of semi-trusted data
* like a diff or a file from a repository that we want to show to the user.
* We can't just dump it out because it may be dangerous for any number of
* reasons; instead, we need to serve it through the File abstraction so it
* ends up on the CDN domain if one is configured and so on. However, if we
* simply wrote a new file every time we'd potentially end up with a lot
* of redundant data in file storage.
*
* To solve these problems, we use file storage as a cache and reuse the
* same file again if we've previously written it.
*
* NOTE: This method unguards writes.
*
* @param string Raw file data.
* @param dict Dictionary of file information.
*/
public static function buildFromFileDataOrHash(
$data,
array $params = array()) {
$file = id(new PhabricatorFile())->loadOneWhere(
'name = %s AND contentHash = %s LIMIT 1',
self::normalizeFileName(idx($params, 'name')),
self::hashFileContent($data));
if (!$file) {
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$file = PhabricatorFile::newFromFileData($data, $params);
unset($unguarded);
}
return $file;
}
public static function newFileFromContentHash($hash, $params) {
// Check to see if a file with same contentHash exist
$file = id(new PhabricatorFile())->loadOneWhere(
'contentHash = %s LIMIT 1', $hash);
if ($file) {
// copy storageEngine, storageHandle, storageFormat
$copy_of_storage_engine = $file->getStorageEngine();
$copy_of_storage_handle = $file->getStorageHandle();
$copy_of_storage_format = $file->getStorageFormat();
$copy_of_byteSize = $file->getByteSize();
$copy_of_mimeType = $file->getMimeType();
$file_name = idx($params, 'name');
$file_name = self::normalizeFileName($file_name);
$file_ttl = idx($params, 'ttl');
$authorPHID = idx($params, 'authorPHID');
$new_file = new PhabricatorFile();
$new_file->setName($file_name);
$new_file->setByteSize($copy_of_byteSize);
$new_file->setAuthorPHID($authorPHID);
$new_file->setTtl($file_ttl);
$new_file->setContentHash($hash);
$new_file->setStorageEngine($copy_of_storage_engine);
$new_file->setStorageHandle($copy_of_storage_handle);
$new_file->setStorageFormat($copy_of_storage_format);
$new_file->setMimeType($copy_of_mimeType);
+ $new_file->copyDimensions($file);
$new_file->save();
return $new_file;
}
return $file;
}
private static function buildFromFileData($data, array $params = array()) {
$selector = PhabricatorEnv::newObjectFromConfig('storage.engine-selector');
if (isset($params['storageEngines'])) {
$engines = $params['storageEngines'];
} else {
$selector = PhabricatorEnv::newObjectFromConfig(
'storage.engine-selector');
$engines = $selector->selectStorageEngines($data, $params);
}
assert_instances_of($engines, 'PhabricatorFileStorageEngine');
if (!$engines) {
throw new Exception("No valid storage engines are available!");
}
$file = new PhabricatorFile();
$data_handle = null;
$engine_identifier = null;
$exceptions = array();
foreach ($engines as $engine) {
$engine_class = get_class($engine);
try {
list($engine_identifier, $data_handle) = $file->writeToEngine(
$engine,
$data,
$params);
// We stored the file somewhere so stop trying to write it to other
// places.
break;
} catch (PhabricatorFileStorageConfigurationException $ex) {
// If an engine is outright misconfigured (or misimplemented), raise
// that immediately since it probably needs attention.
throw $ex;
} catch (Exception $ex) {
phlog($ex);
// If an engine doesn't work, keep trying all the other valid engines
// in case something else works.
$exceptions[$engine_class] = $ex;
}
}
if (!$data_handle) {
throw new PhutilAggregateException(
"All storage engines failed to write file:",
$exceptions);
}
$file_name = idx($params, 'name');
$file_name = self::normalizeFileName($file_name);
$file_ttl = idx($params, 'ttl');
// If for whatever reason, authorPHID isn't passed as a param
// (always the case with newFromFileDownload()), store a ''
$authorPHID = idx($params, 'authorPHID');
$file->setName($file_name);
$file->setByteSize(strlen($data));
$file->setAuthorPHID($authorPHID);
$file->setTtl($file_ttl);
$file->setContentHash(self::hashFileContent($data));
$file->setStorageEngine($engine_identifier);
$file->setStorageHandle($data_handle);
// TODO: This is probably YAGNI, but allows for us to do encryption or
// compression later if we want.
$file->setStorageFormat(self::STORAGE_FORMAT_RAW);
if (isset($params['mime-type'])) {
$file->setMimeType($params['mime-type']);
} else {
$tmp = new TempFile();
Filesystem::writeFile($tmp, $data);
$file->setMimeType(Filesystem::getMimeType($tmp));
}
try {
$file->updateDimensions(false);
} catch (Exception $ex) {
// Do nothing
}
$file->save();
return $file;
}
public static function newFromFileData($data, array $params = array()) {
$hash = self::hashFileContent($data);
$file = self::newFileFromContentHash($hash, $params);
if ($file) {
return $file;
}
return self::buildFromFileData($data, $params);
}
public function migrateToEngine(PhabricatorFileStorageEngine $engine) {
if (!$this->getID() || !$this->getStorageHandle()) {
throw new Exception(
"You can not migrate a file which hasn't yet been saved.");
}
$data = $this->loadFileData();
$params = array(
'name' => $this->getName(),
);
list($new_identifier, $new_handle) = $this->writeToEngine(
$engine,
$data,
$params);
$old_engine = $this->instantiateStorageEngine();
$old_handle = $this->getStorageHandle();
$this->setStorageEngine($new_identifier);
$this->setStorageHandle($new_handle);
$this->save();
$old_engine->deleteFile($old_handle);
return $this;
}
private function writeToEngine(
PhabricatorFileStorageEngine $engine,
$data,
array $params) {
$engine_class = get_class($engine);
$data_handle = $engine->writeFile($data, $params);
if (!$data_handle || strlen($data_handle) > 255) {
// This indicates an improperly implemented storage engine.
throw new PhabricatorFileStorageConfigurationException(
"Storage engine '{$engine_class}' executed writeFile() but did ".
"not return a valid handle ('{$data_handle}') to the data: it ".
"must be nonempty and no longer than 255 characters.");
}
$engine_identifier = $engine->getEngineIdentifier();
if (!$engine_identifier || strlen($engine_identifier) > 32) {
throw new PhabricatorFileStorageConfigurationException(
"Storage engine '{$engine_class}' returned an improper engine ".
"identifier '{$engine_identifier}': it must be nonempty ".
"and no longer than 32 characters.");
}
return array($engine_identifier, $data_handle);
}
public static function newFromFileDownload($uri, array $params = array()) {
// Make sure we're allowed to make a request first
if (!PhabricatorEnv::getEnvConfig('security.allow-outbound-http')) {
throw new Exception("Outbound HTTP requests are disabled!");
}
$uri = new PhutilURI($uri);
$protocol = $uri->getProtocol();
switch ($protocol) {
case 'http':
case 'https':
break;
default:
// Make sure we are not accessing any file:// URIs or similar.
return null;
}
$timeout = 5;
list($file_data) = id(new HTTPSFuture($uri))
->setTimeout($timeout)
->resolvex();
$params = $params + array(
'name' => basename($uri),
);
return self::newFromFileData($file_data, $params);
}
public static function normalizeFileName($file_name) {
return preg_replace('/[^a-zA-Z0-9.~_-]/', '_', $file_name);
}
public function delete() {
// delete all records of this file in transformedfile
$trans_files = id(new PhabricatorTransformedFile())->loadAllWhere(
'TransformedPHID = %s', $this->getPHID());
$this->openTransaction();
foreach ($trans_files as $trans_file) {
$trans_file->delete();
}
$ret = parent::delete();
$this->saveTransaction();
// Check to see if other files are using storage
$other_file = id(new PhabricatorFile())->loadAllWhere(
'storageEngine = %s AND storageHandle = %s AND
storageFormat = %s AND id != %d LIMIT 1', $this->getStorageEngine(),
$this->getStorageHandle(), $this->getStorageFormat(),
$this->getID());
// If this is the only file using the storage, delete storage
if (count($other_file) == 0) {
$engine = $this->instantiateStorageEngine();
$engine->deleteFile($this->getStorageHandle());
}
return $ret;
}
public static function hashFileContent($data) {
return sha1($data);
}
public function loadFileData() {
$engine = $this->instantiateStorageEngine();
$data = $engine->readFile($this->getStorageHandle());
switch ($this->getStorageFormat()) {
case self::STORAGE_FORMAT_RAW:
$data = $data;
break;
default:
throw new Exception("Unknown storage format.");
}
return $data;
}
public function getViewURI() {
if (!$this->getPHID()) {
throw new Exception(
"You must save a file before you can generate a view URI.");
}
$name = phutil_escape_uri($this->getName());
$path = '/file/data/'.$this->getSecretKey().'/'.$this->getPHID().'/'.$name;
return PhabricatorEnv::getCDNURI($path);
}
public function getInfoURI() {
return '/file/info/'.$this->getPHID().'/';
}
public function getBestURI() {
if ($this->isViewableInBrowser()) {
return $this->getViewURI();
} else {
return $this->getInfoURI();
}
}
public function getDownloadURI() {
$uri = id(new PhutilURI($this->getViewURI()))
->setQueryParam('download', true);
return (string) $uri;
}
public function getThumb60x45URI() {
$path = '/file/xform/thumb-60x45/'.$this->getPHID().'/'
.$this->getSecretKey().'/';
return PhabricatorEnv::getCDNURI($path);
}
public function getThumb160x120URI() {
$path = '/file/xform/thumb-160x120/'.$this->getPHID().'/'
.$this->getSecretKey().'/';
return PhabricatorEnv::getCDNURI($path);
}
public function getPreview140URI() {
$path = '/file/xform/preview-140/'.$this->getPHID().'/'
.$this->getSecretKey().'/';
return PhabricatorEnv::getCDNURI($path);
}
public function getPreview220URI() {
$path = '/file/xform/preview-220/'.$this->getPHID().'/'
.$this->getSecretKey().'/';
return PhabricatorEnv::getCDNURI($path);
}
public function getThumb220x165URI() {
$path = '/file/xform/thumb-220x165/'.$this->getPHID().'/'
.$this->getSecretKey().'/';
return PhabricatorEnv::getCDNURI($path);
}
public function getThumb280x210URI() {
$path = '/file/xform/thumb-280x210/'.$this->getPHID().'/'
.$this->getSecretKey().'/';
return PhabricatorEnv::getCDNURI($path);
}
public function isViewableInBrowser() {
return ($this->getViewableMimeType() !== null);
}
public function isViewableImage() {
if (!$this->isViewableInBrowser()) {
return false;
}
$mime_map = PhabricatorEnv::getEnvConfig('files.image-mime-types');
$mime_type = $this->getMimeType();
return idx($mime_map, $mime_type);
}
public function isTransformableImage() {
// NOTE: The way the 'gd' extension works in PHP is that you can install it
// with support for only some file types, so it might be able to handle
// PNG but not JPEG. Try to generate thumbnails for whatever we can. Setup
// warns you if you don't have complete support.
$matches = null;
$ok = preg_match(
'@^image/(gif|png|jpe?g)@',
$this->getViewableMimeType(),
$matches);
if (!$ok) {
return false;
}
switch ($matches[1]) {
case 'jpg';
case 'jpeg':
return function_exists('imagejpeg');
break;
case 'png':
return function_exists('imagepng');
break;
case 'gif':
return function_exists('imagegif');
break;
default:
throw new Exception('Unknown type matched as image MIME type.');
}
}
public static function getTransformableImageFormats() {
$supported = array();
if (function_exists('imagejpeg')) {
$supported[] = 'jpg';
}
if (function_exists('imagepng')) {
$supported[] = 'png';
}
if (function_exists('imagegif')) {
$supported[] = 'gif';
}
return $supported;
}
protected function instantiateStorageEngine() {
return self::buildEngine($this->getStorageEngine());
}
public static function buildEngine($engine_identifier) {
$engines = self::buildAllEngines();
foreach ($engines as $engine) {
if ($engine->getEngineIdentifier() == $engine_identifier) {
return $engine;
}
}
throw new Exception(
"Storage engine '{$engine_identifier}' could not be located!");
}
public static function buildAllEngines() {
$engines = id(new PhutilSymbolLoader())
->setType('class')
->setConcreteOnly(true)
->setAncestorClass('PhabricatorFileStorageEngine')
->selectAndLoadSymbols();
$results = array();
foreach ($engines as $engine_class) {
$results[] = newv($engine_class['name'], array());
}
return $results;
}
public function getViewableMimeType() {
$mime_map = PhabricatorEnv::getEnvConfig('files.viewable-mime-types');
$mime_type = $this->getMimeType();
$mime_parts = explode(';', $mime_type);
$mime_type = trim(reset($mime_parts));
return idx($mime_map, $mime_type);
}
public function getDisplayIconForMimeType() {
$mime_map = PhabricatorEnv::getEnvConfig('files.icon-mime-types');
$mime_type = $this->getMimeType();
return idx($mime_map, $mime_type, 'docs_file');
}
public function validateSecretKey($key) {
return ($key == $this->getSecretKey());
}
public function save() {
if (!$this->getSecretKey()) {
$this->setSecretKey($this->generateSecretKey());
}
return parent::save();
}
public function generateSecretKey() {
return Filesystem::readRandomCharacters(20);
}
public function updateDimensions($save = true) {
if (!$this->isViewableImage()) {
throw new Exception(
"This file is not a viewable image.");
}
if (!function_exists("imagecreatefromstring")) {
throw new Exception(
"Cannot retrieve image information.");
}
$data = $this->loadFileData();
$img = imagecreatefromstring($data);
if ($img === false) {
throw new Exception(
"Error when decoding image.");
}
$this->metadata[self::METADATA_IMAGE_WIDTH] = imagesx($img);
$this->metadata[self::METADATA_IMAGE_HEIGHT] = imagesy($img);
if ($save) {
$this->save();
}
return $this;
}
+ public function copyDimensions(PhabricatorFile $file) {
+ $metadata = $file->getMetadata();
+ $width = idx($metadata, self::METADATA_IMAGE_WIDTH);
+ if ($width) {
+ $this->metadata[self::METADATA_IMAGE_WIDTH] = $width;
+ }
+ $height = idx($metadata, self::METADATA_IMAGE_HEIGHT);
+ if ($height) {
+ $this->metadata[self::METADATA_IMAGE_HEIGHT] = $height;
+ }
+
+ return $this;
+ }
+
public static function getMetadataName($metadata) {
switch ($metadata) {
case self::METADATA_IMAGE_WIDTH:
$name = pht('Width');
break;
case self::METADATA_IMAGE_HEIGHT:
$name = pht('Height');
break;
default:
$name = ucfirst($metadata);
break;
}
return $name;
}
/* -( PhabricatorPolicyInterface Implementation )-------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
);
}
public function getPolicy($capability) {
// TODO: Implement proper per-object policies.
return PhabricatorPolicies::POLICY_USER;
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return false;
}
}
diff --git a/webroot/rsrc/css/application/conpherence/widget-pane.css b/webroot/rsrc/css/application/conpherence/widget-pane.css
index 97a6b19e58..f793eca9b7 100644
--- a/webroot/rsrc/css/application/conpherence/widget-pane.css
+++ b/webroot/rsrc/css/application/conpherence/widget-pane.css
@@ -1,174 +1,176 @@
/**
* @provides conpherence-widget-pane-css
*/
.conpherence-widget-pane {
position: fixed;
right: 0px;
top: 124px;
width: 320px;
height: 100%;
border-width: 0 0 0 1px;
border-color: #CCC;
border-style: solid;
background: url('/rsrc/image/texture/dust_background.jpg');
overflow-y: auto;
}
.conpherence-widget-pane .aphront-form-input {
margin: 0;
width: 100%;
}
.conpherence-widget-pane .widgets-header {
background-color: #d8dce2;
width: 320px;
box-shadow: 0px 2px 2px rgba(0,0,0,0.15);
}
.conpherence-widget-pane .widgets-header .widgets-header-icon-holder {
height: 40px;
}
.device-desktop .conpherence-widget-pane .widgets-header
.widgets-header-icon-holder {
width: 196px;
margin: 0px auto 0px auto;
}
.conpherence-widget-pane .widgets-header .sprite-conpherence {
display: block;
width: 29px;
height: 34px;
margin: 4px 0px 0px 20px;
float: left;
clear: none;
}
.conpherence-widget-pane .widgets-header .conpherence_list_on,
.conpherence-widget-pane .widgets-header .conpherence_conversation_on,
.conpherence-widget-pane .widgets-header .conpherence_people_on,
.conpherence-widget-pane .widgets-header .conpherence_files_on,
.conpherence-widget-pane .widgets-header .conpherence_calendar_on,
.conpherence-widget-pane .widgets-header .conpherence_settings_on {
border-bottom: 3px solid #525252;
}
.device-desktop .conpherence-widget-pane .widgets-header
#widgets-conpherence-list-toggle,
.device-desktop .conpherence-widget-pane .widgets-header
#widgets-conpherence-conversation-toggle {
display: none;
}
.conpherence-widget-pane .widgets-body {
position: fixed;
overflow-y: auto;
top: 165px;
bottom: 0px;
width: 320px;
}
/* files widget */
.conpherence-widget-pane #widgets-files .file-entry {
padding: 12px 0px 14px 0px;
width: 320px;
}
.conpherence-widget-pane #widgets-files .file-icon {
position: relative;
top: 0px;
left: 8px;
width: 32px;
height: 32px;
float: left;
}
.conpherence-widget-pane #widgets-files .file-title {
position: relative;
top: -4px;
left: 20px;
+ overflow-x: hidden;
+ width: 270px;
font-weight: bold;
}
.conpherence-widget-pane #widgets-files .file-uploaded-by {
color: #bfbfbf;
position: relative;
top: 0px;
left: 20px;
width: 270px;
font-size: 11px;
}
.conpherence-widget-pane #widgets-files .file-extra {
display: block;
height: 120px;
width: 160px;
margin: 8px 0px 8px 52px;
border: 1px solid rgb(24, 85, 157);
}
.conpherence-widget-pane #widgets-files .divider {
float: left;
clear: both;
width: 242px;
margin: 8px 0px 0px 52px;
border: 1px dashed #bfbfbf;
}
/* calendar widget */
.conpherence-widget-pane #widgets-calendar .user-status {
height: 60px;
}
.conpherence-widget-pane #widgets-calendar .user-status .icon {
border-radius: 10px;
position: relative;
top: 24px;
left: 12px;
height: 16px;
width: 16px;
box-shadow: 0px 0px 1px #000;
}
.conpherence-widget-pane #widgets-calendar .sporadic .icon {
background-color: rgb(222, 226, 232);
}
.conpherence-widget-pane #widgets-calendar .away .icon {
background-color: rgb(102, 204, 255);
}
.conpherence-widget-pane #widgets-calendar .user-status .epoch-range {
float: right;
font-style: italic;
position: relative;
top: 24px;
right: 8px;
font-size: 11px;
}
.conpherence-widget-pane #widgets-calendar .user-status .description {
position: relative;
left: 40px;
top: 0px;
width: 260px;
}
.conpherence-widget-pane #widgets-calendar .user-status .participant {
position: relative;
left: 40px;
top: 0px;
font-style: italic;
font-size: 11px;
width: 260px;
}
.conpherence-widget-pane .widget-icon {
display: block;
height: 14px;
width: 14px;
}
.conpherence-widget-pane .phabricator-remarkup-embed-layout-link {
padding-bottom: 1px;
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Nov 13, 5:24 PM (10 h, 7 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
336412
Default Alt Text
(37 KB)

Event Timeline