diff --git a/src/applications/base/controller/PhabricatorController.php b/src/applications/base/controller/PhabricatorController.php index 12d71357c6..989855d0cc 100644 --- a/src/applications/base/controller/PhabricatorController.php +++ b/src/applications/base/controller/PhabricatorController.php @@ -1,564 +1,564 @@ shouldRequireLogin()) { return false; } if (!$this->shouldRequireEnabledUser()) { return false; } if ($this->shouldAllowPartialSessions()) { return false; } $user = $this->getRequest()->getUser(); if (!$user->getIsStandardUser()) { return false; } return PhabricatorEnv::getEnvConfig('security.require-multi-factor-auth'); } public function willBeginExecution() { $request = $this->getRequest(); if ($request->getUser()) { // NOTE: Unit tests can set a user explicitly. Normal requests are not // permitted to do this. PhabricatorTestCase::assertExecutingUnitTests(); $user = $request->getUser(); } else { $user = new PhabricatorUser(); $session_engine = new PhabricatorAuthSessionEngine(); $phsid = $request->getCookie(PhabricatorCookies::COOKIE_SESSION); if (strlen($phsid)) { $session_user = $session_engine->loadUserForSession( PhabricatorAuthSession::TYPE_WEB, $phsid); if ($session_user) { $user = $session_user; } } else { // If the client doesn't have a session token, generate an anonymous // session. This is used to provide CSRF protection to logged-out users. $phsid = $session_engine->establishSession( PhabricatorAuthSession::TYPE_WEB, null, $partial = false); // This may be a resource request, in which case we just don't set // the cookie. if ($request->canSetCookies()) { $request->setCookie(PhabricatorCookies::COOKIE_SESSION, $phsid); } } if (!$user->isLoggedIn()) { $user->attachAlternateCSRFString(PhabricatorHash::digest($phsid)); } $request->setUser($user); } $translation = $user->getTranslation(); if ($translation && $translation != PhabricatorEnv::getEnvConfig('translation.provider')) { $translation = newv($translation, array()); PhutilTranslator::getInstance() ->setLanguage($translation->getLanguage()) ->addTranslations($translation->getTranslations()); } $preferences = $user->loadPreferences(); if (PhabricatorEnv::getEnvConfig('darkconsole.enabled')) { $dark_console = PhabricatorUserPreferences::PREFERENCE_DARK_CONSOLE; if ($preferences->getPreference($dark_console) || PhabricatorEnv::getEnvConfig('darkconsole.always-on')) { $console = new DarkConsoleCore(); $request->getApplicationConfiguration()->setConsole($console); } } // NOTE: We want to set up the user first so we can render a real page // here, but fire this before any real logic. $restricted = array( 'code', ); foreach ($restricted as $parameter) { if ($request->getExists($parameter)) { if (!$this->shouldAllowRestrictedParameter($parameter)) { throw new Exception( pht( 'Request includes restricted parameter "%s", but this '. 'controller ("%s") does not whitelist it. Refusing to '. 'serve this request because it might be part of a redirection '. 'attack.', $parameter, get_class($this))); } } } if ($this->shouldRequireEnabledUser()) { if ($user->isLoggedIn() && !$user->getIsApproved()) { $controller = new PhabricatorAuthNeedsApprovalController(); return $this->delegateToController($controller); } if ($user->getIsDisabled()) { $controller = new PhabricatorDisabledUserController(); return $this->delegateToController($controller); } } $event = new PhabricatorEvent( PhabricatorEventType::TYPE_CONTROLLER_CHECKREQUEST, array( 'request' => $request, 'controller' => $this, )); $event->setUser($user); PhutilEventEngine::dispatchEvent($event); $checker_controller = $event->getValue('controller'); if ($checker_controller != $this) { return $this->delegateToController($checker_controller); } $auth_class = 'PhabricatorAuthApplication'; $auth_application = PhabricatorApplication::getByClass($auth_class); // Require partial sessions to finish login before doing anything. if (!$this->shouldAllowPartialSessions()) { if ($user->hasSession() && $user->getSession()->getIsPartial()) { $login_controller = new PhabricatorAuthFinishController(); $this->setCurrentApplication($auth_application); return $this->delegateToController($login_controller); } } // Check if the user needs to configure MFA. $need_mfa = $this->shouldRequireMultiFactorEnrollment(); $have_mfa = $user->getIsEnrolledInMultiFactor(); if ($need_mfa && !$have_mfa) { // Check if the cache is just out of date. Otherwise, roadblock the user // and require MFA enrollment. $user->updateMultiFactorEnrollment(); if (!$user->getIsEnrolledInMultiFactor()) { $mfa_controller = new PhabricatorAuthNeedsMultiFactorController(); $this->setCurrentApplication($auth_application); return $this->delegateToController($mfa_controller); } } if ($this->shouldRequireLogin()) { // This actually means we need either: // - a valid user, or a public controller; and // - permission to see the application. $allow_public = $this->shouldAllowPublic() && PhabricatorEnv::getEnvConfig('policy.allow-public'); // If this controller isn't public, and the user isn't logged in, require // login. if (!$allow_public && !$user->isLoggedIn()) { $login_controller = new PhabricatorAuthStartController(); $this->setCurrentApplication($auth_application); return $this->delegateToController($login_controller); } if ($user->isLoggedIn()) { if ($this->shouldRequireEmailVerification()) { if (!$user->getIsEmailVerified()) { $controller = new PhabricatorMustVerifyEmailController(); $this->setCurrentApplication($auth_application); return $this->delegateToController($controller); } } } // If the user doesn't have access to the application, don't let them use // any of its controllers. We query the application in order to generate // a policy exception if the viewer doesn't have permission. $application = $this->getCurrentApplication(); if ($application) { id(new PhabricatorApplicationQuery()) ->setViewer($user) ->withPHIDs(array($application->getPHID())) ->executeOne(); } } // NOTE: We do this last so that users get a login page instead of a 403 // if they need to login. if ($this->shouldRequireAdmin() && !$user->getIsAdmin()) { return new Aphront403Response(); } } public function buildStandardPageView() { $view = new PhabricatorStandardPageView(); $view->setRequest($this->getRequest()); $view->setController($this); return $view; } public function buildStandardPageResponse($view, array $data) { $page = $this->buildStandardPageView(); $page->appendChild($view); $response = new AphrontWebpageResponse(); $response->setContent($page->render()); return $response; } public function getApplicationURI($path = '') { if (!$this->getCurrentApplication()) { throw new Exception('No application!'); } return $this->getCurrentApplication()->getApplicationURI($path); } public function buildApplicationPage($view, array $options) { $page = $this->buildStandardPageView(); $title = PhabricatorEnv::getEnvConfig('phabricator.serious-business') ? 'Phabricator' : pht('Bacon Ice Cream for Breakfast'); $application = $this->getCurrentApplication(); $page->setTitle(idx($options, 'title', $title)); if ($application) { $page->setApplicationName($application->getName()); if ($application->getTitleGlyph()) { $page->setGlyph($application->getTitleGlyph()); } } if (!($view instanceof AphrontSideNavFilterView)) { $nav = new AphrontSideNavFilterView(); $nav->appendChild($view); $view = $nav; } $user = $this->getRequest()->getUser(); $view->setUser($user); $page->appendChild($view); $object_phids = idx($options, 'pageObjects', array()); if ($object_phids) { $page->appendPageObjects($object_phids); foreach ($object_phids as $object_phid) { PhabricatorFeedStoryNotification::updateObjectNotificationViews( $user, $object_phid); } } if (idx($options, 'device', true)) { $page->setDeviceReady(true); } $page->setShowFooter(idx($options, 'showFooter', true)); $page->setShowChrome(idx($options, 'chrome', true)); $application_menu = $this->buildApplicationMenu(); if ($application_menu) { $page->setApplicationMenu($application_menu); } $response = new AphrontWebpageResponse(); return $response->setContent($page->render()); } public function didProcessRequest($response) { // If a bare DialogView is returned, wrap it in a DialogResponse. if ($response instanceof AphrontDialogView) { $response = id(new AphrontDialogResponse())->setDialog($response); } $request = $this->getRequest(); $response->setRequest($request); $seen = array(); while ($response instanceof AphrontProxyResponse) { $hash = spl_object_hash($response); if (isset($seen[$hash])) { $seen[] = get_class($response); throw new Exception( 'Cycle while reducing proxy responses: '. implode(' -> ', $seen)); } $seen[$hash] = get_class($response); $response = $response->reduceProxyResponse(); } if ($response instanceof AphrontDialogResponse) { if (!$request->isAjax()) { $dialog = $response->getDialog(); $title = $dialog->getTitle(); $short = $dialog->getShortTitle(); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(coalesce($short, $title)); $page_content = array( $crumbs, $response->buildResponseString(), ); $view = id(new PhabricatorStandardPageView()) ->setRequest($request) ->setController($this) ->setDeviceReady(true) ->setTitle($title) ->appendChild($page_content); $response = id(new AphrontWebpageResponse()) ->setContent($view->render()) ->setHTTPResponseCode($response->getHTTPResponseCode()); } else { $response->getDialog()->setIsStandalone(true); return id(new AphrontAjaxResponse()) ->setContent(array( 'dialog' => $response->buildResponseString(), )); } } else if ($response instanceof AphrontRedirectResponse) { if ($request->isAjax()) { return id(new AphrontAjaxResponse()) ->setContent( array( 'redirect' => $response->getURI(), )); } } return $response; } protected function getHandle($phid) { if (empty($this->handles[$phid])) { throw new Exception( "Attempting to access handle which wasn't loaded: {$phid}"); } return $this->handles[$phid]; } protected function loadHandles(array $phids) { $phids = array_filter($phids); $this->handles = $this->loadViewerHandles($phids); return $this; } protected function getLoadedHandles() { return $this->handles; } protected function loadViewerHandles(array $phids) { return id(new PhabricatorHandleQuery()) ->setViewer($this->getRequest()->getUser()) ->withPHIDs($phids) ->execute(); } /** * Render a list of links to handles, identified by PHIDs. The handles must * already be loaded. * * @param list List of PHIDs to render links to. * @param string Style, one of "\n" (to put each item on its own line) * or "," (to list items inline, separated by commas). * @return string Rendered list of handle links. */ protected function renderHandlesForPHIDs(array $phids, $style = "\n") { $style_map = array( "\n" => phutil_tag('br'), ',' => ', ', ); if (empty($style_map[$style])) { throw new Exception("Unknown handle list style '{$style}'!"); } return implode_selected_handle_links($style_map[$style], $this->getLoadedHandles(), array_filter($phids)); } protected function buildApplicationMenu() { return null; } protected function buildApplicationCrumbs() { $crumbs = array(); $application = $this->getCurrentApplication(); if ($application) { $sprite = $application->getIconName(); if (!$sprite) { $sprite = 'application'; } $crumbs[] = id(new PhabricatorCrumbView()) ->setHref($this->getApplicationURI()) ->setAural($application->getName()) ->setIcon($sprite); } $view = new PhabricatorCrumbsView(); foreach ($crumbs as $crumb) { $view->addCrumb($crumb); } return $view; } protected function hasApplicationCapability($capability) { return PhabricatorPolicyFilter::hasCapability( $this->getRequest()->getUser(), $this->getCurrentApplication(), $capability); } protected function requireApplicationCapability($capability) { PhabricatorPolicyFilter::requireCapability( $this->getRequest()->getUser(), $this->getCurrentApplication(), $capability); } protected function explainApplicationCapability( $capability, $positive_message, $negative_message) { $can_act = $this->hasApplicationCapability($capability); if ($can_act) { $message = $positive_message; $icon_name = 'fa-play-circle-o lightgreytext'; } else { $message = $negative_message; $icon_name = 'fa-lock'; } $icon = id(new PHUIIconView()) ->setIconFont($icon_name); require_celerity_resource('policy-css'); $phid = $this->getCurrentApplication()->getPHID(); $explain_uri = "/policy/explain/{$phid}/{$capability}/"; $message = phutil_tag( 'div', array( 'class' => 'policy-capability-explanation', ), array( $icon, javelin_tag( 'a', array( 'href' => $explain_uri, 'sigil' => 'workflow', ), $message), )); return array($can_act, $message); } public function getDefaultResourceSource() { return 'phabricator'; } /** * Create a new @{class:AphrontDialogView} with defaults filled in. * * @return AphrontDialogView New dialog. */ public function newDialog() { $submit_uri = new PhutilURI($this->getRequest()->getRequestURI()); $submit_uri = $submit_uri->getPath(); return id(new AphrontDialogView()) ->setUser($this->getRequest()->getUser()) ->setSubmitURI($submit_uri); } protected function buildTransactionTimeline( - PhabricatorLiskDAO $object, + PhabricatorApplicationTransactionInterface $object, PhabricatorApplicationTransactionQuery $query, PhabricatorMarkupEngine $engine = null) { $viewer = $this->getRequest()->getUser(); $xaction = $object->getApplicationTransactionTemplate(); $view = $xaction->getApplicationTransactionViewObject(); $xactions = $query ->setViewer($viewer) ->withObjectPHIDs(array($object->getPHID())) ->needComments(true) ->execute(); if ($engine) { foreach ($xactions as $xaction) { if ($xaction->getComment()) { $engine->addObject( $xaction->getComment(), PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT); } } $engine->process(); $view->setMarkupEngine($engine); } $timeline = $view ->setUser($viewer) ->setObjectPHID($object->getPHID()) ->setTransactions($xactions); return $timeline; } } diff --git a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php index 71a1ece904..c07a139344 100644 --- a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php +++ b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php @@ -1,788 +1,771 @@ getOldPatches() as $old_name => $old_patch) { if (preg_match('/^db\./', $old_name)) { $old_patch['name'] = substr($old_name, 3); $old_patch['type'] = 'db'; } else { if (empty($old_patch['name'])) { $old_patch['name'] = $this->getPatchPath($old_name); } if (empty($old_patch['type'])) { $matches = null; preg_match('/\.(sql|php)$/', $old_name, $matches); $old_patch['type'] = $matches[1]; } } $patches[$old_name] = $old_patch; } $root = dirname(phutil_get_library_root('phabricator')); $auto_root = $root.'/resources/sql/autopatches/'; - $auto_list = Filesystem::listDirectory($auto_root, $include_hidden = false); - sort($auto_list); - - foreach ($auto_list as $auto_patch) { - $matches = null; - if (!preg_match('/\.(sql|php)$/', $auto_patch, $matches)) { - throw new Exception( - pht( - 'Unknown patch "%s" in "%s", expected ".php" or ".sql" suffix.', - $auto_patch, - $auto_root)); - } - - $patches[$auto_patch] = array( - 'type' => $matches[1], - 'name' => $auto_root.$auto_patch, - ); - } + $patches += $this->buildPatchesFromDirectory($auto_root); return $patches; } public function getOldPatches() { return array( 'db.audit' => array( 'after' => array( /* First Patch */ ), ), 'db.calendar' => array(), 'db.chatlog' => array(), 'db.conduit' => array(), 'db.countdown' => array(), 'db.daemon' => array(), 'db.differential' => array(), 'db.draft' => array(), 'db.drydock' => array(), 'db.feed' => array(), 'db.file' => array(), 'db.flag' => array(), 'db.harbormaster' => array(), 'db.herald' => array(), 'db.maniphest' => array(), 'db.meta_data' => array(), 'db.metamta' => array(), 'db.oauth_server' => array(), 'db.owners' => array(), 'db.pastebin' => array(), 'db.phame' => array(), 'db.phriction' => array(), 'db.project' => array(), 'db.repository' => array(), 'db.search' => array(), 'db.slowvote' => array(), 'db.timeline' => array( 'dead' => true, ), 'db.user' => array(), 'db.worker' => array(), 'db.xhpastview' => array(), 'db.cache' => array(), 'db.fact' => array(), 'db.ponder' => array(), 'db.xhprof' => array(), 'db.pholio' => array(), 'db.conpherence' => array(), 'db.config' => array(), 'db.token' => array(), 'db.releeph' => array(), 'db.phlux' => array(), 'db.phortune' => array(), 'db.phrequent' => array(), 'db.diviner' => array(), 'db.auth' => array(), 'db.doorkeeper' => array(), 'db.legalpad' => array(), 'db.policy' => array(), 'db.nuance' => array(), 'db.passphrase' => array(), 'db.phragment' => array(), 'db.dashboard' => array(), 'db.system' => array(), 'db.fund' => array(), 'db.almanac' => array(), '0000.legacy.sql' => array( 'legacy' => 0, ), '000.project.sql' => array( 'legacy' => 0, ), '001.maniphest_projects.sql' => array( 'legacy' => 1, ), '002.oauth.sql' => array( 'legacy' => 2, ), '003.more_oauth.sql' => array( 'legacy' => 3, ), '004.daemonrepos.sql' => array( 'legacy' => 4, ), '005.workers.sql' => array( 'legacy' => 5, ), '006.repository.sql' => array( 'legacy' => 6, ), '007.daemonlog.sql' => array( 'legacy' => 7, ), '008.repoopt.sql' => array( 'legacy' => 8, ), '009.repo_summary.sql' => array( 'legacy' => 9, ), '010.herald.sql' => array( 'legacy' => 10, ), '011.badcommit.sql' => array( 'legacy' => 11, ), '012.dropphidtype.sql' => array( 'legacy' => 12, ), '013.commitdetail.sql' => array( 'legacy' => 13, ), '014.shortcuts.sql' => array( 'legacy' => 14, ), '015.preferences.sql' => array( 'legacy' => 15, ), '016.userrealnameindex.sql' => array( 'legacy' => 16, ), '017.sessionkeys.sql' => array( 'legacy' => 17, ), '018.owners.sql' => array( 'legacy' => 18, ), '019.arcprojects.sql' => array( 'legacy' => 19, ), '020.pathcapital.sql' => array( 'legacy' => 20, ), '021.xhpastview.sql' => array( 'legacy' => 21, ), '022.differentialcommit.sql' => array( 'legacy' => 22, ), '023.dxkeys.sql' => array( 'legacy' => 23, ), '024.mlistkeys.sql' => array( 'legacy' => 24, ), '025.commentopt.sql' => array( 'legacy' => 25, ), '026.diffpropkey.sql' => array( 'legacy' => 26, ), '027.metamtakeys.sql' => array( 'legacy' => 27, ), '028.systemagent.sql' => array( 'legacy' => 28, ), '029.cursors.sql' => array( 'legacy' => 29, ), '030.imagemacro.sql' => array( 'legacy' => 30, ), '031.workerrace.sql' => array( 'legacy' => 31, ), '032.viewtime.sql' => array( 'legacy' => 32, ), '033.privtest.sql' => array( 'legacy' => 33, ), '034.savedheader.sql' => array( 'legacy' => 34, ), '035.proxyimage.sql' => array( 'legacy' => 35, ), '036.mailkey.sql' => array( 'legacy' => 36, ), '037.setuptest.sql' => array( 'legacy' => 37, ), '038.admin.sql' => array( 'legacy' => 38, ), '039.userlog.sql' => array( 'legacy' => 39, ), '040.transform.sql' => array( 'legacy' => 40, ), '041.heraldrepetition.sql' => array( 'legacy' => 41, ), '042.commentmetadata.sql' => array( 'legacy' => 42, ), '043.pastebin.sql' => array( 'legacy' => 43, ), '044.countdown.sql' => array( 'legacy' => 44, ), '045.timezone.sql' => array( 'legacy' => 45, ), '046.conduittoken.sql' => array( 'legacy' => 46, ), '047.projectstatus.sql' => array( 'legacy' => 47, ), '048.relationshipkeys.sql' => array( 'legacy' => 48, ), '049.projectowner.sql' => array( 'legacy' => 49, ), '050.taskdenormal.sql' => array( 'legacy' => 50, ), '051.projectfilter.sql' => array( 'legacy' => 51, ), '052.pastelanguage.sql' => array( 'legacy' => 52, ), '053.feed.sql' => array( 'legacy' => 53, ), '054.subscribers.sql' => array( 'legacy' => 54, ), '055.add_author_to_files.sql' => array( 'legacy' => 55, ), '056.slowvote.sql' => array( 'legacy' => 56, ), '057.parsecache.sql' => array( 'legacy' => 57, ), '058.missingkeys.sql' => array( 'legacy' => 58, ), '059.engines.php' => array( 'legacy' => 59, ), '060.phriction.sql' => array( 'legacy' => 60, ), '061.phrictioncontent.sql' => array( 'legacy' => 61, ), '062.phrictionmenu.sql' => array( 'legacy' => 62, ), '063.pasteforks.sql' => array( 'legacy' => 63, ), '064.subprojects.sql' => array( 'legacy' => 64, ), '065.sshkeys.sql' => array( 'legacy' => 65, ), '066.phrictioncontent.sql' => array( 'legacy' => 66, ), '067.preferences.sql' => array( 'legacy' => 67, ), '068.maniphestauxiliarystorage.sql' => array( 'legacy' => 68, ), '069.heraldxscript.sql' => array( 'legacy' => 69, ), '070.differentialaux.sql' => array( 'legacy' => 70, ), '071.contentsource.sql' => array( 'legacy' => 71, ), '072.blamerevert.sql' => array( 'legacy' => 72, ), '073.reposymbols.sql' => array( 'legacy' => 73, ), '074.affectedpath.sql' => array( 'legacy' => 74, ), '075.revisionhash.sql' => array( 'legacy' => 75, ), '076.indexedlanguages.sql' => array( 'legacy' => 76, ), '077.originalemail.sql' => array( 'legacy' => 77, ), '078.nametoken.sql' => array( 'legacy' => 78, ), '079.nametokenindex.php' => array( 'legacy' => 79, ), '080.filekeys.sql' => array( 'legacy' => 80, ), '081.filekeys.php' => array( 'legacy' => 81, ), '082.xactionkey.sql' => array( 'legacy' => 82, ), '083.dxviewtime.sql' => array( 'legacy' => 83, ), '084.pasteauthorkey.sql' => array( 'legacy' => 84, ), '085.packagecommitrelationship.sql' => array( 'legacy' => 85, ), '086.formeraffil.sql' => array( 'legacy' => 86, ), '087.phrictiondelete.sql' => array( 'legacy' => 87, ), '088.audit.sql' => array( 'legacy' => 88, ), '089.projectwiki.sql' => array( 'legacy' => 89, ), '090.forceuniqueprojectnames.php' => array( 'legacy' => 90, ), '091.uniqueslugkey.sql' => array( 'legacy' => 91, ), '092.dropgithubnotification.sql' => array( 'legacy' => 92, ), '093.gitremotes.php' => array( 'legacy' => 93, ), '094.phrictioncolumn.sql' => array( 'legacy' => 94, ), '095.directory.sql' => array( 'legacy' => 95, ), '096.filename.sql' => array( 'legacy' => 96, ), '097.heraldruletypes.sql' => array( 'legacy' => 97, ), '098.heraldruletypemigration.php' => array( 'legacy' => 98, ), '099.drydock.sql' => array( 'legacy' => 99, ), '100.projectxaction.sql' => array( 'legacy' => 100, ), '101.heraldruleapplied.sql' => array( 'legacy' => 101, ), '102.heraldcleanup.php' => array( 'legacy' => 102, ), '103.heraldedithistory.sql' => array( 'legacy' => 103, ), '104.searchkey.sql' => array( 'legacy' => 104, ), '105.mimetype.sql' => array( 'legacy' => 105, ), '106.chatlog.sql' => array( 'legacy' => 106, ), '107.oauthserver.sql' => array( 'legacy' => 107, ), '108.oauthscope.sql' => array( 'legacy' => 108, ), '109.oauthclientphidkey.sql' => array( 'legacy' => 109, ), '110.commitaudit.sql' => array( 'legacy' => 110, ), '111.commitauditmigration.php' => array( 'legacy' => 111, ), '112.oauthaccesscoderedirecturi.sql' => array( 'legacy' => 112, ), '113.lastreviewer.sql' => array( 'legacy' => 113, ), '114.auditrequest.sql' => array( 'legacy' => 114, ), '115.prepareutf8.sql' => array( 'legacy' => 115, ), '116.utf8-backup-first-expect-wait.sql' => array( 'legacy' => 116, ), '117.repositorydescription.php' => array( 'legacy' => 117, ), '118.auditinline.sql' => array( 'legacy' => 118, ), '119.filehash.sql' => array( 'legacy' => 119, ), '120.noop.sql' => array( 'legacy' => 120, ), '121.drydocklog.sql' => array( 'legacy' => 121, ), '122.flag.sql' => array( 'legacy' => 122, ), '123.heraldrulelog.sql' => array( 'legacy' => 123, ), '124.subpriority.sql' => array( 'legacy' => 124, ), '125.ipv6.sql' => array( 'legacy' => 125, ), '126.edges.sql' => array( 'legacy' => 126, ), '127.userkeybody.sql' => array( 'legacy' => 127, ), '128.phabricatorcom.sql' => array( 'legacy' => 128, ), '129.savedquery.sql' => array( 'legacy' => 129, ), '130.denormalrevisionquery.sql' => array( 'legacy' => 130, ), '131.migraterevisionquery.php' => array( 'legacy' => 131, ), '132.phame.sql' => array( 'legacy' => 132, ), '133.imagemacro.sql' => array( 'legacy' => 133, ), '134.emptysearch.sql' => array( 'legacy' => 134, ), '135.datecommitted.sql' => array( 'legacy' => 135, ), '136.sex.sql' => array( 'legacy' => 136, ), '137.auditmetadata.sql' => array( 'legacy' => 137, ), '138.notification.sql' => array(), 'holidays.sql' => array(), 'userstatus.sql' => array(), 'emailtable.sql' => array(), 'emailtableport.sql' => array( // NOTE: This is a ".php" patch, but the key is ".sql". 'type' => 'php', 'name' => $this->getPatchPath('emailtableport.php'), ), 'emailtableremove.sql' => array(), 'phiddrop.sql' => array(), 'testdatabase.sql' => array(), 'ldapinfo.sql' => array(), 'threadtopic.sql' => array(), 'usertranslation.sql' => array(), 'differentialbookmarks.sql' => array(), 'harbormasterobject.sql' => array(), 'markupcache.sql' => array(), 'maniphestxcache.sql' => array(), 'migrate-maniphest-dependencies.php' => array(), 'migrate-differential-dependencies.php' => array(), 'phameblog.sql' => array(), 'migrate-maniphest-revisions.php' => array(), 'daemonstatus.sql' => array(), 'symbolcontexts.sql' => array(), 'migrate-project-edges.php' => array(), 'fact-raw.sql' => array(), 'ponder.sql' => array(), 'policy-project.sql' => array(), 'daemonstatuskey.sql' => array(), 'edgetype.sql' => array(), 'ponder-comments.sql' => array(), 'pastepolicy.sql' => array(), 'xhprof.sql' => array(), 'draft-metadata.sql' => array(), 'phamedomain.sql' => array(), 'ponder-mailkey.sql' => array(), 'ponder-mailkey-populate.php' => array(), 'phamepolicy.sql' => array(), 'phameoneblog.sql' => array(), 'statustxt.sql' => array(), 'daemontaskarchive.sql' => array(), 'drydocktaskid.sql' => array(), 'drydockresoucetype.sql' => array( // NOTE: The key for this patch misspells "resource" as "resouce". 'name' => $this->getPatchPath('drydockresourcetype.sql'), ), 'liskcounters.sql' => array(), 'liskcounters.php' => array(), 'dropfileproxyimage.sql' => array(), 'repository-lint.sql' => array(), 'liskcounters-task.sql' => array(), 'pholio.sql' => array(), 'owners-exclude.sql' => array(), '20121209.pholioxactions.sql' => array(), '20121209.xmacroadd.sql' => array(), '20121209.xmacromigrate.php' => array(), '20121209.xmacromigratekey.sql' => array(), '20121220.generalcache.sql' => array(), '20121226.config.sql' => array(), '20130101.confxaction.sql' => array(), '20130102.metamtareceivedmailmessageidhash.sql' => array(), '20130103.filemetadata.sql' => array(), '20130111.conpherence.sql' => array(), '20130127.altheraldtranscript.sql' => array(), '20130201.revisionunsubscribed.php' => array(), '20130201.revisionunsubscribed.sql' => array(), '20130131.conpherencepics.sql' => array(), '20130214.chatlogchannel.sql' => array(), '20130214.chatlogchannelid.sql' => array(), '20130214.token.sql' => array(), '20130215.phabricatorfileaddttl.sql' => array(), '20130217.cachettl.sql' => array(), '20130218.updatechannelid.php' => array(), '20130218.longdaemon.sql' => array(), '20130219.commitsummary.sql' => array(), '20130219.commitsummarymig.php' => array(), '20130222.dropchannel.sql' => array(), '20130226.commitkey.sql' => array(), '20131302.maniphestvalue.sql' => array(), '20130304.lintauthor.sql' => array(), 'releeph.sql' => array(), '20130319.phabricatorfileexplicitupload.sql' => array(), '20130319.conpherence.sql' => array(), '20130320.phlux.sql' => array(), '20130317.phrictionedge.sql' => array(), '20130321.token.sql' => array(), '20130310.xactionmeta.sql' => array(), '20130322.phortune.sql' => array(), '20130323.phortunepayment.sql' => array(), '20130324.phortuneproduct.sql' => array(), '20130330.phrequent.sql' => array(), '20130403.conpherencecache.sql' => array(), '20130403.conpherencecachemig.php' => array(), '20130409.commitdrev.php' => array(), '20130417.externalaccount.sql' => array(), '20130423.updateexternalaccount.sql' => array(), '20130423.phortunepaymentrevised.sql' => array(), '20130423.conpherenceindices.sql' => array(), '20130426.search_savedquery.sql' => array(), '20130502.countdownrevamp1.sql' => array(), '20130502.countdownrevamp2.php' => array(), '20130502.countdownrevamp3.sql' => array(), '20130507.releephrqsimplifycols.sql' => array(), '20130507.releephrqmailkey.sql' => array(), '20130507.releephrqmailkeypop.php' => array(), '20130508.search_namedquery.sql' => array(), '20130508.releephtransactions.sql' => array(), '20130508.releephtransactionsmig.php' => array(), '20130513.receviedmailstatus.sql' => array(), '20130519.diviner.sql' => array(), '20130521.dropconphimages.sql' => array(), '20130523.maniphest_owners.sql' => array(), '20130524.repoxactions.sql' => array(), '20130529.macroauthor.sql' => array(), '20130529.macroauthormig.php' => array(), '20130530.sessionhash.php' => array(), '20130530.macrodatekey.sql' => array(), '20130530.pastekeys.sql' => array(), '20130531.filekeys.sql' => array(), '20130602.morediviner.sql' => array(), '20130602.namedqueries.sql' => array(), '20130606.userxactions.sql' => array(), '20130607.xaccount.sql' => array(), '20130611.migrateoauth.php' => array(), '20130611.nukeldap.php' => array(), '20130613.authdb.sql' => array(), '20130619.authconf.php' => array(), '20130620.diffxactions.sql' => array(), '20130621.diffcommentphid.sql' => array(), '20130621.diffcommentphidmig.php' => array(), '20130621.diffcommentunphid.sql' => array(), '20130622.doorkeeper.sql' => array(), '20130628.legalpadv0.sql' => array(), '20130701.conduitlog.sql' => array(), 'legalpad-mailkey.sql' => array(), 'legalpad-mailkey-populate.php' => array(), '20130703.legalpaddocdenorm.sql' => array(), '20130703.legalpaddocdenorm.php' => array(), '20130709.legalpadsignature.sql' => array(), '20130709.droptimeline.sql' => array(), '20130711.trimrealnames.php' => array(), '20130714.votexactions.sql' => array(), '20130715.votecomments.php' => array(), '20130715.voteedges.sql' => array(), '20130711.pholioimageobsolete.sql' => array(), '20130711.pholioimageobsolete.php' => array(), '20130711.pholioimageobsolete2.sql' => array(), '20130716.archivememberlessprojects.php' => array(), '20130722.pholioreplace.sql' => array(), '20130723.taskstarttime.sql' => array(), '20130727.ponderquestionstatus.sql' => array(), '20130726.ponderxactions.sql' => array(), '20130728.ponderunique.php' => array(), '20130728.ponderuniquekey.sql' => array(), '20130728.ponderxcomment.php' => array(), '20130801.pastexactions.sql' => array(), '20130801.pastexactions.php' => array(), '20130805.pastemailkey.sql' => array(), '20130805.pasteedges.sql' => array(), '20130805.pastemailkeypop.php' => array(), '20130802.heraldphid.sql' => array(), '20130802.heraldphids.php' => array(), '20130802.heraldphidukey.sql' => array(), '20130802.heraldxactions.sql' => array(), '20130731.releephrepoid.sql' => array(), '20130731.releephproject.sql' => array(), '20130731.releephcutpointidentifier.sql' => array(), '20130814.usercustom.sql' => array(), '20130820.releephxactions.sql' => array(), '20130826.divinernode.sql' => array(), '20130820.filexactions.sql' => array(), '20130820.filemailkey.sql' => array(), '20130820.file-mailkey-populate.php' => array(), '20130912.maniphest.1.touch.sql' => array(), '20130912.maniphest.2.created.sql' => array(), '20130912.maniphest.3.nameindex.sql' => array(), '20130912.maniphest.4.fillindex.php' => array(), '20130913.maniphest.1.migratesearch.php' => array(), '20130914.usercustom.sql' => array(), '20130915.maniphestcustom.sql' => array(), '20130915.maniphestmigrate.php' => array(), '20130919.mfieldconf.php' => array(), '20130920.repokeyspolicy.sql' => array(), '20130921.mtransactions.sql' => array(), '20130921.xmigratemaniphest.php' => array(), '20130923.mrename.sql' => array(), '20130924.mdraftkey.sql' => array(), '20130925.mpolicy.sql' => array(), '20130925.xpolicy.sql' => array(), '20130926.dcustom.sql' => array(), '20130926.dinkeys.sql' => array(), '20130927.audiomacro.sql' => array(), '20130929.filepolicy.sql' => array(), '20131004.dxedgekey.sql' => array(), '20131004.dxreviewers.php' => array(), '20131006.hdisable.sql' => array(), '20131010.pstorage.sql' => array(), '20131015.cpolicy.sql' => array(), '20130915.maniphestqdrop.sql' => array(), '20130926.dinline.php' => array(), '20131020.pcustom.sql' => array(), '20131020.col1.sql' => array(), '20131020.pxaction.sql' => array(), '20131020.pxactionmig.php' => array(), '20131020.harbormaster.sql' => array(), '20131025.repopush.sql' => array(), '20131026.commitstatus.sql' => array(), '20131030.repostatusmessage.sql' => array(), '20131031.vcspassword.sql' => array(), '20131105.buildstep.sql' => array(), '20131106.diffphid.1.col.sql' => array(), '20131106.diffphid.2.mig.php' => array(), '20131106.diffphid.3.key.sql' => array(), '20131106.nuance-v0.sql' => array(), '20131107.buildlog.sql' => array(), '20131112.userverified.1.col.sql' => array(), '20131112.userverified.2.mig.php' => array(), '20131118.ownerorder.php' => array(), '20131119.passphrase.sql' => array(), '20131120.nuancesourcetype.sql' => array(), '20131121.passphraseedge.sql' => array(), '20131121.repocredentials.1.col.sql' => array(), '20131121.repocredentials.2.mig.php' => array(), '20131122.repomirror.sql' => array(), '20131123.drydockblueprintpolicy.sql' => array(), '20131129.drydockresourceblueprint.sql' => array(), '20131205.buildtargets.sql' => array(), '20131204.pushlog.sql' => array(), '20131205.buildsteporder.sql' => array(), '20131205.buildstepordermig.php' => array(), '20131206.phragment.sql' => array(), '20131206.phragmentnull.sql' => array(), '20131208.phragmentsnapshot.sql' => array(), '20131211.phragmentedges.sql' => array(), '20131217.pushlogphid.1.col.sql' => array(), '20131217.pushlogphid.2.mig.php' => array(), '20131217.pushlogphid.3.key.sql' => array(), '20131219.pxdrop.sql' => array(), '20131224.harbormanual.sql' => array(), '20131227.heraldobject.sql' => array(), '20131231.dropshortcut.sql' => array(), ); // NOTE: STOP! Don't add new patches here. // Use 'resources/sql/autopatches/' instead! } } diff --git a/src/infrastructure/storage/patch/PhabricatorSQLPatchList.php b/src/infrastructure/storage/patch/PhabricatorSQLPatchList.php index 65070e67e6..282cbbd822 100644 --- a/src/infrastructure/storage/patch/PhabricatorSQLPatchList.php +++ b/src/infrastructure/storage/patch/PhabricatorSQLPatchList.php @@ -1,150 +1,181 @@ $matches[1], + 'name' => rtrim($directory, '/').'/'.$patch, + ); + } + + return $patches; + } + final public static function buildAllPatches() { $patch_lists = id(new PhutilSymbolLoader()) ->setAncestorClass('PhabricatorSQLPatchList') ->setConcreteOnly(true) ->selectAndLoadSymbols(); $specs = array(); $seen_namespaces = array(); foreach ($patch_lists as $patch_class) { $patch_class = $patch_class['name']; $patch_list = newv($patch_class, array()); $namespace = $patch_list->getNamespace(); if (isset($seen_namespaces[$namespace])) { $prior = $seen_namespaces[$namespace]; throw new Exception( "PatchList '{$patch_class}' has the same namespace, '{$namespace}', ". "as another patch list class, '{$prior}'. Each patch list MUST have ". "a unique namespace."); } $last_key = null; foreach ($patch_list->getPatches() as $key => $patch) { if (!is_array($patch)) { throw new Exception( "PatchList '{$patch_class}' has a patch '{$key}' which is not ". "an array."); } $valid = array( 'type' => true, 'name' => true, 'after' => true, 'legacy' => true, 'dead' => true, ); foreach ($patch as $pkey => $pval) { if (empty($valid[$pkey])) { throw new Exception( "PatchList '{$patch_class}' has a patch, '{$key}', with an ". "unknown property, '{$pkey}'. Patches must have only valid ". "keys: ".implode(', ', array_keys($valid)).'.'); } } if (is_numeric($key)) { throw new Exception( "PatchList '{$patch_class}' has a patch with a numeric key, ". "'{$key}'. Patches must use string keys."); } if (strpos($key, ':') !== false) { throw new Exception( "PatchList '{$patch_class}' has a patch with a colon in the ". "key name, '{$key}'. Patch keys may not contain colons."); } $full_key = "{$namespace}:{$key}"; if (isset($specs[$full_key])) { throw new Exception( "PatchList '{$patch_class}' has a patch '{$key}' which ". "duplicates an existing patch key."); } $patch['key'] = $key; $patch['fullKey'] = $full_key; $patch['dead'] = (bool)idx($patch, 'dead', false); if (isset($patch['legacy'])) { if ($namespace != 'phabricator') { throw new Exception( "Only patches in the 'phabricator' namespace may contain ". "'legacy' keys."); } } else { $patch['legacy'] = false; } if (!array_key_exists('after', $patch)) { if ($last_key === null) { throw new Exception( "Patch '{$full_key}' is missing key 'after', and is the first ". "patch in the patch list '{$patch_class}', so its application ". "order can not be determined implicitly. The first patch in a ". "patch list must list the patch or patches it depends on ". "explicitly."); } else { $patch['after'] = array($last_key); } } $last_key = $full_key; foreach ($patch['after'] as $after_key => $after) { if (strpos($after, ':') === false) { $patch['after'][$after_key] = $namespace.':'.$after; } } $type = idx($patch, 'type'); if (!$type) { throw new Exception( "Patch '{$namespace}:{$key}' is missing key 'type'. Every patch ". "must have a type."); } switch ($type) { case 'db': case 'sql': case 'php': break; default: throw new Exception( "Patch '{$namespace}:{$key}' has unknown patch type '{$type}'."); } $specs[$full_key] = $patch; } } foreach ($specs as $key => $patch) { foreach ($patch['after'] as $after) { if (empty($specs[$after])) { throw new Exception( "Patch '{$key}' references nonexistent dependency, '{$after}'. ". "Patches may only depend on patches which actually exist."); } } } $patches = array(); foreach ($specs as $full_key => $spec) { $patches[$full_key] = new PhabricatorStoragePatch($spec); } // TODO: Detect cycles? return $patches; } }