mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-04 20:52:43 +01:00
(stable) Promote 2016 Week 49
This commit is contained in:
commit
ad65d933fa
55 changed files with 1039 additions and 260 deletions
|
@ -9,8 +9,8 @@ return array(
|
|||
'names' => array(
|
||||
'conpherence.pkg.css' => '0b64e988',
|
||||
'conpherence.pkg.js' => '6249a1cf',
|
||||
'core.pkg.css' => '6ae56144',
|
||||
'core.pkg.js' => '519f84e8',
|
||||
'core.pkg.css' => '94090cab',
|
||||
'core.pkg.js' => 'e4260032',
|
||||
'darkconsole.pkg.js' => 'e7393ebb',
|
||||
'differential.pkg.css' => 'a4ba74b5',
|
||||
'differential.pkg.js' => '634399e9',
|
||||
|
@ -108,7 +108,7 @@ return array(
|
|||
'rsrc/css/application/tokens/tokens.css' => '3d0f239e',
|
||||
'rsrc/css/application/uiexample/example.css' => '528b19de',
|
||||
'rsrc/css/core/core.css' => 'd0801452',
|
||||
'rsrc/css/core/remarkup.css' => '8e3d4635',
|
||||
'rsrc/css/core/remarkup.css' => '8606d9c6',
|
||||
'rsrc/css/core/syntax.css' => '769d3498',
|
||||
'rsrc/css/core/z-index.css' => 'd1270942',
|
||||
'rsrc/css/diviner/diviner-shared.css' => 'aa3656aa',
|
||||
|
@ -132,7 +132,7 @@ return array(
|
|||
'rsrc/css/phui/phui-chart.css' => '6bf6f78e',
|
||||
'rsrc/css/phui/phui-cms.css' => 'be43c8a8',
|
||||
'rsrc/css/phui/phui-comment-form.css' => '4ecc56ef',
|
||||
'rsrc/css/phui/phui-comment-panel.css' => '5659325f',
|
||||
'rsrc/css/phui/phui-comment-panel.css' => 'f50152ad',
|
||||
'rsrc/css/phui/phui-crumbs-view.css' => '195ac419',
|
||||
'rsrc/css/phui/phui-curtain-view.css' => '947bf1a4',
|
||||
'rsrc/css/phui/phui-document-pro.css' => 'c354e312',
|
||||
|
@ -141,17 +141,17 @@ return array(
|
|||
'rsrc/css/phui/phui-feed-story.css' => '44a9c8e9',
|
||||
'rsrc/css/phui/phui-fontkit.css' => '9cda225e',
|
||||
'rsrc/css/phui/phui-form-view.css' => '3fadd537',
|
||||
'rsrc/css/phui/phui-form.css' => 'b8fb087a',
|
||||
'rsrc/css/phui/phui-form.css' => '2342b0e5',
|
||||
'rsrc/css/phui/phui-head-thing.css' => 'fd311e5f',
|
||||
'rsrc/css/phui/phui-header-view.css' => '6ec8f155',
|
||||
'rsrc/css/phui/phui-hovercard.css' => 'de1a2119',
|
||||
'rsrc/css/phui/phui-icon-set-selector.css' => '1ab67aad',
|
||||
'rsrc/css/phui/phui-icon.css' => '417f80fb',
|
||||
'rsrc/css/phui/phui-icon.css' => '09f46dd9',
|
||||
'rsrc/css/phui/phui-image-mask.css' => 'a8498f9c',
|
||||
'rsrc/css/phui/phui-info-panel.css' => '27ea50a1',
|
||||
'rsrc/css/phui/phui-info-view.css' => 'ec92802a',
|
||||
'rsrc/css/phui/phui-invisible-character-view.css' => '6993d9f0',
|
||||
'rsrc/css/phui/phui-lightbox.css' => '04367b4f',
|
||||
'rsrc/css/phui/phui-lightbox.css' => '0a035e40',
|
||||
'rsrc/css/phui/phui-list.css' => '9da2aa00',
|
||||
'rsrc/css/phui/phui-object-box.css' => '6b487c57',
|
||||
'rsrc/css/phui/phui-object-item-list-view.css' => '87278fa0',
|
||||
|
@ -166,8 +166,8 @@ return array(
|
|||
'rsrc/css/phui/phui-tag-view.css' => '6bbd83e2',
|
||||
'rsrc/css/phui/phui-timeline-view.css' => 'bc523970',
|
||||
'rsrc/css/phui/phui-two-column-view.css' => 'bbe32c23',
|
||||
'rsrc/css/phui/workboards/phui-workboard-color.css' => '207828dd',
|
||||
'rsrc/css/phui/workboards/phui-workboard.css' => '60d09514',
|
||||
'rsrc/css/phui/workboards/phui-workboard-color.css' => 'b60ef38a',
|
||||
'rsrc/css/phui/workboards/phui-workboard.css' => '16441d5e',
|
||||
'rsrc/css/phui/workboards/phui-workcard.css' => '0c62d7c5',
|
||||
'rsrc/css/phui/workboards/phui-workpanel.css' => '92197373',
|
||||
'rsrc/css/sprite-login.css' => '6dbbbd97',
|
||||
|
@ -378,7 +378,7 @@ return array(
|
|||
'rsrc/js/application/calendar/behavior-event-all-day.js' => 'b41537c9',
|
||||
'rsrc/js/application/calendar/behavior-month-view.js' => 'fe33e256',
|
||||
'rsrc/js/application/config/behavior-reorder-fields.js' => 'b6993408',
|
||||
'rsrc/js/application/conpherence/ConpherenceThreadManager.js' => '358c717b',
|
||||
'rsrc/js/application/conpherence/ConpherenceThreadManager.js' => 'c8b5ee6f',
|
||||
'rsrc/js/application/conpherence/behavior-conpherence-search.js' => '9bbf3762',
|
||||
'rsrc/js/application/conpherence/behavior-durable-column.js' => 'aa3bd034',
|
||||
'rsrc/js/application/conpherence/behavior-menu.js' => '7524fcfa',
|
||||
|
@ -505,7 +505,7 @@ return array(
|
|||
'rsrc/js/core/behavior-hovercard.js' => 'bcaccd64',
|
||||
'rsrc/js/core/behavior-keyboard-pager.js' => 'a8da01f0',
|
||||
'rsrc/js/core/behavior-keyboard-shortcuts.js' => '01fca1f0',
|
||||
'rsrc/js/core/behavior-lightbox-attachments.js' => '2674e4fa',
|
||||
'rsrc/js/core/behavior-lightbox-attachments.js' => 'ddcd41cf',
|
||||
'rsrc/js/core/behavior-line-linker.js' => '1499a8cb',
|
||||
'rsrc/js/core/behavior-more.js' => 'a80d0378',
|
||||
'rsrc/js/core/behavior-object-selector.js' => 'e0ec7f2f',
|
||||
|
@ -566,7 +566,7 @@ return array(
|
|||
'conpherence-message-pane-css' => 'b085d40d',
|
||||
'conpherence-notification-css' => '965db05b',
|
||||
'conpherence-participant-pane-css' => 'ac1baaa8',
|
||||
'conpherence-thread-manager' => '358c717b',
|
||||
'conpherence-thread-manager' => 'c8b5ee6f',
|
||||
'conpherence-transaction-css' => '85129c68',
|
||||
'd3' => 'a11a5ff2',
|
||||
'differential-changeset-view-css' => 'b158cc46',
|
||||
|
@ -651,7 +651,7 @@ return array(
|
|||
'javelin-behavior-history-install' => '7ee2b591',
|
||||
'javelin-behavior-icon-composer' => '8499b6ab',
|
||||
'javelin-behavior-launch-icon-composer' => '48086888',
|
||||
'javelin-behavior-lightbox-attachments' => '2674e4fa',
|
||||
'javelin-behavior-lightbox-attachments' => 'ddcd41cf',
|
||||
'javelin-behavior-line-chart' => 'e4232876',
|
||||
'javelin-behavior-load-blame' => '42126667',
|
||||
'javelin-behavior-maniphest-batch-editor' => '782ab6e7',
|
||||
|
@ -803,7 +803,7 @@ return array(
|
|||
'phabricator-object-selector-css' => '85ee8ce6',
|
||||
'phabricator-phtize' => 'd254d646',
|
||||
'phabricator-prefab' => '8d40ae75',
|
||||
'phabricator-remarkup-css' => '8e3d4635',
|
||||
'phabricator-remarkup-css' => '8606d9c6',
|
||||
'phabricator-search-results-css' => '7dea472c',
|
||||
'phabricator-shaped-request' => '7cbe244b',
|
||||
'phabricator-slowvote-css' => 'a94b7230',
|
||||
|
@ -847,7 +847,7 @@ return array(
|
|||
'phui-chart-css' => '6bf6f78e',
|
||||
'phui-cms-css' => 'be43c8a8',
|
||||
'phui-comment-form-css' => '4ecc56ef',
|
||||
'phui-comment-panel-css' => '5659325f',
|
||||
'phui-comment-panel-css' => 'f50152ad',
|
||||
'phui-crumbs-view-css' => '195ac419',
|
||||
'phui-curtain-view-css' => '947bf1a4',
|
||||
'phui-document-summary-view-css' => '9ca48bdf',
|
||||
|
@ -856,20 +856,20 @@ return array(
|
|||
'phui-feed-story-css' => '44a9c8e9',
|
||||
'phui-font-icon-base-css' => '870a7360',
|
||||
'phui-fontkit-css' => '9cda225e',
|
||||
'phui-form-css' => 'b8fb087a',
|
||||
'phui-form-css' => '2342b0e5',
|
||||
'phui-form-view-css' => '3fadd537',
|
||||
'phui-head-thing-view-css' => 'fd311e5f',
|
||||
'phui-header-view-css' => '6ec8f155',
|
||||
'phui-hovercard' => '1bd28176',
|
||||
'phui-hovercard-view-css' => 'de1a2119',
|
||||
'phui-icon-set-selector-css' => '1ab67aad',
|
||||
'phui-icon-view-css' => '417f80fb',
|
||||
'phui-icon-view-css' => '09f46dd9',
|
||||
'phui-image-mask-css' => 'a8498f9c',
|
||||
'phui-info-panel-css' => '27ea50a1',
|
||||
'phui-info-view-css' => 'ec92802a',
|
||||
'phui-inline-comment-view-css' => '5953c28e',
|
||||
'phui-invisible-character-view-css' => '6993d9f0',
|
||||
'phui-lightbox-css' => '04367b4f',
|
||||
'phui-lightbox-css' => '0a035e40',
|
||||
'phui-list-view-css' => '9da2aa00',
|
||||
'phui-object-box-css' => '6b487c57',
|
||||
'phui-object-item-list-view-css' => '87278fa0',
|
||||
|
@ -885,8 +885,8 @@ return array(
|
|||
'phui-theme-css' => '798c69b8',
|
||||
'phui-timeline-view-css' => 'bc523970',
|
||||
'phui-two-column-view-css' => 'bbe32c23',
|
||||
'phui-workboard-color-css' => '207828dd',
|
||||
'phui-workboard-view-css' => '60d09514',
|
||||
'phui-workboard-color-css' => 'b60ef38a',
|
||||
'phui-workboard-view-css' => '16441d5e',
|
||||
'phui-workcard-view-css' => '0c62d7c5',
|
||||
'phui-workpanel-view-css' => '92197373',
|
||||
'phuix-action-list-view' => 'b5c256b8',
|
||||
|
@ -1106,15 +1106,6 @@ return array(
|
|||
'javelin-workflow',
|
||||
'javelin-util',
|
||||
),
|
||||
'2674e4fa' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
'javelin-mask',
|
||||
'javelin-util',
|
||||
'phuix-icon-view',
|
||||
'phabricator-busy',
|
||||
),
|
||||
'2926fff2' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
|
@ -1166,17 +1157,6 @@ return array(
|
|||
'javelin-dom',
|
||||
'javelin-workflow',
|
||||
),
|
||||
'358c717b' => array(
|
||||
'javelin-dom',
|
||||
'javelin-util',
|
||||
'javelin-stratcom',
|
||||
'javelin-install',
|
||||
'javelin-aphlict',
|
||||
'javelin-workflow',
|
||||
'javelin-router',
|
||||
'javelin-behavior-device',
|
||||
'javelin-vector',
|
||||
),
|
||||
'3ab51e2c' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-behavior-device',
|
||||
|
@ -1361,9 +1341,6 @@ return array(
|
|||
'phabricator-drag-and-drop-file-upload',
|
||||
'javelin-workboard-board',
|
||||
),
|
||||
'5659325f' => array(
|
||||
'phui-timeline-view-css',
|
||||
),
|
||||
'58dea2fa' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
|
@ -1975,6 +1952,17 @@ return array(
|
|||
'c7ccd872' => array(
|
||||
'phui-fontkit-css',
|
||||
),
|
||||
'c8b5ee6f' => array(
|
||||
'javelin-dom',
|
||||
'javelin-util',
|
||||
'javelin-stratcom',
|
||||
'javelin-install',
|
||||
'javelin-aphlict',
|
||||
'javelin-workflow',
|
||||
'javelin-router',
|
||||
'javelin-behavior-device',
|
||||
'javelin-vector',
|
||||
),
|
||||
'c90a04fc' => array(
|
||||
'javelin-dom',
|
||||
'javelin-dynval',
|
||||
|
@ -2059,6 +2047,15 @@ return array(
|
|||
'javelin-util',
|
||||
'phabricator-shaped-request',
|
||||
),
|
||||
'ddcd41cf' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
'javelin-mask',
|
||||
'javelin-util',
|
||||
'phuix-icon-view',
|
||||
'phabricator-busy',
|
||||
),
|
||||
'de2e896f' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
|
@ -2177,6 +2174,9 @@ return array(
|
|||
'javelin-request',
|
||||
'phabricator-keyboard-shortcut',
|
||||
),
|
||||
'f50152ad' => array(
|
||||
'phui-timeline-view-css',
|
||||
),
|
||||
'f6555212' => array(
|
||||
'javelin-install',
|
||||
'javelin-reactornode',
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
CREATE TABLE {$NAMESPACE}_search.stopwords (
|
||||
value VARCHAR(32) NOT NULL COLLATE {$COLLATE_SORT}
|
||||
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
2
resources/sql/autopatches/20161125.search.01.stemmed.sql
Normal file
2
resources/sql/autopatches/20161125.search.01.stemmed.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_search.search_documentfield
|
||||
ADD stemmedCorpus LONGTEXT COLLATE {$COLLATE_FULLTEXT};
|
6
resources/sql/autopatches/20161130.search.01.manual.sql
Normal file
6
resources/sql/autopatches/20161130.search.01.manual.sql
Normal file
|
@ -0,0 +1,6 @@
|
|||
CREATE TABLE {$NAMESPACE}_config.config_manualactivity (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
activityType VARCHAR(64) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
parameters LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
UNIQUE KEY `key_type` (activityType)
|
||||
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
26
resources/sql/autopatches/20161130.search.02.rebuild.php
Normal file
26
resources/sql/autopatches/20161130.search.02.rebuild.php
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
$search_engine = PhabricatorFulltextStorageEngine::loadEngine();
|
||||
$use_mysql = ($search_engine instanceof PhabricatorMySQLFulltextStorageEngine);
|
||||
|
||||
if ($use_mysql) {
|
||||
$field = new PhabricatorSearchDocumentField();
|
||||
$conn = $field->establishConnection('r');
|
||||
|
||||
// We're only going to require this if the index isn't empty: if you're on a
|
||||
// fresh install, you don't have to do anything.
|
||||
$any_documents = queryfx_one(
|
||||
$conn,
|
||||
'SELECT * FROM %T LIMIT 1',
|
||||
$field->getTableName());
|
||||
|
||||
if ($any_documents) {
|
||||
try {
|
||||
id(new PhabricatorConfigManualActivity())
|
||||
->setActivityType(PhabricatorConfigManualActivity::TYPE_REINDEX)
|
||||
->save();
|
||||
} catch (AphrontDuplicateKeyQueryException $ex) {
|
||||
// If we've already noted that this activity is required, just move on.
|
||||
}
|
||||
}
|
||||
}
|
|
@ -67,7 +67,7 @@ then
|
|||
if [ $? -ne 0 ]; then
|
||||
echo "It doesn't look like you have the EPEL repo enabled. We are to add it"
|
||||
echo "for you, so that we can install git."
|
||||
$SUDO rpm -Uvh http://download.fedoraproject.org/pub/epel/5/i386/epel-release-5-4.noarch.rpm
|
||||
$SUDO rpm -Uvh https://download.fedoraproject.org/pub/epel/5/i386/epel-release-5-4.noarch.rpm
|
||||
fi
|
||||
YUMCOMMAND="$SUDO yum install httpd git php53 php53-cli php53-mysql php53-process php53-devel php53-gd gcc wget make pcre-devel mysql-server"
|
||||
else
|
||||
|
@ -92,7 +92,7 @@ then
|
|||
# Now that we've ensured all the devel packages required for pecl/apc are there, let's
|
||||
# set up PEAR, and install apc.
|
||||
echo "Attempting to install PEAR"
|
||||
wget http://pear.php.net/go-pear.phar
|
||||
wget https://pear.php.net/go-pear.phar
|
||||
$SUDO php go-pear.phar && $SUDO pecl install apc
|
||||
fi
|
||||
|
||||
|
|
|
@ -1050,6 +1050,7 @@ phutil_register_library_map(array(
|
|||
'FileMailReceiver' => 'applications/files/mail/FileMailReceiver.php',
|
||||
'FileQueryChunksConduitAPIMethod' => 'applications/files/conduit/FileQueryChunksConduitAPIMethod.php',
|
||||
'FileReplyHandler' => 'applications/files/mail/FileReplyHandler.php',
|
||||
'FileTypeIcon' => 'applications/files/constants/FileTypeIcon.php',
|
||||
'FileUploadChunkConduitAPIMethod' => 'applications/files/conduit/FileUploadChunkConduitAPIMethod.php',
|
||||
'FileUploadConduitAPIMethod' => 'applications/files/conduit/FileUploadConduitAPIMethod.php',
|
||||
'FileUploadHashConduitAPIMethod' => 'applications/files/conduit/FileUploadHashConduitAPIMethod.php',
|
||||
|
@ -2271,11 +2272,13 @@ phutil_register_library_map(array(
|
|||
'PhabricatorConfigListController' => 'applications/config/controller/PhabricatorConfigListController.php',
|
||||
'PhabricatorConfigLocalSource' => 'infrastructure/env/PhabricatorConfigLocalSource.php',
|
||||
'PhabricatorConfigManagementDeleteWorkflow' => 'applications/config/management/PhabricatorConfigManagementDeleteWorkflow.php',
|
||||
'PhabricatorConfigManagementDoneWorkflow' => 'applications/config/management/PhabricatorConfigManagementDoneWorkflow.php',
|
||||
'PhabricatorConfigManagementGetWorkflow' => 'applications/config/management/PhabricatorConfigManagementGetWorkflow.php',
|
||||
'PhabricatorConfigManagementListWorkflow' => 'applications/config/management/PhabricatorConfigManagementListWorkflow.php',
|
||||
'PhabricatorConfigManagementMigrateWorkflow' => 'applications/config/management/PhabricatorConfigManagementMigrateWorkflow.php',
|
||||
'PhabricatorConfigManagementSetWorkflow' => 'applications/config/management/PhabricatorConfigManagementSetWorkflow.php',
|
||||
'PhabricatorConfigManagementWorkflow' => 'applications/config/management/PhabricatorConfigManagementWorkflow.php',
|
||||
'PhabricatorConfigManualActivity' => 'applications/config/storage/PhabricatorConfigManualActivity.php',
|
||||
'PhabricatorConfigModule' => 'applications/config/module/PhabricatorConfigModule.php',
|
||||
'PhabricatorConfigModuleController' => 'applications/config/controller/PhabricatorConfigModuleController.php',
|
||||
'PhabricatorConfigOption' => 'applications/config/option/PhabricatorConfigOption.php',
|
||||
|
@ -2895,6 +2898,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorManiphestApplication' => 'applications/maniphest/application/PhabricatorManiphestApplication.php',
|
||||
'PhabricatorManiphestConfigOptions' => 'applications/maniphest/config/PhabricatorManiphestConfigOptions.php',
|
||||
'PhabricatorManiphestTaskTestDataGenerator' => 'applications/maniphest/lipsum/PhabricatorManiphestTaskTestDataGenerator.php',
|
||||
'PhabricatorManualActivitySetupCheck' => 'applications/config/check/PhabricatorManualActivitySetupCheck.php',
|
||||
'PhabricatorMarkupCache' => 'applications/cache/storage/PhabricatorMarkupCache.php',
|
||||
'PhabricatorMarkupEngine' => 'infrastructure/markup/PhabricatorMarkupEngine.php',
|
||||
'PhabricatorMarkupEngineTestCase' => 'infrastructure/markup/__tests__/PhabricatorMarkupEngineTestCase.php',
|
||||
|
@ -5732,6 +5736,7 @@ phutil_register_library_map(array(
|
|||
'FileMailReceiver' => 'PhabricatorObjectMailReceiver',
|
||||
'FileQueryChunksConduitAPIMethod' => 'FileConduitAPIMethod',
|
||||
'FileReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
|
||||
'FileTypeIcon' => 'Phobject',
|
||||
'FileUploadChunkConduitAPIMethod' => 'FileConduitAPIMethod',
|
||||
'FileUploadConduitAPIMethod' => 'FileConduitAPIMethod',
|
||||
'FileUploadHashConduitAPIMethod' => 'FileConduitAPIMethod',
|
||||
|
@ -7176,11 +7181,13 @@ phutil_register_library_map(array(
|
|||
'PhabricatorConfigListController' => 'PhabricatorConfigController',
|
||||
'PhabricatorConfigLocalSource' => 'PhabricatorConfigProxySource',
|
||||
'PhabricatorConfigManagementDeleteWorkflow' => 'PhabricatorConfigManagementWorkflow',
|
||||
'PhabricatorConfigManagementDoneWorkflow' => 'PhabricatorConfigManagementWorkflow',
|
||||
'PhabricatorConfigManagementGetWorkflow' => 'PhabricatorConfigManagementWorkflow',
|
||||
'PhabricatorConfigManagementListWorkflow' => 'PhabricatorConfigManagementWorkflow',
|
||||
'PhabricatorConfigManagementMigrateWorkflow' => 'PhabricatorConfigManagementWorkflow',
|
||||
'PhabricatorConfigManagementSetWorkflow' => 'PhabricatorConfigManagementWorkflow',
|
||||
'PhabricatorConfigManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||
'PhabricatorConfigManualActivity' => 'PhabricatorConfigEntryDAO',
|
||||
'PhabricatorConfigModule' => 'Phobject',
|
||||
'PhabricatorConfigModuleController' => 'PhabricatorConfigController',
|
||||
'PhabricatorConfigOption' => array(
|
||||
|
@ -7875,6 +7882,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorManiphestApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorManiphestConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorManiphestTaskTestDataGenerator' => 'PhabricatorTestDataGenerator',
|
||||
'PhabricatorManualActivitySetupCheck' => 'PhabricatorSetupCheck',
|
||||
'PhabricatorMarkupCache' => 'PhabricatorCacheDAO',
|
||||
'PhabricatorMarkupEngine' => 'Phobject',
|
||||
'PhabricatorMarkupEngineTestCase' => 'PhabricatorTestCase',
|
||||
|
|
|
@ -545,6 +545,13 @@ final class AphrontRequest extends Phobject {
|
|||
return id(new PhutilURI($path))->setQueryParams($get);
|
||||
}
|
||||
|
||||
public function getAbsoluteRequestURI() {
|
||||
$uri = $this->getRequestURI();
|
||||
$uri->setDomain($this->getHost());
|
||||
$uri->setProtocol($this->isHTTPS() ? 'https' : 'http');
|
||||
return $uri;
|
||||
}
|
||||
|
||||
public function isDialogFormPost() {
|
||||
return $this->isFormPost() && $this->getStr('__dialog__');
|
||||
}
|
||||
|
|
|
@ -409,19 +409,25 @@ abstract class AphrontApplicationConfiguration extends Phobject {
|
|||
if (!preg_match('@/$@', $path) && $request->isHTTPGet()) {
|
||||
$result = $this->routePath($maps, $path.'/');
|
||||
if ($result) {
|
||||
$slash_uri = $request->getRequestURI()->setPath($path.'/');
|
||||
$target_uri = $request->getAbsoluteRequestURI();
|
||||
|
||||
// We need to restore URI encoding because the webserver has
|
||||
// interpreted it. For example, this allows us to redirect a path
|
||||
// like `/tag/aa%20bb` to `/tag/aa%20bb/`, which may eventually be
|
||||
// resolved meaningfully by an application.
|
||||
$slash_uri = phutil_escape_uri($slash_uri);
|
||||
$target_path = phutil_escape_uri($path.'/');
|
||||
$target_uri->setPath($target_path);
|
||||
$target_uri = (string)$target_uri;
|
||||
|
||||
$external = strlen($request->getRequestURI()->getDomain());
|
||||
return $this->buildRedirectController($slash_uri, $external);
|
||||
return $this->buildRedirectController($target_uri, true);
|
||||
}
|
||||
}
|
||||
|
||||
$result = $site->new404Controller($request);
|
||||
if ($result) {
|
||||
return array($result, array());
|
||||
}
|
||||
|
||||
return $this->build404Controller();
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,10 @@ abstract class AphrontSite extends Phobject {
|
|||
abstract public function newSiteForRequest(AphrontRequest $request);
|
||||
abstract public function getRoutingMaps();
|
||||
|
||||
public function new404Controller(AphrontRequest $request) {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function isHostMatch($host, array $uris) {
|
||||
foreach ($uris as $uri) {
|
||||
if (!strlen($uri)) {
|
||||
|
|
|
@ -138,18 +138,54 @@ final class PhabricatorCalendarEventCancelController
|
|||
->addSubmitButton($submit);
|
||||
|
||||
if ($show_control) {
|
||||
$start_time = phutil_tag(
|
||||
'strong',
|
||||
array(),
|
||||
phabricator_datetime($event->getStartDateTimeEpoch(), $viewer));
|
||||
|
||||
if ($is_cancelled) {
|
||||
$this_name = pht('Reinstate Only This Event');
|
||||
$this_caption = pht(
|
||||
'Reinstate only the event which occurs on %s.',
|
||||
$start_time);
|
||||
|
||||
$future_name = pht('Reinstate This And All Later Events');
|
||||
$future_caption = pht(
|
||||
'Reinstate this event and all events in the series which occur '.
|
||||
'on or after %s.',
|
||||
$start_time);
|
||||
} else {
|
||||
$this_name = pht('Cancel Only This Event');
|
||||
$this_caption = pht(
|
||||
'Cancel only the event which occurs on %s.',
|
||||
$start_time);
|
||||
|
||||
$future_name = pht('Cancel This And All Later Events');
|
||||
$future_caption = pht(
|
||||
'Cancel this event and all events in the series which occur '.
|
||||
'on or after %s.',
|
||||
$start_time);
|
||||
}
|
||||
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setViewer($viewer)
|
||||
->appendControl(
|
||||
id(new AphrontFormSelectControl())
|
||||
->setLabel(pht('Cancel Events'))
|
||||
id(new AphrontFormRadioButtonControl())
|
||||
->setName('mode')
|
||||
->setOptions(
|
||||
array(
|
||||
'this' => pht('Only This Event'),
|
||||
'future' => pht('All Future Events'),
|
||||
)));
|
||||
$dialog->appendForm($form);
|
||||
->setValue(PhabricatorCalendarEventEditEngine::MODE_THIS)
|
||||
->addButton(
|
||||
PhabricatorCalendarEventEditEngine::MODE_THIS,
|
||||
$this_name,
|
||||
$this_caption)
|
||||
->addButton(
|
||||
PhabricatorCalendarEventEditEngine::MODE_FUTURE,
|
||||
$future_name,
|
||||
$future_caption));
|
||||
|
||||
$dialog
|
||||
->setWidth(AphrontDialogView::WIDTH_FORM)
|
||||
->appendForm($form);
|
||||
}
|
||||
|
||||
return $dialog;
|
||||
|
|
|
@ -46,22 +46,34 @@ final class PhabricatorCalendarEventEditController
|
|||
}
|
||||
|
||||
if (!$mode) {
|
||||
$start_time = phutil_tag(
|
||||
'strong',
|
||||
array(),
|
||||
phabricator_datetime($event->getStartDateTimeEpoch(), $viewer));
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setViewer($viewer)
|
||||
->appendControl(
|
||||
id(new AphrontFormSelectControl())
|
||||
->setLabel(pht('Edit Events'))
|
||||
id(new AphrontFormRadioButtonControl())
|
||||
->setName('mode')
|
||||
->setOptions(
|
||||
array(
|
||||
PhabricatorCalendarEventEditEngine::MODE_THIS
|
||||
=> pht('Edit Only This Event'),
|
||||
PhabricatorCalendarEventEditEngine::MODE_FUTURE
|
||||
=> pht('Edit All Future Events'),
|
||||
)));
|
||||
|
||||
->setValue(PhabricatorCalendarEventEditEngine::MODE_THIS)
|
||||
->addButton(
|
||||
PhabricatorCalendarEventEditEngine::MODE_THIS,
|
||||
pht('Edit Only This Event'),
|
||||
pht(
|
||||
'Edit only the event which occurs at %s.',
|
||||
$start_time))
|
||||
->addButton(
|
||||
PhabricatorCalendarEventEditEngine::MODE_FUTURE,
|
||||
pht('Edit This And All Later Events'),
|
||||
pht(
|
||||
'Edit this event and all events in the series which '.
|
||||
'occur on or after %s. This will overwrite previous '.
|
||||
'edits!',
|
||||
$start_time)));
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Edit Event'))
|
||||
->setWidth(AphrontDialogView::WIDTH_FORM)
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'This event is part of a series. Which events do you '.
|
||||
|
|
|
@ -55,7 +55,7 @@ final class PhabricatorCalendarEventEditor
|
|||
if ($xaction->getTransactionType() != $type_allday) {
|
||||
continue;
|
||||
}
|
||||
$target_alllday = (bool)$xaction->getNewValue();
|
||||
$new_allday = (bool)$xaction->getNewValue();
|
||||
}
|
||||
|
||||
$this->oldIsAllDay = $old_allday;
|
||||
|
|
|
@ -1024,6 +1024,10 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
|
||||
$set = new PhutilCalendarRecurrenceSet();
|
||||
|
||||
if ($this->viewerTimezone) {
|
||||
$set->setViewerTimezone($this->viewerTimezone);
|
||||
}
|
||||
|
||||
$rrule = $this->newRecurrenceRule();
|
||||
if (!$rrule) {
|
||||
return null;
|
||||
|
|
|
@ -22,6 +22,35 @@ abstract class PhabricatorCalendarEventDateTransaction
|
|||
->toDictionary();
|
||||
}
|
||||
|
||||
public function getTransactionHasEffect($object, $old, $new) {
|
||||
$editor = $this->getEditor();
|
||||
|
||||
$actor = $this->getActor();
|
||||
$actor_timezone = $actor->getTimezoneIdentifier();
|
||||
|
||||
// When an edit only changes the timezone of an event without materially
|
||||
// changing the absolute time, discard it. This can happen if two users in
|
||||
// different timezones edit an event without rescheduling it.
|
||||
|
||||
// Eventually, after T11073, there may be a UI control to adjust timezones.
|
||||
// If a user explicitly changed the timezone, we should respect that.
|
||||
// However, there is no way for users to intentionally apply this kind of
|
||||
// edit today.
|
||||
|
||||
$old_datetime = PhutilCalendarAbsoluteDateTime::newFromDictionary($old)
|
||||
->setIsAllDay($editor->getNewIsAllDay())
|
||||
->setViewerTimezone($actor_timezone);
|
||||
|
||||
$new_datetime = PhutilCalendarAbsoluteDateTime::newFromDictionary($new)
|
||||
->setIsAllDay($editor->getNewIsAllDay())
|
||||
->setViewerTimezone($actor_timezone);
|
||||
|
||||
$old_epoch = $old_datetime->getEpoch();
|
||||
$new_epoch = $new_datetime->getEpoch();
|
||||
|
||||
return ($old_epoch !== $new_epoch);
|
||||
}
|
||||
|
||||
public function validateTransactions($object, array $xactions) {
|
||||
$errors = array();
|
||||
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorManualActivitySetupCheck
|
||||
extends PhabricatorSetupCheck {
|
||||
|
||||
public function getDefaultGroup() {
|
||||
return self::GROUP_OTHER;
|
||||
}
|
||||
|
||||
protected function executeChecks() {
|
||||
$activities = id(new PhabricatorConfigManualActivity())->loadAll();
|
||||
|
||||
foreach ($activities as $activity) {
|
||||
$type = $activity->getActivityType();
|
||||
|
||||
// For now, there is only one type of manual activity. It's not clear
|
||||
// if we're really going to have too much more of this stuff so this
|
||||
// is a bit under-designed for now.
|
||||
|
||||
$activity_name = pht('Rebuild Search Index');
|
||||
$activity_summary = pht(
|
||||
'The search index algorithm has been updated and the index needs '.
|
||||
'be rebuilt.');
|
||||
|
||||
$message = array();
|
||||
|
||||
$message[] = pht(
|
||||
'The indexing algorithm for the fulltext search index has been '.
|
||||
'updated and the index needs to be rebuilt. Until you rebuild the '.
|
||||
'index, global search (and other fulltext search) will not '.
|
||||
'function correctly.');
|
||||
|
||||
$message[] = pht(
|
||||
'You can rebuild the search index while Phabricator is running.');
|
||||
|
||||
$message[] = pht(
|
||||
'To rebuild the index, run this command:');
|
||||
|
||||
$message[] = phutil_tag(
|
||||
'pre',
|
||||
array(),
|
||||
(string)csprintf(
|
||||
'phabricator/ $ ./bin/search index --all --force --background'));
|
||||
|
||||
$message[] = pht(
|
||||
'You can find more information about rebuilding the search '.
|
||||
'index here: %s',
|
||||
phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => 'https://phurl.io/u/reindex',
|
||||
'target' => '_blank',
|
||||
),
|
||||
'https://phurl.io/u/reindex'));
|
||||
|
||||
$message[] = pht(
|
||||
'After rebuilding the index, run this command to clear this setup '.
|
||||
'warning:');
|
||||
|
||||
$message[] = phutil_tag(
|
||||
'pre',
|
||||
array(),
|
||||
(string)csprintf('phabricator/ $ ./bin/config done %R', $type));
|
||||
|
||||
$activity_message = phutil_implode_html("\n\n", $message);
|
||||
|
||||
$this->newIssue('manual.'.$type)
|
||||
->setName($activity_name)
|
||||
->setSummary($activity_summary)
|
||||
->setMessage($activity_message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -121,8 +121,18 @@ final class PhabricatorMySQLSetupCheck extends PhabricatorSetupCheck {
|
|||
->addMySQLConfig('sql_mode');
|
||||
}
|
||||
|
||||
$stopword_file = $ref->loadRawMySQLConfigValue('ft_stopword_file');
|
||||
$is_innodb_fulltext = false;
|
||||
$is_myisam_fulltext = false;
|
||||
if ($this->shouldUseMySQLSearchEngine()) {
|
||||
if (PhabricatorSearchDocument::isInnoDBFulltextEngineAvailable()) {
|
||||
$is_innodb_fulltext = true;
|
||||
} else {
|
||||
$is_myisam_fulltext = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($is_myisam_fulltext) {
|
||||
$stopword_file = $ref->loadRawMySQLConfigValue('ft_stopword_file');
|
||||
if ($stopword_file === null) {
|
||||
$summary = pht(
|
||||
'Your version of MySQL (on database host "%s") does not support '.
|
||||
|
@ -200,9 +210,9 @@ final class PhabricatorMySQLSetupCheck extends PhabricatorSetupCheck {
|
|||
}
|
||||
}
|
||||
|
||||
$min_len = $ref->loadRawMySQLConfigValue('ft_min_word_len');
|
||||
if ($min_len >= 4) {
|
||||
if ($this->shouldUseMySQLSearchEngine()) {
|
||||
if ($is_myisam_fulltext) {
|
||||
$min_len = $ref->loadRawMySQLConfigValue('ft_min_word_len');
|
||||
if ($min_len >= 4) {
|
||||
$namespace = PhabricatorEnv::getEnvConfig('storage.default-namespace');
|
||||
|
||||
$summary = pht(
|
||||
|
@ -248,6 +258,18 @@ final class PhabricatorMySQLSetupCheck extends PhabricatorSetupCheck {
|
|||
}
|
||||
}
|
||||
|
||||
// NOTE: The default value of "innodb_ft_min_token_size" is 3, which is
|
||||
// a reasonable value, so we do not warn about it: if it is set to
|
||||
// something else, the user adjusted it on their own.
|
||||
|
||||
// NOTE: We populate a stopwords table at "phabricator_search.stopwords",
|
||||
// but the default InnoDB stopword list is pretty reasonable (36 words,
|
||||
// versus 500+ in MyISAM). Just use the builtin list until we run into
|
||||
// concrete issues with it. Users can switch to our stopword table with:
|
||||
//
|
||||
// [mysqld]
|
||||
// innodb_ft_server_stopword_table = phabricator_search/stopwords
|
||||
|
||||
$innodb_pool = $ref->loadRawMySQLConfigValue('innodb_buffer_pool_size');
|
||||
$innodb_bytes = phutil_parse_bytes($innodb_pool);
|
||||
$innodb_readable = phutil_format_bytes($innodb_bytes);
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorConfigManagementDoneWorkflow
|
||||
extends PhabricatorConfigManagementWorkflow {
|
||||
|
||||
protected function didConstruct() {
|
||||
$this
|
||||
->setName('done')
|
||||
->setExamples('**done** __activity__')
|
||||
->setSynopsis(pht('Mark a manual upgrade activity as complete.'))
|
||||
->setArguments(
|
||||
array(
|
||||
array(
|
||||
'name' => 'force',
|
||||
'short' => 'f',
|
||||
'help' => pht(
|
||||
'Mark activities complete even if there is no outstanding '.
|
||||
'need to complete them.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'activities',
|
||||
'wildcard' => true,
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
public function execute(PhutilArgumentParser $args) {
|
||||
$is_force = $args->getArg('force');
|
||||
|
||||
$activities = $args->getArg('activities');
|
||||
if (!$activities) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht('Specify an activity to mark as completed.'));
|
||||
}
|
||||
|
||||
foreach ($activities as $type) {
|
||||
$activity = id(new PhabricatorConfigManualActivity())->loadOneWhere(
|
||||
'activityType = %s',
|
||||
$type);
|
||||
if (!$activity) {
|
||||
if ($is_force) {
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht(
|
||||
'Activity "%s" did not need to be marked as complete.',
|
||||
$type));
|
||||
} else {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Activity "%s" is not currently marked as required, so there '.
|
||||
'is no need to complete it.',
|
||||
$type));
|
||||
}
|
||||
} else {
|
||||
$activity->delete();
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht(
|
||||
'Marked activity "%s" as completed.',
|
||||
$type));
|
||||
}
|
||||
}
|
||||
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht('Done.'));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -68,7 +68,7 @@ final class PhabricatorConfigSchemaQuery extends Phobject {
|
|||
|
||||
$tables = queryfx_all(
|
||||
$conn,
|
||||
'SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_COLLATION
|
||||
'SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_COLLATION, ENGINE
|
||||
FROM INFORMATION_SCHEMA.TABLES
|
||||
WHERE TABLE_SCHEMA IN (%Ls)',
|
||||
$databases);
|
||||
|
@ -146,7 +146,8 @@ final class PhabricatorConfigSchemaQuery extends Phobject {
|
|||
|
||||
$table_schema = id(new PhabricatorConfigTableSchema())
|
||||
->setName($table_name)
|
||||
->setCollation($table['TABLE_COLLATION']);
|
||||
->setCollation($table['TABLE_COLLATION'])
|
||||
->setEngine($table['ENGINE']);
|
||||
|
||||
$columns = idx($database_column_info, $table_name, array());
|
||||
foreach ($columns as $column) {
|
||||
|
|
|
@ -64,6 +64,12 @@ abstract class PhabricatorConfigSchemaSpec extends Phobject {
|
|||
|
||||
$table = $this->newTable($table_name);
|
||||
|
||||
if (PhabricatorSearchDocument::isInnoDBFulltextEngineAvailable()) {
|
||||
$fulltext_engine = 'InnoDB';
|
||||
} else {
|
||||
$fulltext_engine = 'MyISAM';
|
||||
}
|
||||
|
||||
foreach ($columns as $name => $type) {
|
||||
if ($type === null) {
|
||||
continue;
|
||||
|
@ -85,6 +91,15 @@ abstract class PhabricatorConfigSchemaSpec extends Phobject {
|
|||
->setNullable($nullable)
|
||||
->setAutoIncrement($auto);
|
||||
|
||||
// If this table has any FULLTEXT fields, we expect it to use the best
|
||||
// available FULLTEXT engine, which may not be InnoDB.
|
||||
switch ($type) {
|
||||
case 'fulltext':
|
||||
case 'fulltext?':
|
||||
$table->setEngine($fulltext_engine);
|
||||
break;
|
||||
}
|
||||
|
||||
$table->addColumn($column);
|
||||
}
|
||||
|
||||
|
@ -174,7 +189,8 @@ abstract class PhabricatorConfigSchemaSpec extends Phobject {
|
|||
protected function newTable($name) {
|
||||
return id(new PhabricatorConfigTableSchema())
|
||||
->setName($name)
|
||||
->setCollation($this->getUTF8BinaryCollation());
|
||||
->setCollation($this->getUTF8BinaryCollation())
|
||||
->setEngine('InnoDB');
|
||||
}
|
||||
|
||||
protected function newColumn($name) {
|
||||
|
|
|
@ -18,6 +18,7 @@ abstract class PhabricatorConfigStorageSchema extends Phobject {
|
|||
const ISSUE_AUTOINCREMENT = 'autoincrement';
|
||||
const ISSUE_UNKNOWN = 'unknown';
|
||||
const ISSUE_ACCESSDENIED = 'accessdenied';
|
||||
const ISSUE_ENGINE = 'engine';
|
||||
|
||||
const STATUS_OKAY = 'okay';
|
||||
const STATUS_WARN = 'warn';
|
||||
|
@ -133,6 +134,8 @@ abstract class PhabricatorConfigStorageSchema extends Phobject {
|
|||
return pht('Column Has No Specification');
|
||||
case self::ISSUE_ACCESSDENIED:
|
||||
return pht('Access Denied');
|
||||
case self::ISSUE_ENGINE:
|
||||
return pht('Better Table Engine Available');
|
||||
default:
|
||||
throw new Exception(pht('Unknown schema issue "%s"!', $issue));
|
||||
}
|
||||
|
@ -170,6 +173,8 @@ abstract class PhabricatorConfigStorageSchema extends Phobject {
|
|||
return pht('This column has the wrong autoincrement setting.');
|
||||
case self::ISSUE_UNKNOWN:
|
||||
return pht('This column is missing a type specification.');
|
||||
case self::ISSUE_ENGINE:
|
||||
return pht('This table can use a better table engine.');
|
||||
default:
|
||||
throw new Exception(pht('Unknown schema issue "%s"!', $issue));
|
||||
}
|
||||
|
@ -194,6 +199,7 @@ abstract class PhabricatorConfigStorageSchema extends Phobject {
|
|||
case self::ISSUE_KEYCOLUMNS:
|
||||
case self::ISSUE_LONGKEY:
|
||||
case self::ISSUE_AUTOINCREMENT:
|
||||
case self::ISSUE_ENGINE:
|
||||
return self::STATUS_WARN;
|
||||
default:
|
||||
throw new Exception(pht('Unknown schema issue "%s"!', $issue));
|
||||
|
|
|
@ -4,6 +4,7 @@ final class PhabricatorConfigTableSchema
|
|||
extends PhabricatorConfigStorageSchema {
|
||||
|
||||
private $collation;
|
||||
private $engine;
|
||||
private $columns = array();
|
||||
private $keys = array();
|
||||
|
||||
|
@ -62,6 +63,15 @@ final class PhabricatorConfigTableSchema
|
|||
return $this->collation;
|
||||
}
|
||||
|
||||
public function setEngine($engine) {
|
||||
$this->engine = $engine;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEngine() {
|
||||
return $this->engine;
|
||||
}
|
||||
|
||||
protected function compareToSimilarSchema(
|
||||
PhabricatorConfigStorageSchema $expect) {
|
||||
|
||||
|
@ -70,6 +80,10 @@ final class PhabricatorConfigTableSchema
|
|||
$issues[] = self::ISSUE_COLLATION;
|
||||
}
|
||||
|
||||
if ($this->getEngine() != $expect->getEngine()) {
|
||||
$issues[] = self::ISSUE_ENGINE;
|
||||
}
|
||||
|
||||
return $issues;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorConfigManualActivity
|
||||
extends PhabricatorConfigEntryDAO {
|
||||
|
||||
protected $activityType;
|
||||
protected $parameters = array();
|
||||
|
||||
const TYPE_REINDEX = 'reindex';
|
||||
|
||||
protected function getConfiguration() {
|
||||
return array(
|
||||
self::CONFIG_TIMESTAMPS => false,
|
||||
self::CONFIG_SERIALIZATION => array(
|
||||
'parameters' => self::SERIALIZATION_JSON,
|
||||
),
|
||||
self::CONFIG_COLUMN_SCHEMA => array(
|
||||
'activityType' => 'text64',
|
||||
),
|
||||
self::CONFIG_KEY_SCHEMA => array(
|
||||
'key_type' => array(
|
||||
'columns' => array('activityType'),
|
||||
'unique' => true,
|
||||
),
|
||||
),
|
||||
) + parent::getConfiguration();
|
||||
}
|
||||
|
||||
public function setParameter($key, $value) {
|
||||
$this->parameters[$key] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getParameter($key, $default = null) {
|
||||
return idx($this->parameters, $key, $default);
|
||||
}
|
||||
|
||||
}
|
|
@ -110,67 +110,6 @@ final class DifferentialChangesetDetailView extends AphrontView {
|
|||
return $this->vsChangesetID;
|
||||
}
|
||||
|
||||
public function getFileIcon($filename) {
|
||||
$path_info = pathinfo($filename);
|
||||
$extension = idx($path_info, 'extension');
|
||||
switch ($extension) {
|
||||
case 'psd':
|
||||
case 'ai':
|
||||
$icon = 'fa-eye';
|
||||
break;
|
||||
case 'conf':
|
||||
$icon = 'fa-wrench';
|
||||
break;
|
||||
case 'wav':
|
||||
case 'mp3':
|
||||
case 'aiff':
|
||||
$icon = 'fa-file-sound-o';
|
||||
break;
|
||||
case 'm4v':
|
||||
case 'mov':
|
||||
$icon = 'fa-file-movie-o';
|
||||
break;
|
||||
case 'sql':
|
||||
case 'db':
|
||||
$icon = 'fa-database';
|
||||
break;
|
||||
case 'xls':
|
||||
case 'csv':
|
||||
$icon = 'fa-file-excel-o';
|
||||
break;
|
||||
case 'ics':
|
||||
$icon = 'fa-calendar';
|
||||
break;
|
||||
case 'zip':
|
||||
case 'tar':
|
||||
case 'bz':
|
||||
case 'tgz':
|
||||
case 'gz':
|
||||
$icon = 'fa-file-archive-o';
|
||||
break;
|
||||
case 'png':
|
||||
case 'jpg':
|
||||
case 'bmp':
|
||||
case 'gif':
|
||||
$icon = 'fa-file-picture-o';
|
||||
break;
|
||||
case 'txt':
|
||||
$icon = 'fa-file-text-o';
|
||||
break;
|
||||
case 'doc':
|
||||
case 'docx':
|
||||
$icon = 'fa-file-word-o';
|
||||
break;
|
||||
case 'pdf':
|
||||
$icon = 'fa-file-pdf-o';
|
||||
break;
|
||||
default:
|
||||
$icon = 'fa-file-code-o';
|
||||
break;
|
||||
}
|
||||
return $icon;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
$this->requireResource('differential-changeset-view-css');
|
||||
$this->requireResource('syntax-highlighting-css');
|
||||
|
@ -204,7 +143,7 @@ final class DifferentialChangesetDetailView extends AphrontView {
|
|||
}
|
||||
|
||||
$display_filename = $changeset->getDisplayFilename();
|
||||
$display_icon = $this->getFileIcon($display_filename);
|
||||
$display_icon = FileTypeIcon::getFileIcon($display_filename);
|
||||
$icon = id(new PHUIIconView())
|
||||
->setIcon($display_icon);
|
||||
|
||||
|
|
|
@ -462,6 +462,11 @@ final class DiffusionURIEditor
|
|||
->withRepositories(array($repository))
|
||||
->execute();
|
||||
|
||||
// Reattach the current URIs to the repository: we're going to rebuild
|
||||
// the index explicitly below, and want to include any changes made to
|
||||
// this URI in the index update.
|
||||
$repository->attachURIs($uris);
|
||||
|
||||
$observe_uri = null;
|
||||
foreach ($uris as $uri) {
|
||||
if ($uri->getIoType() != PhabricatorRepositoryURI::IO_OBSERVE) {
|
||||
|
@ -488,6 +493,9 @@ final class DiffusionURIEditor
|
|||
|
||||
$repository->save();
|
||||
|
||||
// Explicitly update the URI index.
|
||||
$repository->updateURIIndex();
|
||||
|
||||
$is_hosted = $repository->isHosted();
|
||||
|
||||
// If we've swapped the repository from hosted to observed or vice versa,
|
||||
|
|
|
@ -35,4 +35,14 @@ abstract class DiffusionGitSSHWorkflow
|
|||
}
|
||||
}
|
||||
|
||||
protected function raiseWrongVCSException(
|
||||
PhabricatorRepository $repository) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'This repository ("%s") is not a Git repository. Use "%s" to '.
|
||||
'interact with this repository.',
|
||||
$repository->getDisplayName(),
|
||||
$repository->getVersionControlSystem()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -119,4 +119,14 @@ final class DiffusionMercurialServeSSHWorkflow
|
|||
return $raw_message;
|
||||
}
|
||||
|
||||
protected function raiseWrongVCSException(
|
||||
PhabricatorRepository $repository) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'This repository ("%s") is not a Mercurial repository. Use "%s" to '.
|
||||
'interact with this repository.',
|
||||
$repository->getDisplayName(),
|
||||
$repository->getVersionControlSystem()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -43,6 +43,8 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
|
|||
*/
|
||||
abstract protected function identifyRepository();
|
||||
abstract protected function executeRepositoryOperations();
|
||||
abstract protected function raiseWrongVCSException(
|
||||
PhabricatorRepository $repository);
|
||||
|
||||
protected function getBaseRequestPath() {
|
||||
return $this->baseRequestPath;
|
||||
|
@ -199,6 +201,10 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
|
|||
$repository->getDisplayName()));
|
||||
}
|
||||
|
||||
if ($repository->getVersionControlSystem() != $vcs) {
|
||||
$this->raiseWrongVCSException($repository);
|
||||
}
|
||||
|
||||
return $repository;
|
||||
}
|
||||
|
||||
|
|
|
@ -449,4 +449,14 @@ final class DiffusionSubversionServeSSHWorkflow
|
|||
return $path;
|
||||
}
|
||||
|
||||
protected function raiseWrongVCSException(
|
||||
PhabricatorRepository $repository) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'This repository ("%s") is not a Subversion repository. Use "%s" to '.
|
||||
'interact with this repository.',
|
||||
$repository->getDisplayName(),
|
||||
$repository->getVersionControlSystem()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
66
src/applications/files/constants/FileTypeIcon.php
Normal file
66
src/applications/files/constants/FileTypeIcon.php
Normal file
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
final class FileTypeIcon extends Phobject {
|
||||
|
||||
public static function getFileIcon($filename) {
|
||||
$path_info = pathinfo($filename);
|
||||
$extension = idx($path_info, 'extension');
|
||||
switch ($extension) {
|
||||
case 'psd':
|
||||
case 'ai':
|
||||
$icon = 'fa-file-image-o';
|
||||
break;
|
||||
case 'conf':
|
||||
$icon = 'fa-wrench';
|
||||
break;
|
||||
case 'wav':
|
||||
case 'mp3':
|
||||
case 'aiff':
|
||||
$icon = 'fa-file-sound-o';
|
||||
break;
|
||||
case 'm4v':
|
||||
case 'mov':
|
||||
$icon = 'fa-file-movie-o';
|
||||
break;
|
||||
case 'sql':
|
||||
case 'db':
|
||||
$icon = 'fa-database';
|
||||
break;
|
||||
case 'xls':
|
||||
case 'csv':
|
||||
$icon = 'fa-file-excel-o';
|
||||
break;
|
||||
case 'ics':
|
||||
$icon = 'fa-calendar';
|
||||
break;
|
||||
case 'zip':
|
||||
case 'tar':
|
||||
case 'bz':
|
||||
case 'tgz':
|
||||
case 'gz':
|
||||
$icon = 'fa-file-archive-o';
|
||||
break;
|
||||
case 'png':
|
||||
case 'jpg':
|
||||
case 'bmp':
|
||||
case 'gif':
|
||||
$icon = 'fa-file-picture-o';
|
||||
break;
|
||||
case 'txt':
|
||||
$icon = 'fa-file-text-o';
|
||||
break;
|
||||
case 'doc':
|
||||
case 'docx':
|
||||
$icon = 'fa-file-word-o';
|
||||
break;
|
||||
case 'pdf':
|
||||
$icon = 'fa-file-pdf-o';
|
||||
break;
|
||||
default:
|
||||
$icon = 'fa-file-text-o';
|
||||
break;
|
||||
}
|
||||
return $icon;
|
||||
}
|
||||
|
||||
}
|
|
@ -287,6 +287,7 @@ final class PhabricatorEmbedFileRemarkupRule
|
|||
->setFileDownloadURI($file->getDownloadURI())
|
||||
->setFileViewURI($file->getBestURI())
|
||||
->setFileViewable((bool)$options['viewable'])
|
||||
->setFileSize(phutil_format_bytes($file->getByteSize()))
|
||||
->setFileMonogram($file->getMonogram());
|
||||
}
|
||||
|
||||
|
|
|
@ -92,7 +92,6 @@ final class PhabricatorPhameApplication extends PhabricatorApplication {
|
|||
'/' => array(
|
||||
'' => 'PhameBlogViewController',
|
||||
'post/(?P<id>\d+)/(?:(?P<slug>[^/]+)/)?' => 'PhamePostViewController',
|
||||
'.*' => 'PhameBlog404Controller',
|
||||
),
|
||||
|
||||
);
|
||||
|
|
|
@ -60,6 +60,10 @@ final class PhameBlogSite extends PhameSite {
|
|||
return id(new PhameBlogSite())->setBlog($blog);
|
||||
}
|
||||
|
||||
public function new404Controller(AphrontRequest $request) {
|
||||
return new PhameBlog404Controller();
|
||||
}
|
||||
|
||||
public function getRoutingMaps() {
|
||||
$app = PhabricatorApplication::getByClass('PhabricatorPhameApplication');
|
||||
|
||||
|
|
|
@ -416,6 +416,8 @@ final class PhabricatorProjectBoardViewController
|
|||
->appendChild($board)
|
||||
->addClass('project-board-wrapper');
|
||||
|
||||
$nav = $this->getProfileMenu();
|
||||
|
||||
$divider = id(new PHUIListItemView())
|
||||
->setType(PHUIListItemView::TYPE_DIVIDER);
|
||||
$fullscreen = $this->buildFullscreenMenu();
|
||||
|
@ -438,6 +440,7 @@ final class PhabricatorProjectBoardViewController
|
|||
))
|
||||
->setPageObjectPHIDs(array($project->getPHID()))
|
||||
->setShowFooter(false)
|
||||
->setNavigation($nav)
|
||||
->setCrumbs($crumbs)
|
||||
->addQuicksandConfig(
|
||||
array(
|
||||
|
|
|
@ -33,6 +33,8 @@ final class PhabricatorMySQLFulltextStorageEngine
|
|||
|
||||
$conn_w = $store->establishConnection('w');
|
||||
|
||||
$stemmer = new PhutilSearchStemmer();
|
||||
|
||||
$field_dao = new PhabricatorSearchDocumentField();
|
||||
queryfx(
|
||||
$conn_w,
|
||||
|
@ -41,16 +43,21 @@ final class PhabricatorMySQLFulltextStorageEngine
|
|||
$phid);
|
||||
foreach ($doc->getFieldData() as $field) {
|
||||
list($ftype, $corpus, $aux_phid) = $field;
|
||||
|
||||
$stemmed_corpus = $stemmer->stemCorpus($corpus);
|
||||
|
||||
queryfx(
|
||||
$conn_w,
|
||||
'INSERT INTO %T (phid, phidType, field, auxPHID, corpus) '.
|
||||
'VALUES (%s, %s, %s, %ns, %s)',
|
||||
'INSERT INTO %T
|
||||
(phid, phidType, field, auxPHID, corpus, stemmedCorpus) '.
|
||||
'VALUES (%s, %s, %s, %ns, %s, %s)',
|
||||
$field_dao->getTableName(),
|
||||
$phid,
|
||||
$doc->getDocumentType(),
|
||||
$ftype,
|
||||
$aux_phid,
|
||||
$corpus);
|
||||
$corpus,
|
||||
$stemmed_corpus);
|
||||
}
|
||||
|
||||
|
||||
|
@ -153,71 +160,112 @@ final class PhabricatorMySQLFulltextStorageEngine
|
|||
}
|
||||
|
||||
public function executeSearch(PhabricatorSavedQuery $query) {
|
||||
$table = new PhabricatorSearchDocument();
|
||||
$document_table = $table->getTableName();
|
||||
$conn = $table->establishConnection('r');
|
||||
|
||||
$subquery = $this->newFulltextSubquery($query, $conn);
|
||||
|
||||
$offset = (int)$query->getParameter('offset', 0);
|
||||
$limit = (int)$query->getParameter('limit', 25);
|
||||
|
||||
// NOTE: We must JOIN the subquery in order to apply a limit.
|
||||
$results = queryfx_all(
|
||||
$conn,
|
||||
'SELECT
|
||||
documentPHID,
|
||||
MAX(fieldScore) AS documentScore
|
||||
FROM (%Q) query
|
||||
JOIN %T root ON query.documentPHID = root.phid
|
||||
GROUP BY documentPHID
|
||||
ORDER BY documentScore DESC
|
||||
LIMIT %d, %d',
|
||||
$subquery,
|
||||
$document_table,
|
||||
$offset,
|
||||
$limit);
|
||||
|
||||
return ipull($results, 'documentPHID');
|
||||
}
|
||||
|
||||
private function newFulltextSubquery(
|
||||
PhabricatorSavedQuery $query,
|
||||
AphrontDatabaseConnection $conn) {
|
||||
|
||||
$field = new PhabricatorSearchDocumentField();
|
||||
$field_table = $field->getTableName();
|
||||
|
||||
$document = new PhabricatorSearchDocument();
|
||||
$document_table = $document->getTableName();
|
||||
|
||||
$select = array();
|
||||
$select[] = 'document.phid AS documentPHID';
|
||||
|
||||
$join = array();
|
||||
$where = array();
|
||||
$join = array();
|
||||
$order = 'ORDER BY documentCreated DESC';
|
||||
|
||||
$dao_doc = new PhabricatorSearchDocument();
|
||||
$dao_field = new PhabricatorSearchDocumentField();
|
||||
|
||||
$t_doc = $dao_doc->getTableName();
|
||||
$t_field = $dao_field->getTableName();
|
||||
|
||||
$conn_r = $dao_doc->establishConnection('r');
|
||||
$title_field = PhabricatorSearchDocumentFieldType::FIELD_TITLE;
|
||||
$title_boost = 1024;
|
||||
|
||||
$raw_query = $query->getParameter('query');
|
||||
$q = $this->compileQuery($raw_query);
|
||||
$compiled_query = $this->compileQuery($raw_query);
|
||||
if (strlen($compiled_query)) {
|
||||
$select[] = qsprintf(
|
||||
$conn,
|
||||
'IF(field.field = %s, %d, 0) +
|
||||
MATCH(corpus, stemmedCorpus) AGAINST (%s IN BOOLEAN MODE)
|
||||
AS fieldScore',
|
||||
$title_field,
|
||||
$title_boost,
|
||||
$compiled_query);
|
||||
|
||||
if (strlen($q)) {
|
||||
$join[] = qsprintf(
|
||||
$conn_r,
|
||||
$join[] = qsprintf(
|
||||
$conn,
|
||||
'%T field ON field.phid = document.phid',
|
||||
$t_field);
|
||||
$field_table);
|
||||
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'MATCH(corpus) AGAINST (%s IN BOOLEAN MODE)',
|
||||
$q);
|
||||
$conn,
|
||||
'MATCH(corpus, stemmedCorpus) AGAINST (%s IN BOOLEAN MODE)',
|
||||
$compiled_query);
|
||||
|
||||
// When searching for a string, promote user listings above other
|
||||
// listings.
|
||||
$order = qsprintf(
|
||||
$conn_r,
|
||||
'ORDER BY
|
||||
IF(documentType = %s, 0, 1) ASC,
|
||||
MAX(MATCH(corpus) AGAINST (%s)) DESC',
|
||||
'USER',
|
||||
$q);
|
||||
|
||||
$field = $query->getParameter('field');
|
||||
if ($field) {
|
||||
if ($query->getParameter('field')) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'field.field = %s',
|
||||
$field);
|
||||
}
|
||||
} else {
|
||||
$select[] = qsprintf(
|
||||
$conn,
|
||||
'document.documentCreated AS fieldScore');
|
||||
}
|
||||
|
||||
$exclude = $query->getParameter('exclude');
|
||||
if ($exclude) {
|
||||
$where[] = qsprintf($conn_r, 'document.phid != %s', $exclude);
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'document.phid != %s',
|
||||
$exclude);
|
||||
}
|
||||
|
||||
$types = $query->getParameter('types');
|
||||
if ($types) {
|
||||
if (strlen($q)) {
|
||||
if (strlen($compiled_query)) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'field.phidType IN (%Ls)',
|
||||
$types);
|
||||
}
|
||||
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'document.documentType IN (%Ls)',
|
||||
$types);
|
||||
}
|
||||
|
||||
$join[] = $this->joinRelationship(
|
||||
$conn_r,
|
||||
$conn,
|
||||
$query,
|
||||
'authorPHIDs',
|
||||
PhabricatorSearchRelationship::RELATIONSHIP_AUTHOR);
|
||||
|
@ -231,14 +279,14 @@ final class PhabricatorMySQLFulltextStorageEngine
|
|||
|
||||
if ($include_open && !$include_closed) {
|
||||
$join[] = $this->joinRelationship(
|
||||
$conn_r,
|
||||
$conn,
|
||||
$query,
|
||||
'statuses',
|
||||
$open_rel,
|
||||
true);
|
||||
} else if ($include_closed && !$include_open) {
|
||||
$join[] = $this->joinRelationship(
|
||||
$conn_r,
|
||||
$conn,
|
||||
$query,
|
||||
'statuses',
|
||||
$closed_rel,
|
||||
|
@ -247,46 +295,47 @@ final class PhabricatorMySQLFulltextStorageEngine
|
|||
|
||||
if ($query->getParameter('withAnyOwner')) {
|
||||
$join[] = $this->joinRelationship(
|
||||
$conn_r,
|
||||
$conn,
|
||||
$query,
|
||||
'withAnyOwner',
|
||||
PhabricatorSearchRelationship::RELATIONSHIP_OWNER,
|
||||
true);
|
||||
} else if ($query->getParameter('withUnowned')) {
|
||||
$join[] = $this->joinRelationship(
|
||||
$conn_r,
|
||||
$conn,
|
||||
$query,
|
||||
'withUnowned',
|
||||
PhabricatorSearchRelationship::RELATIONSHIP_UNOWNED,
|
||||
true);
|
||||
} else {
|
||||
$join[] = $this->joinRelationship(
|
||||
$conn_r,
|
||||
$conn,
|
||||
$query,
|
||||
'ownerPHIDs',
|
||||
PhabricatorSearchRelationship::RELATIONSHIP_OWNER);
|
||||
}
|
||||
|
||||
$join[] = $this->joinRelationship(
|
||||
$conn_r,
|
||||
$conn,
|
||||
$query,
|
||||
'subscriberPHIDs',
|
||||
PhabricatorSearchRelationship::RELATIONSHIP_SUBSCRIBER);
|
||||
|
||||
$join[] = $this->joinRelationship(
|
||||
$conn_r,
|
||||
$conn,
|
||||
$query,
|
||||
'projectPHIDs',
|
||||
PhabricatorSearchRelationship::RELATIONSHIP_PROJECT);
|
||||
|
||||
$join[] = $this->joinRelationship(
|
||||
$conn_r,
|
||||
$conn,
|
||||
$query,
|
||||
'repository',
|
||||
PhabricatorSearchRelationship::RELATIONSHIP_REPOSITORY);
|
||||
|
||||
$join = array_filter($join);
|
||||
$select = implode(', ', $select);
|
||||
|
||||
$join = array_filter($join);
|
||||
foreach ($join as $key => $clause) {
|
||||
$join[$key] = ' JOIN '.$clause;
|
||||
}
|
||||
|
@ -298,27 +347,24 @@ final class PhabricatorMySQLFulltextStorageEngine
|
|||
$where = '';
|
||||
}
|
||||
|
||||
$offset = (int)$query->getParameter('offset', 0);
|
||||
$limit = (int)$query->getParameter('limit', 25);
|
||||
if (strlen($compiled_query)) {
|
||||
$order = '';
|
||||
} else {
|
||||
// When not executing a query, order by document creation date. This
|
||||
// is the default view in object browser dialogs, like "Close Duplicate".
|
||||
$order = qsprintf(
|
||||
$conn,
|
||||
'ORDER BY document.documentCreated DESC');
|
||||
}
|
||||
|
||||
$hits = queryfx_all(
|
||||
$conn_r,
|
||||
'SELECT
|
||||
document.phid
|
||||
FROM %T document
|
||||
%Q
|
||||
%Q
|
||||
GROUP BY document.phid
|
||||
%Q
|
||||
LIMIT %d, %d',
|
||||
$t_doc,
|
||||
return qsprintf(
|
||||
$conn,
|
||||
'SELECT %Q FROM %T document %Q %Q %Q LIMIT 1000',
|
||||
$select,
|
||||
$document_table,
|
||||
$join,
|
||||
$where,
|
||||
$order,
|
||||
$offset,
|
||||
$limit);
|
||||
|
||||
return ipull($hits, 'phid');
|
||||
$order);
|
||||
}
|
||||
|
||||
protected function joinRelationship(
|
||||
|
@ -353,11 +399,17 @@ final class PhabricatorMySQLFulltextStorageEngine
|
|||
}
|
||||
|
||||
private function compileQuery($raw_query) {
|
||||
$compiler = PhabricatorSearchDocument::newQueryCompiler();
|
||||
$stemmer = new PhutilSearchStemmer();
|
||||
|
||||
return $compiler
|
||||
$compiler = PhabricatorSearchDocument::newQueryCompiler()
|
||||
->setQuery($raw_query)
|
||||
->compileQuery();
|
||||
->setStemmer($stemmer);
|
||||
|
||||
$queries = array();
|
||||
$queries[] = $compiler->compileLiteralQuery();
|
||||
$queries[] = $compiler->compileStemmedQuery();
|
||||
|
||||
return implode(' ', array_filter($queries));
|
||||
}
|
||||
|
||||
public function indexExists() {
|
||||
|
|
|
@ -5,6 +5,14 @@ final class PhabricatorSearchSchemaSpec
|
|||
|
||||
public function buildSchemata() {
|
||||
$this->buildEdgeSchemata(new PhabricatorProfilePanelConfiguration());
|
||||
|
||||
$this->buildRawSchema(
|
||||
'search',
|
||||
PhabricatorSearchDocument::STOPWORDS_TABLE,
|
||||
array(
|
||||
'value' => 'sort32',
|
||||
),
|
||||
array());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ final class PhabricatorSearchDocument extends PhabricatorSearchDAO {
|
|||
protected $documentCreated;
|
||||
protected $documentModified;
|
||||
|
||||
const STOPWORDS_TABLE = 'stopwords';
|
||||
|
||||
protected function getConfiguration() {
|
||||
return array(
|
||||
self::CONFIG_TIMESTAMPS => false,
|
||||
|
@ -38,19 +40,46 @@ final class PhabricatorSearchDocument extends PhabricatorSearchDAO {
|
|||
}
|
||||
|
||||
public static function newQueryCompiler() {
|
||||
$table = new self();
|
||||
$conn = $table->establishConnection('r');
|
||||
|
||||
$compiler = new PhutilSearchQueryCompiler();
|
||||
|
||||
$operators = queryfx_one(
|
||||
$conn,
|
||||
'SELECT @@ft_boolean_syntax AS syntax');
|
||||
if ($operators) {
|
||||
$compiler->setOperators($operators['syntax']);
|
||||
if (self::isInnoDBFulltextEngineAvailable()) {
|
||||
// The InnoDB fulltext boolean operators are always the same as the
|
||||
// default MyISAM operators, so we do not need to adjust the compiler.
|
||||
} else {
|
||||
$table = new self();
|
||||
$conn = $table->establishConnection('r');
|
||||
|
||||
$operators = queryfx_one(
|
||||
$conn,
|
||||
'SELECT @@ft_boolean_syntax AS syntax');
|
||||
if ($operators) {
|
||||
$compiler->setOperators($operators['syntax']);
|
||||
}
|
||||
}
|
||||
|
||||
return $compiler;
|
||||
}
|
||||
|
||||
public static function isInnoDBFulltextEngineAvailable() {
|
||||
static $available;
|
||||
|
||||
if ($available === null) {
|
||||
$table = new self();
|
||||
$conn = $table->establishConnection('r');
|
||||
|
||||
// If this system variable exists, we can use InnoDB fulltext. If it
|
||||
// does not, this query will throw and we're stuck with MyISAM.
|
||||
try {
|
||||
queryfx_one(
|
||||
$conn,
|
||||
'SELECT @@innodb_ft_max_token_size');
|
||||
$available = true;
|
||||
} catch (AphrontQueryException $x) {
|
||||
$available = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $available;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ final class PhabricatorSearchDocumentField extends PhabricatorSearchDAO {
|
|||
protected $field;
|
||||
protected $auxPHID;
|
||||
protected $corpus;
|
||||
protected $stemmedCorpus;
|
||||
|
||||
protected function getConfiguration() {
|
||||
return array(
|
||||
|
@ -16,14 +17,15 @@ final class PhabricatorSearchDocumentField extends PhabricatorSearchDAO {
|
|||
'field' => 'text4',
|
||||
'auxPHID' => 'phid?',
|
||||
'corpus' => 'fulltext?',
|
||||
'stemmedCorpus' => 'fulltext?',
|
||||
),
|
||||
self::CONFIG_KEY_SCHEMA => array(
|
||||
'key_phid' => null,
|
||||
'phid' => array(
|
||||
'columns' => array('phid'),
|
||||
),
|
||||
'corpus' => array(
|
||||
'columns' => array('corpus'),
|
||||
'key_corpus' => array(
|
||||
'columns' => array('corpus', 'stemmedCorpus'),
|
||||
'type' => 'FULLTEXT',
|
||||
),
|
||||
),
|
||||
|
|
|
@ -502,6 +502,15 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
return false;
|
||||
}
|
||||
|
||||
$type = $xaction->getTransactionType();
|
||||
$xtype = $this->getModularTransactionType($type);
|
||||
if ($xtype) {
|
||||
return $xtype->getTransactionHasEffect(
|
||||
$object,
|
||||
$xaction->getOldValue(),
|
||||
$xaction->getNewValue());
|
||||
}
|
||||
|
||||
return ($xaction->getOldValue() !== $xaction->getNewValue());
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,10 @@ abstract class PhabricatorModularTransactionType
|
|||
return;
|
||||
}
|
||||
|
||||
public function getTransactionHasEffect($object, $old, $new) {
|
||||
return ($old !== $new);
|
||||
}
|
||||
|
||||
public function extractFilePHIDs($object, $value) {
|
||||
return array();
|
||||
}
|
||||
|
|
|
@ -635,7 +635,7 @@ final class PhabricatorDatabaseRef
|
|||
$application_replicas = array();
|
||||
$default_replicas = array();
|
||||
foreach ($replicas as $replica) {
|
||||
$master = $replica->getMaster();
|
||||
$master = $replica->getMasterRef();
|
||||
|
||||
if ($master->isApplicationHost($application)) {
|
||||
$application_replicas[] = $replica;
|
||||
|
|
|
@ -77,6 +77,17 @@ final class PhabricatorStorageManagementUpgradeWorkflow
|
|||
|
||||
$this->upgradeSchemata($apis, $apply_only, $no_quickstart, $init_only);
|
||||
|
||||
if ($apply_only || $init_only) {
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht('Declining to synchronize static tables.'));
|
||||
} else {
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht('Synchronizing static tables...'));
|
||||
$this->synchronizeSchemata();
|
||||
}
|
||||
|
||||
if ($no_adjust || $init_only || $apply_only) {
|
||||
$console->writeOut(
|
||||
"%s\n",
|
||||
|
@ -93,4 +104,46 @@ final class PhabricatorStorageManagementUpgradeWorkflow
|
|||
return 0;
|
||||
}
|
||||
|
||||
private function synchronizeSchemata() {
|
||||
// Synchronize the InnoDB fulltext stopwords table from the stopwords file
|
||||
// on disk.
|
||||
|
||||
$table = new PhabricatorSearchDocument();
|
||||
$conn = $table->establishConnection('w');
|
||||
$table_ref = PhabricatorSearchDocument::STOPWORDS_TABLE;
|
||||
|
||||
$stopwords_database = queryfx_all(
|
||||
$conn,
|
||||
'SELECT value FROM %T',
|
||||
$table_ref);
|
||||
$stopwords_database = ipull($stopwords_database, 'value', 'value');
|
||||
|
||||
$stopwords_path = phutil_get_library_root('phabricator');
|
||||
$stopwords_path = $stopwords_path.'/../resources/sql/stopwords.txt';
|
||||
$stopwords_file = Filesystem::readFile($stopwords_path);
|
||||
$stopwords_file = phutil_split_lines($stopwords_file, false);
|
||||
$stopwords_file = array_fuse($stopwords_file);
|
||||
|
||||
$rem_words = array_diff_key($stopwords_database, $stopwords_file);
|
||||
if ($rem_words) {
|
||||
queryfx(
|
||||
$conn,
|
||||
'DELETE FROM %T WHERE value IN (%Ls)',
|
||||
$table_ref,
|
||||
$rem_words);
|
||||
}
|
||||
|
||||
$add_words = array_diff_key($stopwords_file, $stopwords_database);
|
||||
if ($add_words) {
|
||||
foreach ($add_words as $word) {
|
||||
queryfx(
|
||||
$conn,
|
||||
'INSERT IGNORE INTO %T (value) VALUES (%s)',
|
||||
$table_ref,
|
||||
$word);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -308,10 +308,11 @@ abstract class PhabricatorStorageManagementWorkflow
|
|||
if ($phase == 'main') {
|
||||
queryfx(
|
||||
$conn,
|
||||
'ALTER TABLE %T.%T COLLATE = %s',
|
||||
'ALTER TABLE %T.%T COLLATE = %s, ENGINE = %s',
|
||||
$adjust['database'],
|
||||
$adjust['table'],
|
||||
$adjust['collation']);
|
||||
$adjust['collation'],
|
||||
$adjust['engine']);
|
||||
}
|
||||
break;
|
||||
case 'column':
|
||||
|
@ -480,6 +481,7 @@ abstract class PhabricatorStorageManagementWorkflow
|
|||
$issue_unique = PhabricatorConfigStorageSchema::ISSUE_UNIQUE;
|
||||
$issue_longkey = PhabricatorConfigStorageSchema::ISSUE_LONGKEY;
|
||||
$issue_auto = PhabricatorConfigStorageSchema::ISSUE_AUTOINCREMENT;
|
||||
$issue_engine = PhabricatorConfigStorageSchema::ISSUE_ENGINE;
|
||||
|
||||
$adjustments = array();
|
||||
$errors = array();
|
||||
|
@ -543,6 +545,10 @@ abstract class PhabricatorStorageManagementWorkflow
|
|||
$issues[] = $issue_collation;
|
||||
}
|
||||
|
||||
if ($table->hasIssue($issue_engine)) {
|
||||
$issues[] = $issue_engine;
|
||||
}
|
||||
|
||||
if ($issues) {
|
||||
$adjustments[] = array(
|
||||
'kind' => 'table',
|
||||
|
@ -550,6 +556,7 @@ abstract class PhabricatorStorageManagementWorkflow
|
|||
'table' => $table_name,
|
||||
'issues' => $issues,
|
||||
'collation' => $expect_table->getCollation(),
|
||||
'engine' => $expect_table->getEngine(),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -922,6 +929,7 @@ abstract class PhabricatorStorageManagementWorkflow
|
|||
}
|
||||
|
||||
$applied_map = array();
|
||||
$state_map = array();
|
||||
foreach ($api_map as $ref_key => $api) {
|
||||
$applied = $api->getAppliedPatches();
|
||||
|
||||
|
@ -939,6 +947,7 @@ abstract class PhabricatorStorageManagementWorkflow
|
|||
}
|
||||
|
||||
$applied = array_fuse($applied);
|
||||
$state_map[$ref_key] = $applied;
|
||||
|
||||
if ($apply_only) {
|
||||
if (isset($applied[$apply_only])) {
|
||||
|
@ -1090,7 +1099,7 @@ abstract class PhabricatorStorageManagementWorkflow
|
|||
|
||||
// If we're explicitly reapplying this patch, we don't need to
|
||||
// mark it as applied.
|
||||
if (!isset($applied_map[$ref_key][$key])) {
|
||||
if (!isset($state_map[$ref_key][$key])) {
|
||||
$api->markPatchApplied($key, ($t_end - $t_begin));
|
||||
$applied_map[$ref_key][$key] = true;
|
||||
}
|
||||
|
|
|
@ -2,11 +2,17 @@
|
|||
|
||||
final class PhabricatorLocalTimeTestCase extends PhabricatorTestCase {
|
||||
|
||||
protected function getPhabricatorTestCaseConfiguration() {
|
||||
return array(
|
||||
self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES => true,
|
||||
);
|
||||
}
|
||||
|
||||
public function testLocalTimeFormatting() {
|
||||
$user = new PhabricatorUser();
|
||||
$user = $this->generateNewTestUser();
|
||||
$user->overrideTimezoneIdentifier('America/Los_Angeles');
|
||||
|
||||
$utc = new PhabricatorUser();
|
||||
$utc = $this->generateNewTestUser();
|
||||
$utc->overrideTimezoneIdentifier('UTC');
|
||||
|
||||
$this->assertEqual(
|
||||
|
|
|
@ -8,6 +8,7 @@ final class PhabricatorFileLinkView extends AphrontView {
|
|||
private $fileViewable;
|
||||
private $filePHID;
|
||||
private $fileMonogram;
|
||||
private $fileSize;
|
||||
private $customClass;
|
||||
|
||||
public function setCustomClass($custom_class) {
|
||||
|
@ -73,6 +74,19 @@ final class PhabricatorFileLinkView extends AphrontView {
|
|||
return $this->fileName;
|
||||
}
|
||||
|
||||
public function setFileSize($file_size) {
|
||||
$this->fileSize = $file_size;
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function getFileSize() {
|
||||
return $this->fileSize;
|
||||
}
|
||||
|
||||
private function getFileIcon() {
|
||||
return FileTypeIcon::getFileIcon($this->getFileName());
|
||||
}
|
||||
|
||||
public function getMetadata() {
|
||||
return array(
|
||||
'phid' => $this->getFilePHID(),
|
||||
|
@ -81,6 +95,8 @@ final class PhabricatorFileLinkView extends AphrontView {
|
|||
'dUri' => $this->getFileDownloadURI(),
|
||||
'name' => $this->getFileName(),
|
||||
'monogram' => $this->getFileMonogram(),
|
||||
'icon' => $this->getFileIcon(),
|
||||
'size' => $this->getFileSize(),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -98,7 +114,31 @@ final class PhabricatorFileLinkView extends AphrontView {
|
|||
}
|
||||
|
||||
$icon = id(new PHUIIconView())
|
||||
->setIcon('fa-file-text-o');
|
||||
->setIcon($this->getFileIcon());
|
||||
|
||||
$info = phutil_tag(
|
||||
'span',
|
||||
array(
|
||||
'class' => 'phabricator-remarkup-embed-layout-info',
|
||||
),
|
||||
$this->getFileSize());
|
||||
|
||||
$name = phutil_tag(
|
||||
'span',
|
||||
array(
|
||||
'class' => 'phabricator-remarkup-embed-layout-name',
|
||||
),
|
||||
$this->getFileName());
|
||||
|
||||
$inner = phutil_tag(
|
||||
'span',
|
||||
array(
|
||||
'class' => 'phabricator-remarkup-embed-layout-info-block',
|
||||
),
|
||||
array(
|
||||
$name,
|
||||
$info,
|
||||
));
|
||||
|
||||
return javelin_tag(
|
||||
'a',
|
||||
|
@ -111,7 +151,7 @@ final class PhabricatorFileLinkView extends AphrontView {
|
|||
),
|
||||
array(
|
||||
$icon,
|
||||
$this->getFileName(),
|
||||
$inner,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -277,16 +277,17 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView
|
|||
'action' => '#',
|
||||
'method' => 'POST',
|
||||
'class' => 'lightbox-download-form',
|
||||
'sigil' => 'download',
|
||||
'sigil' => 'download lightbox-download-submit',
|
||||
'id' => 'lightbox-download-form',
|
||||
),
|
||||
phutil_tag(
|
||||
'button',
|
||||
'a',
|
||||
array(
|
||||
'class' => 'button grey has-icon',
|
||||
'class' => 'lightbox-download phui-icon-circle hover-green',
|
||||
'href' => '#',
|
||||
),
|
||||
array(
|
||||
$icon,
|
||||
pht('Download'),
|
||||
)));
|
||||
|
||||
Javelin::initBehavior(
|
||||
|
@ -511,6 +512,7 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView
|
|||
'div',
|
||||
array(
|
||||
'class' => implode(' ', $classes),
|
||||
'id' => 'main-page-frame',
|
||||
),
|
||||
array(
|
||||
$main_page,
|
||||
|
|
|
@ -370,20 +370,41 @@ video.phabricator-media {
|
|||
}
|
||||
|
||||
.phabricator-remarkup-embed-layout-link {
|
||||
padding: 2px 0;
|
||||
padding: 8px 8px 8px 32px;
|
||||
border-radius: 3px;
|
||||
margin: 0;
|
||||
margin: 0 0 4px;
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
color: {$anchor};
|
||||
-webkit-font-smoothing: antialiased;
|
||||
border: 1px solid {$lightblueborder};
|
||||
border-radius: 3px;
|
||||
color: #000;
|
||||
min-width: 240px;
|
||||
position: relative;
|
||||
height: 22px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.phabricator-remarkup-embed-layout-link .phui-icon-view {
|
||||
margin-right: 8px;
|
||||
font-size: 20px;
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
left: 8px;
|
||||
}
|
||||
|
||||
.phabricator-remarkup-embed-layout-info {
|
||||
color: {$lightgreytext};
|
||||
font-size: {$smallerfontsize};
|
||||
font-weight: normal;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.phabricator-remarkup-embed-layout-link:hover {
|
||||
border-color: {$violet};
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.phabricator-remarkup-embed-layout-link:hover,
|
||||
.phabricator-remarkup-embed-layout-link:hover .phui-icon-view {
|
||||
color: {$violet};
|
||||
}
|
||||
|
|
|
@ -26,6 +26,11 @@
|
|||
margin-left: 40px;
|
||||
}
|
||||
|
||||
.device-phone .phui-comment-panel .phui-timeline-view
|
||||
.phui-timeline-event-view {
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
.device-desktop .phui-comment-panel .phui-timeline-view .phui-timeline-image {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
|
@ -73,6 +78,10 @@
|
|||
border: none;
|
||||
}
|
||||
|
||||
.phui-comment-panel .phui-timeline-badges {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.lightbox-comment-form .phui-form-view {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
|
|
@ -116,6 +116,7 @@ select {
|
|||
border: 1px solid {$greyborder};
|
||||
height: 28px;
|
||||
padding: 0 24px 0 8px;
|
||||
margin: 0;
|
||||
min-width: 180px;
|
||||
}
|
||||
|
||||
|
|
|
@ -49,6 +49,8 @@ a.phui-icon-view:hover {
|
|||
text-align: center;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.phui-icon-circle.circle-medium {
|
||||
|
@ -111,6 +113,16 @@ a.phui-icon-circle.hover-green:hover .phui-icon-view {
|
|||
color: {$green};
|
||||
}
|
||||
|
||||
a.phui-icon-circle.hover-red:hover {
|
||||
text-decoration: none;
|
||||
border-color: {$red};
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
a.phui-icon-circle.hover-red:hover .phui-icon-view {
|
||||
color: {$red};
|
||||
}
|
||||
|
||||
/* - Icon in a Square ------------------------------------------------------- */
|
||||
|
||||
.phui-icon-view.phui-icon-square {
|
||||
|
|
|
@ -43,7 +43,6 @@
|
|||
.lightbox-comment-frame {
|
||||
position: absolute;
|
||||
top: -19999px;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
opacity: 0;
|
||||
transition: all 0.3s;
|
||||
|
@ -56,6 +55,13 @@
|
|||
display: block;
|
||||
height: 120px;
|
||||
width: 320px;
|
||||
color: {$darkbluetext};
|
||||
}
|
||||
|
||||
.lightbox-attachment .lightbox-icon-frame:hover,
|
||||
.lightbox-attachment .lightbox-icon-frame:hover .phui-icon-view {
|
||||
color: {$anchor};
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.lightbox-attachment.comment-panel-open .lightbox-icon-frame {
|
||||
|
@ -78,6 +84,11 @@
|
|||
opacity: 1;
|
||||
}
|
||||
|
||||
.device-phone .comment-panel-open .lightbox-comment-frame {
|
||||
width: auto;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.jx-mask + .lightbox-attachment {
|
||||
background: {$lightgreybackground};
|
||||
}
|
||||
|
@ -101,18 +112,26 @@
|
|||
color: {$greytext};
|
||||
}
|
||||
|
||||
.device-phone .lightbox-attachment .lightbox-status {
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
.lightbox-attachment .lightbox-status .lightbox-download {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.lightbox-attachment .lightbox-status a {
|
||||
.lightbox-attachment .lightbox-status-txt a {
|
||||
color: #000;
|
||||
margin-right: 4px;
|
||||
margin-right: 12px;
|
||||
font-size: {$biggerfontsize};
|
||||
}
|
||||
|
||||
.lightbox-download button.has-icon {
|
||||
padding-left: 28px;
|
||||
.lightbox-attachment .lightbox-status .phui-icon-view {
|
||||
height: 18px;
|
||||
width: 24px;
|
||||
font-size: 14px;
|
||||
line-height: 23px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.lightbox-attachment .lightbox-status .lightbox-download
|
||||
|
@ -120,20 +139,17 @@
|
|||
display: inline;
|
||||
}
|
||||
|
||||
.lightbox-attachment .lightbox-comment {
|
||||
.lightbox-attachment a.lightbox-download,
|
||||
.lightbox-attachment a.lightbox-comment,
|
||||
.lightbox-attachment a.lightbox-close {
|
||||
float: right;
|
||||
margin: 9px 0 0 8px;
|
||||
padding-left: 28px;
|
||||
}
|
||||
|
||||
.lightbox-attachment.comment-panel-open .lightbox-comment,
|
||||
.lightbox-attachment.comment-panel-open .lightbox-comment.phui-icon-circle,
|
||||
.lightbox-attachment.comment-panel-open .lightbox-comment .phui-icon-view {
|
||||
color: {$sky};
|
||||
}
|
||||
|
||||
.lightbox-attachment .lightbox-close {
|
||||
float: right;
|
||||
margin: 9px 0 0 8px;
|
||||
border-color: {$sky};
|
||||
}
|
||||
|
||||
.lightbox-attachment .lightbox-left {
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
* @provides phui-workboard-color-css
|
||||
*/
|
||||
|
||||
.phui-workboard-color .phabricator-nav-content .phui-workboard-view-shadow {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.phui-workboard-color .phui-crumbs-view {
|
||||
background-color: rgba({$alphagrey},.15);
|
||||
border: none;
|
||||
|
@ -26,12 +30,36 @@
|
|||
background-color: rgba({$alphawhite},.6);
|
||||
}
|
||||
|
||||
body.phui-workboard-color .phui-profile-menu .phabricator-side-menu {
|
||||
background-color: rgba({$alphagrey},.3);
|
||||
}
|
||||
|
||||
body.phui-workboard-color .phabricator-side-menu .phui-profile-menu-footer-1 {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.phui-workboard-color .phui-profile-menu .phabricator-side-menu {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.phui-workboard-color-preview {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
font-size: 34px;
|
||||
}
|
||||
|
||||
.phui-workboard-color .phui-profile-menu .phabricator-side-menu
|
||||
.phui-list-item-href {
|
||||
color: rgba({$alphawhite},.8);
|
||||
}
|
||||
|
||||
.phui-workboard-color .phui-profile-menu .phabricator-side-menu
|
||||
.phui-list-item-icon,
|
||||
.phui-workboard-color .phui-profile-menu .phabricator-side-menu
|
||||
.phui-list-item-href .phui-list-item-icon {
|
||||
color: rgba({$alphawhite},.8);
|
||||
}
|
||||
|
||||
/* Gradients */
|
||||
|
||||
.phui-workboard-gradient-red {
|
||||
|
|
|
@ -31,6 +31,15 @@
|
|||
background: {$lightbluetext};
|
||||
}
|
||||
|
||||
.device-desktop .project-board-wrapper .phui-workboard-view-shadow {
|
||||
left: {$menu.profile.width};
|
||||
}
|
||||
|
||||
.device-desktop .phui-profile-menu-collapsed .project-board-wrapper
|
||||
.phui-workboard-view-shadow {
|
||||
left: {$menu.profile.width.collapsed};
|
||||
}
|
||||
|
||||
!print .project-board-wrapper .phui-workboard-view-shadow {
|
||||
position: static;
|
||||
}
|
||||
|
@ -65,15 +74,21 @@
|
|||
display: none;
|
||||
}
|
||||
|
||||
.device-desktop .phui-workboard-fullscreen .phui-workboard-view-shadow {
|
||||
top: 35px;
|
||||
left: 0;
|
||||
.device-desktop .phui-workboard-fullscreen .phui-profile-menu
|
||||
.phui-workboard-view-shadow {
|
||||
top: 35px;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.device-desktop .phui-workboard-fullscreen .phui-workpanel-body-content {
|
||||
max-height: calc(100vh - 120px);
|
||||
}
|
||||
|
||||
.device-desktop .phui-workboard-fullscreen .phui-profile-menu
|
||||
.phabricator-nav-local {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.device .phui-workboard-expand-icon {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -468,6 +468,7 @@ JX.install('ConpherenceThreadManager', {
|
|||
}
|
||||
}));
|
||||
this.syncWorkflow(workflow, 'finally');
|
||||
textarea.value = '';
|
||||
|
||||
this._willSendMessageCallback();
|
||||
},
|
||||
|
|
|
@ -50,7 +50,8 @@ JX.behavior('lightbox-attachments', function (config) {
|
|||
|
||||
e.kill();
|
||||
|
||||
var links = JX.DOM.scry(document, 'a', 'lightboxable');
|
||||
var mainFrame = JX.$('main-page-frame');
|
||||
var links = JX.DOM.scry(mainFrame, 'a', 'lightboxable');
|
||||
var phids = {};
|
||||
var data;
|
||||
for (var i = 0; i < links.length; i++) {
|
||||
|
@ -99,7 +100,7 @@ JX.behavior('lightbox-attachments', function (config) {
|
|||
);
|
||||
} else {
|
||||
var imgIcon = new JX.PHUIXIconView()
|
||||
.setIcon('fa-file-text-o phui-lightbox-file-icon')
|
||||
.setIcon(target_data.icon + ' phui-lightbox-file-icon')
|
||||
.getNode();
|
||||
var nameElement =
|
||||
JX.$N('div',
|
||||
|
@ -109,9 +110,11 @@ JX.behavior('lightbox-attachments', function (config) {
|
|||
target_data.name
|
||||
);
|
||||
img =
|
||||
JX.$N('div',
|
||||
JX.$N('a',
|
||||
{
|
||||
className : 'lightbox-icon-frame',
|
||||
sigil : 'lightbox-download-submit',
|
||||
href : '#',
|
||||
},
|
||||
[ imgIcon, nameElement ]
|
||||
);
|
||||
|
@ -121,6 +124,7 @@ JX.behavior('lightbox-attachments', function (config) {
|
|||
JX.$N('div',
|
||||
{
|
||||
className : 'lightbox-image-frame',
|
||||
sigil : 'lightbox-image-frame',
|
||||
},
|
||||
img
|
||||
);
|
||||
|
@ -153,7 +157,7 @@ JX.behavior('lightbox-attachments', function (config) {
|
|||
},
|
||||
[
|
||||
m_url,
|
||||
' Image ' + current + ' of ' + total + '.'
|
||||
current + ' / ' + total
|
||||
]
|
||||
);
|
||||
|
||||
|
@ -165,24 +169,27 @@ JX.behavior('lightbox-attachments', function (config) {
|
|||
);
|
||||
|
||||
var commentIcon = new JX.PHUIXIconView()
|
||||
.setIcon('fa-comment-o')
|
||||
.setIcon('fa-comments')
|
||||
.getNode();
|
||||
var commentButton =
|
||||
JX.$N('a',
|
||||
{
|
||||
className : 'lightbox-comment button grey has-icon',
|
||||
className : 'lightbox-comment phui-icon-circle hover-sky',
|
||||
href : '#',
|
||||
sigil : 'lightbox-comment'
|
||||
},
|
||||
[commentIcon, 'Comment']
|
||||
commentIcon
|
||||
);
|
||||
var closeIcon = new JX.PHUIXIconView()
|
||||
.setIcon('fa-times')
|
||||
.getNode();
|
||||
var closeButton =
|
||||
JX.$N('a',
|
||||
{
|
||||
className : 'lightbox-close button grey',
|
||||
className : 'lightbox-close phui-icon-circle hover-red',
|
||||
href : '#'
|
||||
},
|
||||
'Close');
|
||||
closeIcon);
|
||||
var statusHTML =
|
||||
JX.$N('div',
|
||||
{
|
||||
|
@ -311,7 +318,9 @@ JX.behavior('lightbox-attachments', function (config) {
|
|||
el.click();
|
||||
}
|
||||
|
||||
JX.Stratcom.listen(
|
||||
// Only look for lightboxable inside the main page, not other lightboxes.
|
||||
JX.DOM.listen(
|
||||
JX.$('main-page-frame'),
|
||||
'click',
|
||||
['lightboxable'],
|
||||
loadLightBox);
|
||||
|
@ -324,12 +333,12 @@ JX.behavior('lightbox-attachments', function (config) {
|
|||
// When the user clicks the background, close the lightbox.
|
||||
JX.Stratcom.listen(
|
||||
'click',
|
||||
'lightbox-attachment',
|
||||
'lightbox-image-frame',
|
||||
function (e) {
|
||||
if (!lightbox) {
|
||||
return;
|
||||
}
|
||||
if (e.getTarget() != e.getNode('lightbox-attachment')) {
|
||||
if (e.getTarget() != e.getNode('lightbox-image-frame')) {
|
||||
// Don't close if they clicked some other element, like the image
|
||||
// itself or the next/previous arrows.
|
||||
return;
|
||||
|
@ -356,4 +365,15 @@ JX.behavior('lightbox-attachments', function (config) {
|
|||
'lightbox-comment-form',
|
||||
_sendMessage);
|
||||
|
||||
var _startDownload = function(e) {
|
||||
e.kill();
|
||||
var form = JX.$('lightbox-download-form');
|
||||
form.submit();
|
||||
};
|
||||
|
||||
JX.Stratcom.listen(
|
||||
'click',
|
||||
'lightbox-download-submit',
|
||||
_startDownload);
|
||||
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue