Page MenuHomestyx hydra

No OneTemporary

diff --git a/resources/sql/patches/20130215.phabricatorfileaddttl.sql b/resources/sql/patches/20130215.phabricatorfileaddttl.sql
new file mode 100644
index 0000000000..d8c3cbf8b3
--- /dev/null
+++ b/resources/sql/patches/20130215.phabricatorfileaddttl.sql
@@ -0,0 +1,3 @@
+ALTER TABLE {$NAMESPACE}_file.file
+ ADD ttl INT(10) UNSIGNED DEFAULT NULL,
+ ADD KEY key_ttl (ttl);
diff --git a/src/applications/diffusion/controller/DiffusionBrowseFileController.php b/src/applications/diffusion/controller/DiffusionBrowseFileController.php
index 474605c44c..cb285fda77 100644
--- a/src/applications/diffusion/controller/DiffusionBrowseFileController.php
+++ b/src/applications/diffusion/controller/DiffusionBrowseFileController.php
@@ -1,951 +1,952 @@
<?php
final class DiffusionBrowseFileController extends DiffusionController {
private $corpusType = 'text';
private $lintCommit;
private $lintMessages;
public function processRequest() {
$request = $this->getRequest();
$drequest = $this->getDiffusionRequest();
$before = $request->getStr('before');
if ($before) {
return $this->buildBeforeResponse($before);
}
$path = $drequest->getPath();
$selected = $request->getStr('view');
$preferences = $request->getUser()->loadPreferences();
if (!$selected) {
$selected = $preferences->getPreference(
PhabricatorUserPreferences::PREFERENCE_DIFFUSION_VIEW,
'highlighted');
} else if ($request->isFormPost() && $selected != 'raw') {
$preferences->setPreference(
PhabricatorUserPreferences::PREFERENCE_DIFFUSION_VIEW,
$selected);
$preferences->save();
return id(new AphrontRedirectResponse())
->setURI($request->getRequestURI()->alter('view', $selected));
}
$needs_blame = ($selected == 'blame' || $selected == 'plainblame');
$file_query = DiffusionFileContentQuery::newFromDiffusionRequest(
$this->diffusionRequest);
$file_query->setViewer($request->getUser());
$file_query->setNeedsBlame($needs_blame);
$file_query->loadFileContent();
$data = $file_query->getRawData();
if ($selected === 'raw') {
return $this->buildRawResponse($path, $data);
}
$this->loadLintMessages();
// Build the content of the file.
$corpus = $this->buildCorpus(
$selected,
$file_query,
$needs_blame,
$drequest,
$path,
$data);
require_celerity_resource('diffusion-source-css');
if ($this->corpusType == 'text') {
$view_select_panel = $this->renderViewSelectPanel($selected);
} else {
$view_select_panel = null;
}
// Render the page.
$content = array();
$follow = $request->getStr('follow');
if ($follow) {
$notice = new AphrontErrorView();
$notice->setSeverity(AphrontErrorView::SEVERITY_WARNING);
$notice->setTitle('Unable to Continue');
switch ($follow) {
case 'first':
$notice->appendChild(
"Unable to continue tracing the history of this file because ".
"this commit is the first commit in the repository.");
break;
case 'created':
$notice->appendChild(
"Unable to continue tracing the history of this file because ".
"this commit created the file.");
break;
}
$content[] = $notice;
}
$renamed = $request->getStr('renamed');
if ($renamed) {
$notice = new AphrontErrorView();
$notice->setSeverity(AphrontErrorView::SEVERITY_NOTICE);
$notice->setTitle('File Renamed');
$notice->appendChild(
"File history passes through a rename from '".$drequest->getPath().
"' to '".$renamed."'.");
$content[] = $notice;
}
$content[] = $view_select_panel;
$content[] = $corpus;
$content[] = $this->buildOpenRevisions();
$nav = $this->buildSideNav('browse', true);
$nav->appendChild($content);
$crumbs = $this->buildCrumbs(
array(
'branch' => true,
'path' => true,
'view' => 'browse',
));
$nav->setCrumbs($crumbs);
$basename = basename($this->getDiffusionRequest()->getPath());
return $this->buildApplicationPage(
$nav,
array(
'title' => $basename,
));
}
private function loadLintMessages() {
$drequest = $this->getDiffusionRequest();
$branch = $drequest->loadBranch();
if (!$branch || !$branch->getLintCommit()) {
return;
}
$this->lintCommit = $branch->getLintCommit();
$conn = id(new PhabricatorRepository())->establishConnection('r');
$where = '';
if ($drequest->getLint()) {
$where = qsprintf(
$conn,
'AND code = %s',
$drequest->getLint());
}
$this->lintMessages = queryfx_all(
$conn,
'SELECT * FROM %T WHERE branchID = %d %Q AND path = %s',
PhabricatorRepository::TABLE_LINTMESSAGE,
$branch->getID(),
$where,
'/'.$drequest->getPath());
}
private function buildCorpus($selected,
DiffusionFileContentQuery $file_query,
$needs_blame,
DiffusionRequest $drequest,
$path,
$data) {
if (ArcanistDiffUtils::isHeuristicBinaryFile($data)) {
$file = $this->loadFileForData($path, $data);
$file_uri = $file->getBestURI();
if ($file->isViewableImage()) {
$this->corpusType = 'image';
return $this->buildImageCorpus($file_uri);
} else {
$this->corpusType = 'binary';
return $this->buildBinaryCorpus($file_uri, $data);
}
}
switch ($selected) {
case 'plain':
$style =
"margin: 1em 2em; width: 90%; height: 80em; font-family: monospace";
$corpus = phutil_tag(
'textarea',
array(
'style' => $style,
),
$file_query->getRawData());
break;
case 'plainblame':
$style =
"margin: 1em 2em; width: 90%; height: 80em; font-family: monospace";
list($text_list, $rev_list, $blame_dict) =
$file_query->getBlameData();
$rows = array();
foreach ($text_list as $k => $line) {
$rev = $rev_list[$k];
if (isset($blame_dict[$rev]['handle'])) {
$author = $blame_dict[$rev]['handle']->getName();
} else {
$author = $blame_dict[$rev]['author'];
}
$rows[] =
sprintf("%-10s %-20s %s", substr($rev, 0, 7), $author, $line);
}
$corpus = phutil_tag(
'textarea',
array(
'style' => $style,
),
implode("\n", $rows));
break;
case 'highlighted':
case 'blame':
default:
require_celerity_resource('syntax-highlighting-css');
list($text_list, $rev_list, $blame_dict) = $file_query->getBlameData();
$text_list = implode("\n", $text_list);
$text_list = PhabricatorSyntaxHighlighter::highlightWithFilename(
$path,
$text_list);
$text_list = explode("\n", $text_list);
$rows = $this->buildDisplayRows($text_list, $rev_list, $blame_dict,
$needs_blame, $drequest, $file_query, $selected);
$id = celerity_generate_unique_node_id();
$projects = $drequest->loadArcanistProjects();
$langs = array();
foreach ($projects as $project) {
$ls = $project->getSymbolIndexLanguages();
if (!$ls) {
continue;
}
$dep_projects = $project->getSymbolIndexProjects();
$dep_projects[] = $project->getPHID();
foreach ($ls as $lang) {
if (!isset($langs[$lang])) {
$langs[$lang] = array();
}
$langs[$lang] += $dep_projects + array($project);
}
}
$lang = last(explode('.', $drequest->getPath()));
if (isset($langs[$lang])) {
Javelin::initBehavior(
'repository-crossreference',
array(
'container' => $id,
'lang' => $lang,
'projects' => $langs[$lang],
));
}
$corpus_table = javelin_tag(
'table',
array(
'class' => "diffusion-source remarkup-code PhabricatorMonospaced",
'sigil' => 'diffusion-source',
),
$rows);
$corpus = phutil_tag(
'div',
array(
'style' => 'padding: 0 2em;',
'id' => $id,
),
$corpus_table);
break;
}
return $corpus;
}
private function renderViewSelectPanel($selected) {
$toggle_blame = array(
'highlighted' => 'blame',
'blame' => 'highlighted',
'plain' => 'plainblame',
'plainblame' => 'plain',
'raw' => 'raw', // not a real case.
);
$toggle_highlight = array(
'highlighted' => 'plain',
'blame' => 'plainblame',
'plain' => 'highlighted',
'plainblame' => 'blame',
'raw' => 'raw', // not a real case.
);
$user = $this->getRequest()->getUser();
$base_uri = $this->getRequest()->getRequestURI();
$blame_on = ($selected == 'blame' || $selected == 'plainblame');
if ($blame_on) {
$blame_text = pht('Disable Blame');
} else {
$blame_text = pht('Enable Blame');
}
$blame_button = $this->createViewAction(
$blame_text,
$base_uri->alter('view', $toggle_blame[$selected]),
$user);
$highlight_on = ($selected == 'blame' || $selected == 'highlighted');
if ($highlight_on) {
$highlight_text = pht('Disable Highlighting');
} else {
$highlight_text = pht('Enable Highlighting');
}
$highlight_button = $this->createViewAction(
$highlight_text,
$base_uri->alter('view', $toggle_highlight[$selected]),
$user);
$href = null;
if ($this->getRequest()->getStr('lint') !== null) {
$lint_text = pht('Hide %d Lint Message(s)', count($this->lintMessages));
$href = $base_uri->alter('lint', null);
} else if ($this->lintCommit === null) {
$lint_text = pht('Lint not Available');
} else {
$lint_text = pht(
'Show %d Lint Message(s)',
count($this->lintMessages));
$href = $this->getDiffusionRequest()->generateURI(array(
'action' => 'browse',
'commit' => $this->lintCommit,
))->alter('lint', '');
}
$lint_button = $this->createViewAction(
$lint_text,
$href,
$user);
if (!$href) {
$lint_button->setDisabled(true);
}
$raw_button = $this->createViewAction(
pht('View Raw File'),
$base_uri->alter('view', 'raw'),
$user,
'file');
$edit_button = $this->createEditAction();
return id(new PhabricatorActionListView())
->setUser($user)
->addAction($blame_button)
->addAction($highlight_button)
->addAction($lint_button)
->addAction($raw_button)
->addAction($edit_button);
}
private function createViewAction(
$localized_text,
$href,
$user,
$icon = null) {
return id(new PhabricatorActionView())
->setName($localized_text)
->setIcon($icon)
->setUser($user)
->setRenderAsForm(true)
->setHref($href);
}
private function createEditAction() {
$request = $this->getRequest();
$user = $request->getUser();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$path = $drequest->getPath();
$line = nonempty((int)$drequest->getLine(), 1);
$callsign = $repository->getCallsign();
$editor_link = $user->loadEditorLink($path, $line, $callsign);
$action = id(new PhabricatorActionView())
->setName(pht('Open in Editor'))
->setIcon('edit');
$action->setHref($editor_link);
$action->setDisabled(!$editor_link);
return $action;
}
private function buildDisplayRows(
array $text_list,
array $rev_list,
array $blame_dict,
$needs_blame,
DiffusionRequest $drequest,
DiffusionFileContentQuery $file_query,
$selected) {
if ($blame_dict) {
$epoch_list = ipull(ifilter($blame_dict, 'epoch'), 'epoch');
$epoch_min = min($epoch_list);
$epoch_max = max($epoch_list);
$epoch_range = ($epoch_max - $epoch_min) + 1;
}
$line_arr = array();
$line_str = $drequest->getLine();
$ranges = explode(',', $line_str);
foreach ($ranges as $range) {
if (strpos($range, '-') !== false) {
list($min, $max) = explode('-', $range, 2);
$line_arr[] = array(
'min' => min($min, $max),
'max' => max($min, $max),
);
} else if (strlen($range)) {
$line_arr[] = array(
'min' => $range,
'max' => $range,
);
}
}
$display = array();
$line_number = 1;
$last_rev = null;
$color = null;
foreach ($text_list as $k => $line) {
$display_line = array(
'color' => null,
'epoch' => null,
'commit' => null,
'author' => null,
'target' => null,
'highlighted' => null,
'line' => $line_number,
'data' => $line,
);
if ($needs_blame) {
// If the line's rev is same as the line above, show empty content
// with same color; otherwise generate blame info. The newer a change
// is, the more saturated the color.
$rev = idx($rev_list, $k, $last_rev);
if ($last_rev == $rev) {
$display_line['color'] = $color;
} else {
$blame = $blame_dict[$rev];
if (!isset($blame['epoch'])) {
$color = '#ffd'; // Render as warning.
} else {
$color_ratio = ($blame['epoch'] - $epoch_min) / $epoch_range;
$color_value = 0xF6 * (1.0 - $color_ratio);
$color = sprintf(
'#%02x%02x%02x',
$color_value,
0xF6,
$color_value);
}
$display_line['epoch'] = idx($blame, 'epoch');
$display_line['color'] = $color;
$display_line['commit'] = $rev;
if (isset($blame['handle'])) {
$author_link = $blame['handle']->renderLink();
} else {
$author_link = phutil_tag(
'span',
array(
),
$blame['author']);
}
$display_line['author'] = $author_link;
$last_rev = $rev;
}
}
if ($line_arr) {
if ($line_number == $line_arr[0]['min']) {
$display_line['target'] = true;
}
foreach ($line_arr as $range) {
if ($line_number >= $range['min'] &&
$line_number <= $range['max']) {
$display_line['highlighted'] = true;
}
}
}
$display[] = $display_line;
++$line_number;
}
$commits = array_filter(ipull($display, 'commit'));
if ($commits) {
$commits = id(new PhabricatorAuditCommitQuery())
->withIdentifiers($drequest->getRepository()->getID(), $commits)
->needCommitData(true)
->execute();
$commits = mpull($commits, null, 'getCommitIdentifier');
}
$revision_ids = id(new DifferentialRevision())
->loadIDsByCommitPHIDs(mpull($commits, 'getPHID'));
$revisions = array();
if ($revision_ids) {
$revisions = id(new DifferentialRevision())->loadAllWhere(
'id IN (%Ld)',
$revision_ids);
}
$request = $this->getRequest();
$user = $request->getUser();
Javelin::initBehavior('phabricator-oncopy', array());
$engine = null;
$inlines = array();
if ($this->getRequest()->getStr('lint') !== null && $this->lintMessages) {
$engine = new PhabricatorMarkupEngine();
$engine->setViewer($user);
foreach ($this->lintMessages as $message) {
$inline = id(new PhabricatorAuditInlineComment())
->setID($message['id'])
->setSyntheticAuthor(
ArcanistLintSeverity::getStringForSeverity($message['severity']).
' '.$message['code'].' ('.$message['name'].')')
->setLineNumber($message['line'])
->setContent($message['description']);
$inlines[$message['line']][] = $inline;
$engine->addObject(
$inline,
PhabricatorInlineCommentInterface::MARKUP_FIELD_BODY);
}
$engine->process();
require_celerity_resource('differential-changeset-view-css');
}
$rows = $this->renderInlines(
idx($inlines, 0, array()),
$needs_blame,
$engine);
foreach ($display as $line) {
$line_href = $drequest->generateURI(
array(
'action' => 'browse',
'line' => $line['line'],
'stable' => true,
));
$blame = array();
if ($line['color']) {
$color = $line['color'];
$before_link = null;
$commit_link = null;
$revision_link = null;
if (idx($line, 'commit')) {
$commit = $line['commit'];
$summary = 'Unknown';
if (idx($commits, $commit)) {
$summary = $commits[$commit]->getCommitData()->getSummary();
}
$tooltip = phabricator_date(
$line['epoch'],
$user)." \xC2\xB7 ".$summary;
Javelin::initBehavior('phabricator-tooltips', array());
require_celerity_resource('aphront-tooltip-css');
$commit_link = javelin_tag(
'a',
array(
'href' => $drequest->generateURI(
array(
'action' => 'commit',
'commit' => $line['commit'],
)),
'sigil' => 'has-tooltip',
'meta' => array(
'tip' => $tooltip,
'align' => 'E',
'size' => 600,
),
),
phutil_utf8_shorten($line['commit'], 9, ''));
$revision_id = null;
if (idx($commits, $commit)) {
$revision_id = idx($revision_ids, $commits[$commit]->getPHID());
}
if ($revision_id) {
$revision = idx($revisions, $revision_id);
if (!$revision) {
$tooltip = '(Invalid revision)';
} else {
$tooltip =
phabricator_date($revision->getDateModified(), $user).
" \xC2\xB7 ".
$revision->getTitle();
}
$revision_link = javelin_tag(
'a',
array(
'href' => '/D'.$revision_id,
'sigil' => 'has-tooltip',
'meta' => array(
'tip' => $tooltip,
'align' => 'E',
'size' => 600,
),
),
'D'.$revision_id);
}
$uri = $line_href->alter('before', $commit);
$before_link = javelin_tag(
'a',
array(
'href' => $uri->setQueryParam('view', 'blame'),
'sigil' => 'has-tooltip',
'meta' => array(
'tip' => 'Skip Past This Commit',
'align' => 'E',
'size' => 300,
),
),
"\xC2\xAB");
}
$blame[] = phutil_tag(
'th',
array(
'class' => 'diffusion-blame-link',
'style' => 'background: '.$color,
),
$before_link);
$blame[] = phutil_tag(
'th',
array(
'class' => 'diffusion-rev-link',
'style' => 'background: '.$color,
),
$commit_link);
$blame[] = phutil_tag(
'th',
array(
'class' => 'diffusion-rev-link',
'style' => 'background: '.$color,
),
$revision_link);
$blame[] = phutil_tag(
'th',
array(
'class' => 'diffusion-author-link',
'style' => 'background: '.$color,
),
idx($line, 'author'));
}
$line_link = phutil_tag(
'a',
array(
'href' => $line_href,
),
$line['line']);
$blame[] = javelin_tag(
'th',
array(
'class' => 'diffusion-line-link',
'sigil' => 'diffusion-line-link',
'style' => isset($color) ? 'background: '.$color : null,
),
$line_link);
Javelin::initBehavior('diffusion-line-linker');
if ($line['target']) {
Javelin::initBehavior(
'diffusion-jump-to',
array(
'target' => 'scroll_target',
));
$anchor_text = phutil_tag(
'a',
array(
'id' => 'scroll_target',
),
'');
} else {
$anchor_text = null;
}
$blame[] = phutil_tag(
'td',
array(
),
array(
$anchor_text,
// NOTE: See phabricator-oncopy behavior.
"\xE2\x80\x8B",
// TODO: [HTML] Not ideal.
phutil_safe_html($line['data']),
));
$rows[] = phutil_tag(
'tr',
array(
'class' => ($line['highlighted'] ? 'highlighted' : null),
),
$blame);
$rows = array_merge($rows, $this->renderInlines(
idx($inlines, $line['line'], array()),
$needs_blame,
$engine));
}
return $rows;
}
private function renderInlines(array $inlines, $needs_blame, $engine) {
$rows = array();
foreach ($inlines as $inline) {
$inline_view = id(new DifferentialInlineCommentView())
->setMarkupEngine($engine)
->setInlineComment($inline)
->render();
$row = array_fill(0, ($needs_blame ? 5 : 1), phutil_tag('th'));
$row[] = phutil_tag('td', array(), $inline_view);
$rows[] = phutil_tag('tr', array('class' => 'inline'), $row);
}
return $rows;
}
private function loadFileForData($path, $data) {
return PhabricatorFile::buildFromFileDataOrHash(
$data,
array(
'name' => basename($path),
+ 'ttl' => time() + 60 * 60 * 24,
));
}
private function buildRawResponse($path, $data) {
$file = $this->loadFileForData($path, $data);
return id(new AphrontRedirectResponse())->setURI($file->getBestURI());
}
private function buildImageCorpus($file_uri) {
$properties = new PhabricatorPropertyListView();
$properties->addProperty(
pht('Image'),
phutil_tag(
'img',
array(
'src' => $file_uri,
)));
$actions = id(new PhabricatorActionListView())
->setUser($this->getRequest()->getUser())
->addAction($this->createEditAction());
return array($actions, $properties);
}
private function buildBinaryCorpus($file_uri, $data) {
$properties = new PhabricatorPropertyListView();
$size = strlen($data);
$properties->addTextContent(
pht(
'This is a binary file. It is %s byte(s) in length.',
new PhutilNumber($size)));
$actions = id(new PhabricatorActionListView())
->setUser($this->getRequest()->getUser())
->addAction($this->createEditAction())
->addAction(
id(new PhabricatorActionView())
->setName(pht('Download Binary File...'))
->setIcon('download')
->setHref($file_uri));
return array($actions, $properties);
}
private function buildBeforeResponse($before) {
$request = $this->getRequest();
$drequest = $this->getDiffusionRequest();
// NOTE: We need to get the grandparent so we can capture filename changes
// in the parent.
$parent = $this->loadParentRevisionOf($before);
$old_filename = null;
$was_created = false;
if ($parent) {
$grandparent = $this->loadParentRevisionOf(
$parent->getCommitIdentifier());
if ($grandparent) {
$rename_query = new DiffusionRenameHistoryQuery();
$rename_query->setRequest($drequest);
$rename_query->setOldCommit($grandparent->getCommitIdentifier());
$old_filename = $rename_query->loadOldFilename();
$was_created = $rename_query->getWasCreated();
}
}
$follow = null;
if ($was_created) {
// If the file was created in history, that means older commits won't
// have it. Since we know it existed at 'before', it must have been
// created then; jump there.
$target_commit = $before;
$follow = 'created';
} else if ($parent) {
// If we found a parent, jump to it. This is the normal case.
$target_commit = $parent->getCommitIdentifier();
} else {
// If there's no parent, this was probably created in the initial commit?
// And the "was_created" check will fail because we can't identify the
// grandparent. Keep the user at 'before'.
$target_commit = $before;
$follow = 'first';
}
$path = $drequest->getPath();
$renamed = null;
if ($old_filename !== null &&
$old_filename !== '/'.$path) {
$renamed = $path;
$path = $old_filename;
}
$line = null;
// If there's a follow error, drop the line so the user sees the message.
if (!$follow) {
$line = $this->getBeforeLineNumber($target_commit);
}
$before_uri = $drequest->generateURI(
array(
'action' => 'browse',
'commit' => $target_commit,
'line' => $line,
'path' => $path,
));
$before_uri->setQueryParams($request->getRequestURI()->getQueryParams());
$before_uri = $before_uri->alter('before', null);
$before_uri = $before_uri->alter('renamed', $renamed);
$before_uri = $before_uri->alter('follow', $follow);
return id(new AphrontRedirectResponse())->setURI($before_uri);
}
private function getBeforeLineNumber($target_commit) {
$drequest = $this->getDiffusionRequest();
$line = $drequest->getLine();
if (!$line) {
return null;
}
$diff_query = DiffusionRawDiffQuery::newFromDiffusionRequest($drequest);
$diff_query->setAgainstCommit($target_commit);
try {
$raw_diff = $diff_query->loadRawDiff();
$old_line = 0;
$new_line = 0;
foreach (explode("\n", $raw_diff) as $text) {
if ($text[0] == '-' || $text[0] == ' ') {
$old_line++;
}
if ($text[0] == '+' || $text[0] == ' ') {
$new_line++;
}
if ($new_line == $line) {
return $old_line;
}
}
// We didn't find the target line.
return $line;
} catch (Exception $ex) {
return $line;
}
}
private function loadParentRevisionOf($commit) {
$drequest = $this->getDiffusionRequest();
$before_req = DiffusionRequest::newFromDictionary(
array(
'repository' => $drequest->getRepository(),
'commit' => $commit,
));
$query = DiffusionCommitParentsQuery::newFromDiffusionRequest($before_req);
$parents = $query->loadParents();
return head($parents);
}
}
diff --git a/src/applications/files/PhabricatorImageTransformer.php b/src/applications/files/PhabricatorImageTransformer.php
index 80ff562bdb..c3a10e4816 100644
--- a/src/applications/files/PhabricatorImageTransformer.php
+++ b/src/applications/files/PhabricatorImageTransformer.php
@@ -1,419 +1,420 @@
<?php
final class PhabricatorImageTransformer {
public function executeMemeTransform(
PhabricatorFile $file,
$upper_text,
$lower_text) {
$image = $this->applyMemeTo($file, $upper_text, $lower_text);
return PhabricatorFile::newFromFileData(
$image,
array(
'name' => 'meme-'.$file->getName(),
+ 'ttl' => time() + 60 * 60 * 24,
));
}
public function executeThumbTransform(
PhabricatorFile $file,
$x,
$y) {
$image = $this->crudelyScaleTo($file, $x, $y);
return PhabricatorFile::newFromFileData(
$image,
array(
'name' => 'thumb-'.$file->getName(),
));
}
public function executeProfileTransform(
PhabricatorFile $file,
$x,
$min_y,
$max_y) {
$image = $this->crudelyCropTo($file, $x, $min_y, $max_y);
return PhabricatorFile::newFromFileData(
$image,
array(
'name' => 'profile-'.$file->getName(),
));
}
public function executePreviewTransform(
PhabricatorFile $file,
$size) {
$image = $this->generatePreview($file, $size);
return PhabricatorFile::newFromFileData(
$image,
array(
'name' => 'preview-'.$file->getName(),
));
}
public function executeConpherenceTransform(
PhabricatorFile $file,
$top,
$left,
$width,
$height) {
$image = $this->crasslyCropTo(
$file,
$top,
$left,
$width,
$height);
return PhabricatorFile::newFromFileData(
$image,
array(
'name' => 'conpherence-'.$file->getName(),
));
}
private function crudelyCropTo(PhabricatorFile $file, $x, $min_y, $max_y) {
$data = $file->loadFileData();
$img = imagecreatefromstring($data);
$sx = imagesx($img);
$sy = imagesy($img);
$scaled_y = ($x / $sx) * $sy;
if ($scaled_y > $max_y) {
// This image is very tall and thin.
$scaled_y = $max_y;
} else if ($scaled_y < $min_y) {
// This image is very short and wide.
$scaled_y = $min_y;
}
$cropped = $this->applyScaleWithImagemagick($file, $x, $scaled_y);
if ($cropped != null) {
return $cropped;
}
$img = $this->applyScaleTo(
$file,
$x,
$scaled_y);
return $this->saveImageDataInAnyFormat($img, $file->getMimeType());
}
private function crasslyCropTo(PhabricatorFile $file, $top, $left, $w, $h) {
$data = $file->loadFileData();
$src = imagecreatefromstring($data);
$dst = $this->getBlankDestinationFile($w, $h);
$scale = self::getScaleForCrop($file, $w, $h);
$orig_x = $left / $scale;
$orig_y = $top / $scale;
$orig_w = $w / $scale;
$orig_h = $h / $scale;
imagecopyresampled(
$dst,
$src,
0, 0,
$orig_x, $orig_y,
$w, $h,
$orig_w, $orig_h);
return $this->saveImageDataInAnyFormat($dst, $file->getMimeType());
}
/**
* Very crudely scale an image up or down to an exact size.
*/
private function crudelyScaleTo(PhabricatorFile $file, $dx, $dy) {
$scaled = $this->applyScaleWithImagemagick($file, $dx, $dy);
if ($scaled != null) {
return $scaled;
}
$dst = $this->applyScaleTo($file, $dx, $dy);
return $this->saveImageDataInAnyFormat($dst, $file->getMimeType());
}
private function getBlankDestinationFile($dx, $dy) {
$dst = imagecreatetruecolor($dx, $dy);
imagesavealpha($dst, true);
imagefill($dst, 0, 0, imagecolorallocatealpha($dst, 255, 255, 255, 127));
return $dst;
}
private function applyScaleTo(PhabricatorFile $file, $dx, $dy) {
$data = $file->loadFileData();
$src = imagecreatefromstring($data);
$x = imagesx($src);
$y = imagesy($src);
$scale = min(($dx / $x), ($dy / $y), 1);
$sdx = $scale * $x;
$sdy = $scale * $y;
$dst = $this->getBlankDestinationFile($dx, $dy);
imagesavealpha($dst, true);
imagefill($dst, 0, 0, imagecolorallocatealpha($dst, 255, 255, 255, 127));
imagecopyresampled(
$dst,
$src,
($dx - $sdx) / 2, ($dy - $sdy) / 2,
0, 0,
$sdx, $sdy,
$x, $y);
return $dst;
}
public static function getPreviewDimensions(PhabricatorFile $file, $size) {
$data = $file->loadFileData();
$src = imagecreatefromstring($data);
$x = imagesx($src);
$y = imagesy($src);
$scale = min($size / $x, $size / $y, 1);
$dx = max($size / 4, $scale * $x);
$dy = max($size / 4, $scale * $y);
$sdx = $scale * $x;
$sdy = $scale * $y;
return array(
'x' => $x,
'y' => $y,
'dx' => $dx,
'dy' => $dy,
'sdx' => $sdx,
'sdy' => $sdy
);
}
public static function getScaleForCrop(
PhabricatorFile $file,
$des_width,
$des_height) {
$metadata = $file->getMetadata();
$width = $metadata[PhabricatorFile::METADATA_IMAGE_WIDTH];
$height = $metadata[PhabricatorFile::METADATA_IMAGE_HEIGHT];
if ($height < $des_height) {
$scale = $height / $des_height;
} else if ($width < $des_width) {
$scale = $width / $des_width;
} else {
$scale_x = $des_width / $width;
$scale_y = $des_height / $height;
$scale = max($scale_x, $scale_y);
}
return $scale;
}
private function generatePreview(PhabricatorFile $file, $size) {
$data = $file->loadFileData();
$src = imagecreatefromstring($data);
$dimensions = self::getPreviewDimensions($file, $size);
$x = $dimensions['x'];
$y = $dimensions['y'];
$dx = $dimensions['dx'];
$dy = $dimensions['dy'];
$sdx = $dimensions['sdx'];
$sdy = $dimensions['sdy'];
$dst = $this->getBlankDestinationFile($dx, $dy);
imagecopyresampled(
$dst,
$src,
($dx - $sdx) / 2, ($dy - $sdy) / 2,
0, 0,
$sdx, $sdy,
$x, $y);
return $this->saveImageDataInAnyFormat($dst, $file->getMimeType());
}
private function applyMemeTo(
PhabricatorFile $file,
$upper_text,
$lower_text) {
$data = $file->loadFileData();
$img = imagecreatefromstring($data);
$phabricator_root = dirname(phutil_get_library_root('phabricator'));
$font_root = $phabricator_root.'/resources/font/';
$font_path = $font_root.'tuffy.ttf';
if (Filesystem::pathExists($font_root.'impact.ttf')) {
$font_path = $font_root.'impact.ttf';
}
$text_color = imagecolorallocate($img, 255, 255, 255);
$border_color = imagecolorallocatealpha($img, 0, 0, 0, 110);
$border_width = 4;
$font_max = 200;
$font_min = 5;
for ($i = $font_max; $i > $font_min; $i--) {
$fit = $this->doesTextBoundingBoxFitInImage(
$img,
$upper_text,
$i,
$font_path);
if ($fit['doesfit']) {
$x = ($fit['imgwidth'] - $fit['txtwidth']) / 2;
$y = $fit['txtheight'] + 10;
$this->makeImageWithTextBorder($img,
$i,
$x,
$y,
$text_color,
$border_color,
$border_width,
$font_path,
$upper_text);
break;
}
}
for ($i = $font_max; $i > $font_min; $i--) {
$fit = $this->doesTextBoundingBoxFitInImage($img,
$lower_text, $i, $font_path);
if ($fit['doesfit']) {
$x = ($fit['imgwidth'] - $fit['txtwidth']) / 2;
$y = $fit['imgheight'] - 10;
$this->makeImageWithTextBorder(
$img,
$i,
$x,
$y,
$text_color,
$border_color,
$border_width,
$font_path,
$lower_text);
break;
}
}
return $this->saveImageDataInAnyFormat($img, $file->getMimeType());
}
private function makeImageWithTextBorder($img, $font_size, $x, $y,
$color, $stroke_color, $bw, $font, $text) {
$angle = 0;
$bw = abs($bw);
for ($c1 = $x - $bw; $c1 <= $x + $bw; $c1++) {
for ($c2 = $y - $bw; $c2 <= $y + $bw; $c2++) {
if (!(($c1 == $x - $bw || $x + $bw) &&
$c2 == $y - $bw || $c2 == $y + $bw)) {
$bg = imagettftext($img, $font_size,
$angle, $c1, $c2, $stroke_color, $font, $text);
}
}
}
imagettftext($img, $font_size, $angle,
$x , $y, $color , $font, $text);
}
private function doesTextBoundingBoxFitInImage($img,
$text, $font_size, $font_path) {
// Default Angle = 0
$angle = 0;
$bbox = imagettfbbox($font_size, $angle, $font_path, $text);
$text_height = abs($bbox[3] - $bbox[5]);
$text_width = abs($bbox[0] - $bbox[2]);
return array(
"doesfit" => ($text_height * 1.05 <= imagesy($img) / 2
&& $text_width * 1.05 <= imagesx($img)),
"txtwidth" => $text_width,
"txtheight" => $text_height,
"imgwidth" => imagesx($img),
"imgheight" => imagesy($img),
);
}
private function saveImageDataInAnyFormat($data, $preferred_mime = '') {
switch ($preferred_mime) {
case 'image/gif': // Gif doesn't support true color
case 'image/png':
if (function_exists('imagepng')) {
ob_start();
imagepng($data);
return ob_get_clean();
}
break;
}
$img = null;
if (function_exists('imagejpeg')) {
ob_start();
imagejpeg($data);
$img = ob_get_clean();
} else if (function_exists('imagepng')) {
ob_start();
imagepng($data);
$img = ob_get_clean();
} else if (function_exists('imagegif')) {
ob_start();
imagegif($data);
$img = ob_get_clean();
} else {
throw new Exception("No image generation functions exist!");
}
return $img;
}
private function applyScaleWithImagemagick(PhabricatorFile $file, $dx, $dy) {
$img_type = $file->getMimeType();
$imagemagick = PhabricatorEnv::getEnvConfig('files.enable-imagemagick');
if ($img_type != 'image/gif' || $imagemagick == false) {
return null;
}
$data = $file->loadFileData();
$src = imagecreatefromstring($data);
$x = imagesx($src);
$y = imagesy($src);
$scale = min(($dx / $x), ($dy / $y), 1);
$sdx = $scale * $x;
$sdy = $scale * $y;
$input = new TempFile();
Filesystem::writeFile($input, $data);
$resized = new TempFile();
list($err) = exec_manual(
'convert %s -coalesce -resize %sX%s\! %s'
, $input, $sdx, $sdy, $resized);
if (!$err) {
$new_data = Filesystem::readFile($resized);
return $new_data;
} else {
return null;
}
}
}
diff --git a/src/applications/files/controller/PhabricatorFileInfoController.php b/src/applications/files/controller/PhabricatorFileInfoController.php
index faeac9c154..fe031222b7 100644
--- a/src/applications/files/controller/PhabricatorFileInfoController.php
+++ b/src/applications/files/controller/PhabricatorFileInfoController.php
@@ -1,165 +1,173 @@
<?php
final class PhabricatorFileInfoController extends PhabricatorFileController {
private $phid;
public function willProcessRequest(array $data) {
$this->phid = $data['phid'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$file = id(new PhabricatorFileQuery())
->setViewer($user)
->withPHIDs(array($this->phid))
->executeOne();
if (!$file) {
return new Aphront404Response();
}
$this->loadHandles(array($file->getAuthorPHID()));
$phid = $file->getPHID();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addCrumb(
id(new PhabricatorCrumbView())
->setName('F'.$file->getID())
->setHref($this->getApplicationURI("/info/{$phid}/")));
$header = id(new PhabricatorHeaderView())
->setObjectName('F'.$file->getID())
->setHeader($file->getName());
+ $ttl = $file->getTTL();
+ if ($ttl !== null) {
+ $ttl_tag = id(new PhabricatorTagView())
+ ->setType(PhabricatorTagView::TYPE_OBJECT)
+ ->setName(pht("Temporary"));
+ $header->addTag($ttl_tag);
+ }
+
$actions = $this->buildActionView($file);
$properties = $this->buildPropertyView($file);
return $this->buildApplicationPage(
array(
$crumbs,
$header,
$actions,
$properties,
),
array(
'title' => $file->getName(),
'device' => true,
));
}
private function buildActionView(PhabricatorFile $file) {
$request = $this->getRequest();
$user = $request->getUser();
$id = $file->getID();
$view = id(new PhabricatorActionListView())
->setUser($user)
->setObject($file);
if ($file->isViewableInBrowser()) {
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('View File'))
->setIcon('preview')
->setHref($file->getViewURI()));
} else {
$view->addAction(
id(new PhabricatorActionView())
->setUser($user)
->setRenderAsForm(true)
->setDownload(true)
->setName(pht('Download File'))
->setIcon('download')
->setHref($file->getViewURI()));
}
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Delete File'))
->setIcon('delete')
->setHref($this->getApplicationURI("/delete/{$id}/"))
->setWorkflow(true));
return $view;
}
private function buildPropertyView(PhabricatorFile $file) {
$request = $this->getRequest();
$user = $request->getUser();
$view = id(new PhabricatorPropertyListView());
if ($file->getAuthorPHID()) {
$view->addProperty(
pht('Author'),
$this->getHandle($file->getAuthorPHID())->renderLink());
}
$view->addProperty(
pht('Created'),
phabricator_datetime($file->getDateCreated(), $user));
$view->addProperty(
pht('Size'),
phabricator_format_bytes($file->getByteSize()));
$view->addSectionHeader(pht('Technical Details'));
$view->addProperty(
pht('Mime Type'),
$file->getMimeType());
$view->addProperty(
pht('Engine'),
$file->getStorageEngine());
$view->addProperty(
pht('Format'),
$file->getStorageFormat());
$view->addProperty(
pht('Handle'),
$file->getStorageHandle());
$metadata = $file->getMetadata();
if (!empty($metadata)) {
$view->addSectionHeader(pht('Metadata'));
foreach ($metadata as $key => $value) {
$view->addProperty(
PhabricatorFile::getMetadataName($key),
$value);
}
}
if ($file->isViewableImage()) {
// TODO: Clean this up after Pholio (dark backgrounds, standardization,
// etc.)
$image = phutil_tag(
'img',
array(
'src' => $file->getViewURI(),
'class' => 'phabricator-property-list-image',
));
$linked_image = phutil_tag(
'a',
array(
'href' => $file->getViewURI(),
),
$image);
$view->addTextContent($linked_image);
}
return $view;
}
}
diff --git a/src/applications/files/controller/PhabricatorFileListController.php b/src/applications/files/controller/PhabricatorFileListController.php
index 5e31eb3554..a2dd69e08d 100644
--- a/src/applications/files/controller/PhabricatorFileListController.php
+++ b/src/applications/files/controller/PhabricatorFileListController.php
@@ -1,123 +1,126 @@
<?php
final class PhabricatorFileListController extends PhabricatorFileController {
private $filter;
private function setFilter($filter) {
$this->filter = $filter;
return $this;
}
private function getFilter() {
return $this->filter;
}
public function willProcessRequest(array $data) {
$this->setFilter(idx($data, 'filter', 'my'));
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$pager = id(new AphrontCursorPagerView())
->readFromRequest($request);
$query = id(new PhabricatorFileQuery())
->setViewer($user);
switch ($this->getFilter()) {
case 'my':
$query->withAuthorPHIDs(array($user->getPHID()));
$header = pht('Files You Uploaded');
break;
case 'all':
default:
$header = pht('All Files');
break;
}
$files = $query->executeWithCursorPager($pager);
$this->loadHandles(mpull($files, 'getAuthorPHID'));
$highlighted = $request->getStrList('h');
$file_list = $this->buildFileList($files, $highlighted);
$side_nav = $this->buildSideNavView();
$side_nav->selectFilter($this->getFilter());
$header_view = id(new PhabricatorHeaderView())
->setHeader($header);
$side_nav->appendChild(
array(
$header_view,
$file_list,
$pager,
new PhabricatorGlobalUploadTargetView(),
));
$side_nav->setCrumbs(
$this
->buildApplicationCrumbs()
->addCrumb(
id(new PhabricatorCrumbView())
->setName($header)
->setHref($request->getRequestURI())));
return $this->buildApplicationPage(
$side_nav,
array(
'title' => 'Files',
'device' => true,
));
}
private function buildFileList(array $files, array $highlighted_ids) {
assert_instances_of($files, 'PhabricatorFile');
$request = $this->getRequest();
$user = $request->getUser();
$highlighted_ids = array_fill_keys($highlighted_ids, true);
$list_view = id(new PhabricatorObjectItemListView())
->setUser($user);
foreach ($files as $file) {
$id = $file->getID();
$phid = $file->getPHID();
$name = $file->getName();
-
$file_name = "F{$id} {$name}";
$file_uri = $this->getApplicationURI("/info/{$phid}/");
$date_created = phabricator_date($file->getDateCreated(), $user);
-
$author_phid = $file->getAuthorPHID();
if ($author_phid) {
$author_link = $this->getHandle($author_phid)->renderLink();
$uploaded = pht('Uploaded by %s on %s', $author_link, $date_created);
} else {
$uploaded = pht('Uploaded on %s', $date_created);
}
$item = id(new PhabricatorObjectItemView())
->setObject($file)
->setHeader($file_name)
->setHref($file_uri)
->addAttribute($uploaded)
->addIcon('none', phabricator_format_bytes($file->getByteSize()));
+ $ttl = $file->getTTL();
+ if ($ttl !== null) {
+ $item->addIcon('blame', pht('Temporary'));
+ }
+
if (isset($highlighted_ids[$id])) {
$item->setEffect('highlighted');
}
$list_view->addItem($item);
}
return $list_view;
}
}
diff --git a/src/applications/files/storage/PhabricatorFile.php b/src/applications/files/storage/PhabricatorFile.php
index d74f995b42..fee670dd66 100644
--- a/src/applications/files/storage/PhabricatorFile.php
+++ b/src/applications/files/storage/PhabricatorFile.php
@@ -1,633 +1,647 @@
<?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->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) {
$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();
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());
}
-
- $ret = parent::delete();
-
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 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 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 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 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/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php b/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php
index f794585080..d2b2112c0f 100644
--- a/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php
+++ b/src/applications/files/storage/__tests__/PhabricatorFileTestCase.php
@@ -1,129 +1,148 @@
<?php
final class PhabricatorFileTestCase extends PhabricatorTestCase {
public function getPhabricatorTestCaseConfiguration() {
return array(
self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES => true,
);
}
public function testFileStorageReadWrite() {
$engine = new PhabricatorTestStorageEngine();
$data = Filesystem::readRandomCharacters(64);
$params = array(
'name' => 'test.dat',
'storageEngines' => array(
$engine,
),
);
$file = PhabricatorFile::newFromFileData($data, $params);
// Test that the storage engine worked, and was the target of the write. We
// don't actually care what the data is (future changes may compress or
// encrypt it), just that it exists in the test storage engine.
$engine->readFile($file->getStorageHandle());
// Now test that we get the same data back out.
$this->assertEqual($data, $file->loadFileData());
}
public function testFileStorageUploadDifferentFiles() {
$engine = new PhabricatorTestStorageEngine();
$data = Filesystem::readRandomCharacters(64);
$other_data = Filesystem::readRandomCharacters(64);
$params = array(
'name' => 'test.dat',
'storageEngines' => array(
$engine,
),
);
$first_file = PhabricatorFile::newFromFileData($data, $params);
$second_file = PhabricatorFile::newFromFileData($other_data, $params);
// Test that the the second file uses different storage handle from
// the first file.
$first_handle = $first_file->getStorageHandle();
$second_handle = $second_file->getStorageHandle();
$this->assertEqual(true, ($first_handle != $second_handle));
}
public function testFileStorageUploadSameFile() {
$engine = new PhabricatorTestStorageEngine();
$data = Filesystem::readRandomCharacters(64);
$params = array(
'name' => 'test.dat',
'storageEngines' => array(
$engine,
),
);
$first_file = PhabricatorFile::newFromFileData($data, $params);
$second_file = PhabricatorFile::newFromFileData($data, $params);
// Test that the the second file uses the same storage handle as
// the first file.
$handle = $first_file->getStorageHandle();
$second_handle = $second_file->getStorageHandle();
$this->assertEqual($handle, $second_handle);
}
public function testFileStorageDelete() {
$engine = new PhabricatorTestStorageEngine();
$data = Filesystem::readRandomCharacters(64);
$params = array(
'name' => 'test.dat',
'storageEngines' => array(
$engine,
),
);
$file = PhabricatorFile::newFromFileData($data, $params);
$handle = $file->getStorageHandle();
$file->delete();
$caught = null;
try {
$engine->readFile($handle);
} catch (Exception $ex) {
$caught = $ex;
}
$this->assertEqual(true, $caught instanceof Exception);
}
public function testFileStorageDeleteSharedHandle() {
$engine = new PhabricatorTestStorageEngine();
$data = Filesystem::readRandomCharacters(64);
$params = array(
'name' => 'test.dat',
'storageEngines' => array(
$engine,
),
);
$first_file = PhabricatorFile::newFromFileData($data, $params);
$second_file = PhabricatorFile::newFromFileData($data, $params);
$first_file->delete();
$this->assertEqual($data, $second_file->loadFileData());
}
+ public function testReadWriteTtlFiles() {
+ $engine = new PhabricatorTestStorageEngine();
+
+ $data = Filesystem::readRandomCharacters(64);
+
+ $ttl = (time() + 60 * 60 * 24);
+
+ $params = array(
+ 'name' => 'test.dat',
+ 'ttl' => ($ttl),
+ 'storageEngines' => array(
+ $engine,
+ ),
+ );
+
+ $file = PhabricatorFile::newFromFileData($data, $params);
+ $this->assertEqual($ttl, $file->getTTL());
+ }
+
}
diff --git a/src/infrastructure/daemon/PhabricatorGarbageCollectorDaemon.php b/src/infrastructure/daemon/PhabricatorGarbageCollectorDaemon.php
index f4f678b14c..dc736e8982 100644
--- a/src/infrastructure/daemon/PhabricatorGarbageCollectorDaemon.php
+++ b/src/infrastructure/daemon/PhabricatorGarbageCollectorDaemon.php
@@ -1,210 +1,222 @@
<?php
/**
* Collects old logs and caches to reduce the amount of data stored in the
* database.
*
* @group daemon
*/
final class PhabricatorGarbageCollectorDaemon extends PhabricatorDaemon {
public function run() {
do {
$n_herald = $this->collectHeraldTranscripts();
$n_daemon = $this->collectDaemonLogs();
$n_parse = $this->collectParseCaches();
$n_markup = $this->collectMarkupCaches();
$n_tasks = $this->collectArchivedTasks();
$n_cache_ttl = $this->collectGeneralCacheTTL();
$n_cache = $this->collectGeneralCaches();
+ $n_files = $this->collectExpiredFiles();
$collected = array(
'Herald Transcript' => $n_herald,
'Daemon Log' => $n_daemon,
'Differential Parse Cache' => $n_parse,
'Markup Cache' => $n_markup,
'Archived Tasks' => $n_tasks,
'General Cache TTL' => $n_cache_ttl,
'General Cache Entries' => $n_cache,
+ 'Temporary Files' => $n_files,
);
$collected = array_filter($collected);
foreach ($collected as $thing => $count) {
$count = number_format($count);
$this->log("Garbage collected {$count} '{$thing}' objects.");
}
$total = array_sum($collected);
if ($total < 100) {
// We didn't max out any of the GCs so we're basically caught up. Ease
// off the GC loop so we don't keep doing table scans just to delete
// a handful of rows; wake up in a few hours.
$this->sleep(4 * (60 * 60));
} else {
$this->stillWorking();
}
} while (true);
}
private function collectHeraldTranscripts() {
$ttl = PhabricatorEnv::getEnvConfig('gcdaemon.ttl.herald-transcripts');
if ($ttl <= 0) {
return 0;
}
$table = new HeraldTranscript();
$conn_w = $table->establishConnection('w');
queryfx(
$conn_w,
'UPDATE %T SET
objectTranscript = "",
ruleTranscripts = "",
conditionTranscripts = "",
applyTranscripts = "",
garbageCollected = 1
WHERE garbageCollected = 0 AND `time` < %d
LIMIT 100',
$table->getTableName(),
time() - $ttl);
return $conn_w->getAffectedRows();
}
private function collectDaemonLogs() {
$ttl = PhabricatorEnv::getEnvConfig('gcdaemon.ttl.daemon-logs');
if ($ttl <= 0) {
return 0;
}
$table = new PhabricatorDaemonLogEvent();
$conn_w = $table->establishConnection('w');
queryfx(
$conn_w,
'DELETE FROM %T WHERE epoch < %d LIMIT 100',
$table->getTableName(),
time() - $ttl);
return $conn_w->getAffectedRows();
}
private function collectParseCaches() {
$key = 'gcdaemon.ttl.differential-parse-cache';
$ttl = PhabricatorEnv::getEnvConfig($key);
if ($ttl <= 0) {
return 0;
}
$table = new DifferentialChangeset();
$conn_w = $table->establishConnection('w');
queryfx(
$conn_w,
'DELETE FROM %T WHERE dateCreated < %d LIMIT 100',
DifferentialChangeset::TABLE_CACHE,
time() - $ttl);
return $conn_w->getAffectedRows();
}
private function collectMarkupCaches() {
$key = 'gcdaemon.ttl.markup-cache';
$ttl = PhabricatorEnv::getEnvConfig($key);
if ($ttl <= 0) {
return 0;
}
$table = new PhabricatorMarkupCache();
$conn_w = $table->establishConnection('w');
queryfx(
$conn_w,
'DELETE FROM %T WHERE dateCreated < %d LIMIT 100',
$table->getTableName(),
time() - $ttl);
return $conn_w->getAffectedRows();
}
private function collectArchivedTasks() {
$key = 'gcdaemon.ttl.task-archive';
$ttl = PhabricatorEnv::getEnvConfig($key);
if ($ttl <= 0) {
return 0;
}
$table = new PhabricatorWorkerArchiveTask();
$data_table = new PhabricatorWorkerTaskData();
$conn_w = $table->establishConnection('w');
$rows = queryfx_all(
$conn_w,
'SELECT id, dataID FROM %T WHERE dateCreated < %d LIMIT 100',
$table->getTableName(),
time() - $ttl);
if (!$rows) {
return 0;
}
$data_ids = array_filter(ipull($rows, 'dataID'));
$task_ids = ipull($rows, 'id');
$table->openTransaction();
if ($data_ids) {
queryfx(
$conn_w,
'DELETE FROM %T WHERE id IN (%Ld)',
$data_table->getTableName(),
$data_ids);
}
queryfx(
$conn_w,
'DELETE FROM %T WHERE id IN (%Ld)',
$table->getTableName(),
$task_ids);
$table->saveTransaction();
return count($task_ids);
}
private function collectGeneralCacheTTL() {
$cache = new PhabricatorKeyValueDatabaseCache();
$conn_w = $cache->establishConnection('w');
queryfx(
$conn_w,
'DELETE FROM %T WHERE cacheExpires < %d
ORDER BY cacheExpires ASC LIMIT 100',
$cache->getTableName(),
time());
return $conn_w->getAffectedRows();
}
private function collectGeneralCaches() {
$key = 'gcdaemon.ttl.general-cache';
$ttl = PhabricatorEnv::getEnvConfig($key);
if ($ttl <= 0) {
return 0;
}
$cache = new PhabricatorKeyValueDatabaseCache();
$conn_w = $cache->establishConnection('w');
queryfx(
$conn_w,
'DELETE FROM %T WHERE cacheCreated < %d
ORDER BY cacheCreated ASC LIMIT 100',
$cache->getTableName(),
time() - $ttl);
return $conn_w->getAffectedRows();
}
+ private function collectExpiredFiles() {
+ $files = id(new PhabricatorFile())->loadAllWhere('ttl < %d LIMIT 100',
+ time());
+
+ foreach ($files as $file) {
+ $file->delete();
+ }
+
+ return count($files);
+ }
}
diff --git a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
index dd677403b1..f678f7c4cb 100644
--- a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
+++ b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
@@ -1,1143 +1,1147 @@
<?php
final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
public function getNamespace() {
return 'phabricator';
}
private function getPatchPath($file) {
$root = dirname(phutil_get_library_root('phabricator'));
$path = $root.'/resources/sql/patches/'.$file;
// Make sure it exists.
Filesystem::readFile($path);
return $path;
}
public function getPatches() {
return array(
'db.audit' => array(
'type' => 'db',
'name' => 'audit',
'after' => array( /* First Patch */ ),
),
'db.calendar' => array(
'type' => 'db',
'name' => 'calendar',
),
'db.chatlog' => array(
'type' => 'db',
'name' => 'chatlog',
),
'db.conduit' => array(
'type' => 'db',
'name' => 'conduit',
),
'db.countdown' => array(
'type' => 'db',
'name' => 'countdown',
),
'db.daemon' => array(
'type' => 'db',
'name' => 'daemon',
),
'db.differential' => array(
'type' => 'db',
'name' => 'differential',
),
'db.draft' => array(
'type' => 'db',
'name' => 'draft',
),
'db.drydock' => array(
'type' => 'db',
'name' => 'drydock',
),
'db.feed' => array(
'type' => 'db',
'name' => 'feed',
),
'db.file' => array(
'type' => 'db',
'name' => 'file',
),
'db.flag' => array(
'type' => 'db',
'name' => 'flag',
),
'db.harbormaster' => array(
'type' => 'db',
'name' => 'harbormaster',
),
'db.herald' => array(
'type' => 'db',
'name' => 'herald',
),
'db.maniphest' => array(
'type' => 'db',
'name' => 'maniphest',
),
'db.meta_data' => array(
'type' => 'db',
'name' => 'meta_data',
),
'db.metamta' => array(
'type' => 'db',
'name' => 'metamta',
),
'db.oauth_server' => array(
'type' => 'db',
'name' => 'oauth_server',
),
'db.owners' => array(
'type' => 'db',
'name' => 'owners',
),
'db.pastebin' => array(
'type' => 'db',
'name' => 'pastebin',
),
'db.phame' => array(
'type' => 'db',
'name' => 'phame',
),
'db.phriction' => array(
'type' => 'db',
'name' => 'phriction',
),
'db.project' => array(
'type' => 'db',
'name' => 'project',
),
'db.repository' => array(
'type' => 'db',
'name' => 'repository',
),
'db.search' => array(
'type' => 'db',
'name' => 'search',
),
'db.slowvote' => array(
'type' => 'db',
'name' => 'slowvote',
),
'db.timeline' => array(
'type' => 'db',
'name' => 'timeline',
),
'db.user' => array(
'type' => 'db',
'name' => 'user',
),
'db.worker' => array(
'type' => 'db',
'name' => 'worker',
),
'db.xhpastview' => array(
'type' => 'db',
'name' => 'xhpastview',
),
'db.cache' => array(
'type' => 'db',
'name' => 'cache',
),
'db.fact' => array(
'type' => 'db',
'name' => 'fact',
),
'db.ponder' => array(
'type' => 'db',
'name' => 'ponder',
),
'db.xhprof' => array(
'type' => 'db',
'name' => 'xhprof',
),
'db.pholio' => array(
'type' => 'db',
'name' => 'pholio',
),
'db.conpherence' => array(
'type' => 'db',
'name' => 'conpherence',
),
'db.config' => array(
'type' => 'db',
'name' => 'config',
),
'db.token' => array(
'type' => 'db',
'name' => 'token',
),
'0000.legacy.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('0000.legacy.sql'),
'legacy' => 0,
),
'000.project.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('000.project.sql'),
'legacy' => 0,
),
'001.maniphest_projects.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('001.maniphest_projects.sql'),
'legacy' => 1,
),
'002.oauth.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('002.oauth.sql'),
'legacy' => 2,
),
'003.more_oauth.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('003.more_oauth.sql'),
'legacy' => 3,
),
'004.daemonrepos.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('004.daemonrepos.sql'),
'legacy' => 4,
),
'005.workers.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('005.workers.sql'),
'legacy' => 5,
),
'006.repository.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('006.repository.sql'),
'legacy' => 6,
),
'007.daemonlog.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('007.daemonlog.sql'),
'legacy' => 7,
),
'008.repoopt.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('008.repoopt.sql'),
'legacy' => 8,
),
'009.repo_summary.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('009.repo_summary.sql'),
'legacy' => 9,
),
'010.herald.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('010.herald.sql'),
'legacy' => 10,
),
'011.badcommit.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('011.badcommit.sql'),
'legacy' => 11,
),
'012.dropphidtype.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('012.dropphidtype.sql'),
'legacy' => 12,
),
'013.commitdetail.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('013.commitdetail.sql'),
'legacy' => 13,
),
'014.shortcuts.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('014.shortcuts.sql'),
'legacy' => 14,
),
'015.preferences.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('015.preferences.sql'),
'legacy' => 15,
),
'016.userrealnameindex.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('016.userrealnameindex.sql'),
'legacy' => 16,
),
'017.sessionkeys.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('017.sessionkeys.sql'),
'legacy' => 17,
),
'018.owners.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('018.owners.sql'),
'legacy' => 18,
),
'019.arcprojects.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('019.arcprojects.sql'),
'legacy' => 19,
),
'020.pathcapital.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('020.pathcapital.sql'),
'legacy' => 20,
),
'021.xhpastview.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('021.xhpastview.sql'),
'legacy' => 21,
),
'022.differentialcommit.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('022.differentialcommit.sql'),
'legacy' => 22,
),
'023.dxkeys.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('023.dxkeys.sql'),
'legacy' => 23,
),
'024.mlistkeys.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('024.mlistkeys.sql'),
'legacy' => 24,
),
'025.commentopt.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('025.commentopt.sql'),
'legacy' => 25,
),
'026.diffpropkey.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('026.diffpropkey.sql'),
'legacy' => 26,
),
'027.metamtakeys.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('027.metamtakeys.sql'),
'legacy' => 27,
),
'028.systemagent.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('028.systemagent.sql'),
'legacy' => 28,
),
'029.cursors.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('029.cursors.sql'),
'legacy' => 29,
),
'030.imagemacro.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('030.imagemacro.sql'),
'legacy' => 30,
),
'031.workerrace.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('031.workerrace.sql'),
'legacy' => 31,
),
'032.viewtime.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('032.viewtime.sql'),
'legacy' => 32,
),
'033.privtest.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('033.privtest.sql'),
'legacy' => 33,
),
'034.savedheader.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('034.savedheader.sql'),
'legacy' => 34,
),
'035.proxyimage.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('035.proxyimage.sql'),
'legacy' => 35,
),
'036.mailkey.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('036.mailkey.sql'),
'legacy' => 36,
),
'037.setuptest.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('037.setuptest.sql'),
'legacy' => 37,
),
'038.admin.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('038.admin.sql'),
'legacy' => 38,
),
'039.userlog.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('039.userlog.sql'),
'legacy' => 39,
),
'040.transform.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('040.transform.sql'),
'legacy' => 40,
),
'041.heraldrepetition.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('041.heraldrepetition.sql'),
'legacy' => 41,
),
'042.commentmetadata.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('042.commentmetadata.sql'),
'legacy' => 42,
),
'043.pastebin.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('043.pastebin.sql'),
'legacy' => 43,
),
'044.countdown.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('044.countdown.sql'),
'legacy' => 44,
),
'045.timezone.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('045.timezone.sql'),
'legacy' => 45,
),
'046.conduittoken.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('046.conduittoken.sql'),
'legacy' => 46,
),
'047.projectstatus.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('047.projectstatus.sql'),
'legacy' => 47,
),
'048.relationshipkeys.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('048.relationshipkeys.sql'),
'legacy' => 48,
),
'049.projectowner.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('049.projectowner.sql'),
'legacy' => 49,
),
'050.taskdenormal.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('050.taskdenormal.sql'),
'legacy' => 50,
),
'051.projectfilter.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('051.projectfilter.sql'),
'legacy' => 51,
),
'052.pastelanguage.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('052.pastelanguage.sql'),
'legacy' => 52,
),
'053.feed.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('053.feed.sql'),
'legacy' => 53,
),
'054.subscribers.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('054.subscribers.sql'),
'legacy' => 54,
),
'055.add_author_to_files.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('055.add_author_to_files.sql'),
'legacy' => 55,
),
'056.slowvote.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('056.slowvote.sql'),
'legacy' => 56,
),
'057.parsecache.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('057.parsecache.sql'),
'legacy' => 57,
),
'058.missingkeys.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('058.missingkeys.sql'),
'legacy' => 58,
),
'059.engines.php' => array(
'type' => 'php',
'name' => $this->getPatchPath('059.engines.php'),
'legacy' => 59,
),
'060.phriction.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('060.phriction.sql'),
'legacy' => 60,
),
'061.phrictioncontent.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('061.phrictioncontent.sql'),
'legacy' => 61,
),
'062.phrictionmenu.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('062.phrictionmenu.sql'),
'legacy' => 62,
),
'063.pasteforks.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('063.pasteforks.sql'),
'legacy' => 63,
),
'064.subprojects.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('064.subprojects.sql'),
'legacy' => 64,
),
'065.sshkeys.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('065.sshkeys.sql'),
'legacy' => 65,
),
'066.phrictioncontent.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('066.phrictioncontent.sql'),
'legacy' => 66,
),
'067.preferences.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('067.preferences.sql'),
'legacy' => 67,
),
'068.maniphestauxiliarystorage.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('068.maniphestauxiliarystorage.sql'),
'legacy' => 68,
),
'069.heraldxscript.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('069.heraldxscript.sql'),
'legacy' => 69,
),
'070.differentialaux.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('070.differentialaux.sql'),
'legacy' => 70,
),
'071.contentsource.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('071.contentsource.sql'),
'legacy' => 71,
),
'072.blamerevert.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('072.blamerevert.sql'),
'legacy' => 72,
),
'073.reposymbols.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('073.reposymbols.sql'),
'legacy' => 73,
),
'074.affectedpath.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('074.affectedpath.sql'),
'legacy' => 74,
),
'075.revisionhash.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('075.revisionhash.sql'),
'legacy' => 75,
),
'076.indexedlanguages.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('076.indexedlanguages.sql'),
'legacy' => 76,
),
'077.originalemail.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('077.originalemail.sql'),
'legacy' => 77,
),
'078.nametoken.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('078.nametoken.sql'),
'legacy' => 78,
),
'079.nametokenindex.php' => array(
'type' => 'php',
'name' => $this->getPatchPath('079.nametokenindex.php'),
'legacy' => 79,
),
'080.filekeys.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('080.filekeys.sql'),
'legacy' => 80,
),
'081.filekeys.php' => array(
'type' => 'php',
'name' => $this->getPatchPath('081.filekeys.php'),
'legacy' => 81,
),
'082.xactionkey.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('082.xactionkey.sql'),
'legacy' => 82,
),
'083.dxviewtime.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('083.dxviewtime.sql'),
'legacy' => 83,
),
'084.pasteauthorkey.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('084.pasteauthorkey.sql'),
'legacy' => 84,
),
'085.packagecommitrelationship.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('085.packagecommitrelationship.sql'),
'legacy' => 85,
),
'086.formeraffil.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('086.formeraffil.sql'),
'legacy' => 86,
),
'087.phrictiondelete.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('087.phrictiondelete.sql'),
'legacy' => 87,
),
'088.audit.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('088.audit.sql'),
'legacy' => 88,
),
'089.projectwiki.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('089.projectwiki.sql'),
'legacy' => 89,
),
'090.forceuniqueprojectnames.php' => array(
'type' => 'php',
'name' => $this->getPatchPath('090.forceuniqueprojectnames.php'),
'legacy' => 90,
),
'091.uniqueslugkey.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('091.uniqueslugkey.sql'),
'legacy' => 91,
),
'092.dropgithubnotification.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('092.dropgithubnotification.sql'),
'legacy' => 92,
),
'093.gitremotes.php' => array(
'type' => 'php',
'name' => $this->getPatchPath('093.gitremotes.php'),
'legacy' => 93,
),
'094.phrictioncolumn.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('094.phrictioncolumn.sql'),
'legacy' => 94,
),
'095.directory.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('095.directory.sql'),
'legacy' => 95,
),
'096.filename.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('096.filename.sql'),
'legacy' => 96,
),
'097.heraldruletypes.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('097.heraldruletypes.sql'),
'legacy' => 97,
),
'098.heraldruletypemigration.php' => array(
'type' => 'php',
'name' => $this->getPatchPath('098.heraldruletypemigration.php'),
'legacy' => 98,
),
'099.drydock.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('099.drydock.sql'),
'legacy' => 99,
),
'100.projectxaction.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('100.projectxaction.sql'),
'legacy' => 100,
),
'101.heraldruleapplied.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('101.heraldruleapplied.sql'),
'legacy' => 101,
),
'102.heraldcleanup.php' => array(
'type' => 'php',
'name' => $this->getPatchPath('102.heraldcleanup.php'),
'legacy' => 102,
),
'103.heraldedithistory.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('103.heraldedithistory.sql'),
'legacy' => 103,
),
'104.searchkey.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('104.searchkey.sql'),
'legacy' => 104,
),
'105.mimetype.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('105.mimetype.sql'),
'legacy' => 105,
),
'106.chatlog.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('106.chatlog.sql'),
'legacy' => 106,
),
'107.oauthserver.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('107.oauthserver.sql'),
'legacy' => 107,
),
'108.oauthscope.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('108.oauthscope.sql'),
'legacy' => 108,
),
'109.oauthclientphidkey.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('109.oauthclientphidkey.sql'),
'legacy' => 109,
),
'110.commitaudit.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('110.commitaudit.sql'),
'legacy' => 110,
),
'111.commitauditmigration.php' => array(
'type' => 'php',
'name' => $this->getPatchPath('111.commitauditmigration.php'),
'legacy' => 111,
),
'112.oauthaccesscoderedirecturi.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('112.oauthaccesscoderedirecturi.sql'),
'legacy' => 112,
),
'113.lastreviewer.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('113.lastreviewer.sql'),
'legacy' => 113,
),
'114.auditrequest.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('114.auditrequest.sql'),
'legacy' => 114,
),
'115.prepareutf8.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('115.prepareutf8.sql'),
'legacy' => 115,
),
'116.utf8-backup-first-expect-wait.sql' => array(
'type' => 'sql',
'name' =>
$this->getPatchPath('116.utf8-backup-first-expect-wait.sql'),
'legacy' => 116,
),
'117.repositorydescription.php' => array(
'type' => 'php',
'name' => $this->getPatchPath('117.repositorydescription.php'),
'legacy' => 117,
),
'118.auditinline.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('118.auditinline.sql'),
'legacy' => 118,
),
'119.filehash.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('119.filehash.sql'),
'legacy' => 119,
),
'120.noop.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('120.noop.sql'),
'legacy' => 120,
),
'121.drydocklog.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('121.drydocklog.sql'),
'legacy' => 121,
),
'122.flag.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('122.flag.sql'),
'legacy' => 122,
),
'123.heraldrulelog.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('123.heraldrulelog.sql'),
'legacy' => 123,
),
'124.subpriority.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('124.subpriority.sql'),
'legacy' => 124,
),
'125.ipv6.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('125.ipv6.sql'),
'legacy' => 125,
),
'126.edges.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('126.edges.sql'),
'legacy' => 126,
),
'127.userkeybody.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('127.userkeybody.sql'),
'legacy' => 127,
),
'128.phabricatorcom.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('128.phabricatorcom.sql'),
'legacy' => 128,
),
'129.savedquery.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('129.savedquery.sql'),
'legacy' => 129,
),
'130.denormalrevisionquery.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('130.denormalrevisionquery.sql'),
'legacy' => 130,
),
'131.migraterevisionquery.php' => array(
'type' => 'php',
'name' => $this->getPatchPath('131.migraterevisionquery.php'),
'legacy' => 131,
),
'132.phame.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('132.phame.sql'),
'legacy' => 132,
),
'133.imagemacro.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('133.imagemacro.sql'),
'legacy' => 133,
),
'134.emptysearch.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('134.emptysearch.sql'),
'legacy' => 134,
),
'135.datecommitted.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('135.datecommitted.sql'),
'legacy' => 135,
),
'136.sex.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('136.sex.sql'),
'legacy' => 136,
),
'137.auditmetadata.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('137.auditmetadata.sql'),
'legacy' => 137,
),
'138.notification.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('138.notification.sql'),
),
'holidays.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('holidays.sql'),
),
'userstatus.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('userstatus.sql'),
),
'emailtable.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('emailtable.sql'),
),
'emailtableport.sql' => array(
'type' => 'php',
'name' => $this->getPatchPath('emailtableport.php'),
),
'emailtableremove.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('emailtableremove.sql'),
),
'phiddrop.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('phiddrop.sql'),
),
'testdatabase.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('testdatabase.sql'),
),
'ldapinfo.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('ldapinfo.sql'),
),
'threadtopic.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('threadtopic.sql'),
),
'usertranslation.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('usertranslation.sql'),
),
'differentialbookmarks.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('differentialbookmarks.sql'),
),
'harbormasterobject.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('harbormasterobject.sql'),
),
'markupcache.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('markupcache.sql'),
),
'maniphestxcache.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('maniphestxcache.sql'),
),
'migrate-maniphest-dependencies.php' => array(
'type' => 'php',
'name' => $this->getPatchPath('migrate-maniphest-dependencies.php'),
),
'migrate-differential-dependencies.php' => array(
'type' => 'php',
'name' => $this->getPatchPath(
'migrate-differential-dependencies.php'),
),
'phameblog.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('phameblog.sql'),
),
'migrate-maniphest-revisions.php' => array(
'type' => 'php',
'name' => $this->getPatchPath('migrate-maniphest-revisions.php'),
),
'daemonstatus.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('daemonstatus.sql'),
),
'symbolcontexts.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('symbolcontexts.sql'),
),
'migrate-project-edges.php' => array(
'type' => 'php',
'name' => $this->getPatchPath('migrate-project-edges.php'),
),
'fact-raw.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('fact-raw.sql'),
),
'ponder.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('ponder.sql')
),
'policy-project.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('policy-project.sql'),
),
'daemonstatuskey.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('daemonstatuskey.sql'),
),
'edgetype.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('edgetype.sql'),
),
'ponder-comments.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('ponder-comments.sql'),
),
'pastepolicy.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('pastepolicy.sql'),
),
'xhprof.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('xhprof.sql'),
),
'draft-metadata.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('draft-metadata.sql'),
),
'phamedomain.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('phamedomain.sql'),
),
'ponder-mailkey.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('ponder-mailkey.sql'),
),
'ponder-mailkey-populate.php' => array(
'type' => 'php',
'name' => $this->getPatchPath('ponder-mailkey-populate.php'),
),
'phamepolicy.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('phamepolicy.sql'),
),
'phameoneblog.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('phameoneblog.sql'),
),
'statustxt.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('statustxt.sql'),
),
'daemontaskarchive.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('daemontaskarchive.sql'),
),
'drydocktaskid.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('drydocktaskid.sql'),
),
'drydockresoucetype.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('drydockresourcetype.sql'),
),
'liskcounters.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('liskcounters.sql'),
),
'liskcounters.php' => array(
'type' => 'php',
'name' => $this->getPatchPath('liskcounters.php'),
),
'dropfileproxyimage.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('dropfileproxyimage.sql'),
),
'repository-lint.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('repository-lint.sql'),
),
'liskcounters-task.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('liskcounters-task.sql'),
),
'pholio.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('pholio.sql'),
),
'owners-exclude.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('owners-exclude.sql'),
),
'20121209.pholioxactions.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('20121209.pholioxactions.sql'),
),
'20121209.xmacroadd.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('20121209.xmacroadd.sql'),
),
'20121209.xmacromigrate.php' => array(
'type' => 'php',
'name' => $this->getPatchPath('20121209.xmacromigrate.php'),
),
'20121209.xmacromigratekey.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('20121209.xmacromigratekey.sql'),
),
'20121220.generalcache.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('20121220.generalcache.sql'),
),
'20121226.config.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('20121226.config.sql'),
),
'20130101.confxaction.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('20130101.confxaction.sql'),
),
'20130102.metamtareceivedmailmessageidhash.sql' => array(
'type' => 'sql',
'name' =>
$this->getPatchPath('20130102.metamtareceivedmailmessageidhash.sql'),
),
'20130103.filemetadata.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('20130103.filemetadata.sql'),
),
'20130111.conpherence.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('20130111.conpherence.sql'),
),
'20130127.altheraldtranscript.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('20130127.altheraldtranscript.sql'),
),
'20130201.revisionunsubscribed.php' => array(
'type' => 'php',
'name' => $this->getPatchPath('20130201.revisionunsubscribed.php'),
),
'20130201.revisionunsubscribed.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('20130201.revisionunsubscribed.sql'),
),
'20130131.conpherencepics.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('20130131.conpherencepics.sql'),
),
'20130214.chatlogchannel.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('20130214.chatlogchannel.sql'),
),
'20130214.chatlogchannelid.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('20130214.chatlogchannelid.sql'),
),
'20130214.token.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('20130214.token.sql'),
),
'20130217.cachettl.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('20130217.cachettl.sql'),
),
'20130218.updatechannelid.php' => array(
'type' => 'php',
'name' => $this->getPatchPath('20130218.updatechannelid.php'),
),
'20130218.longdaemon.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('20130218.longdaemon.sql'),
),
+ '20130215.phabricatorfileaddttl.sql' => array(
+ 'type' => 'sql',
+ 'name' => $this->getPatchPath('20130215.phabricatorfileaddttl.sql'),
+ ),
);
}
}

File Metadata

Mime Type
text/x-diff
Expires
Fri, Aug 15, 2:09 PM (6 d, 7 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
198187
Default Alt Text
(113 KB)

Event Timeline