/** * @uses ModelAsController::getNestedController() * @param SS_HTTPRequest $request * @param DataModel $model * @return SS_HTTPResponse */ public function handleRequest(SS_HTTPRequest $request, DataModel $model) { $this->setRequest($request); $this->setDataModel($model); $this->pushCurrent(); // Create a response just in case init() decides to redirect $this->response = new SS_HTTPResponse(); $this->init(); // If we had a redirection or something, halt processing. if ($this->response->isFinished()) { $this->popCurrent(); return $this->response; } // If the database has not yet been created, redirect to the build page. if (!DB::is_active() || !ClassInfo::hasTable('SiteTree')) { $this->response->redirect(Director::absoluteBaseURL() . 'dev/build?returnURL=' . (isset($_GET['url']) ? urlencode($_GET['url']) : null)); $this->popCurrent(); return $this->response; } try { $result = $this->getNestedController(); if ($result instanceof RequestHandler) { $result = $result->handleRequest($this->getRequest(), $model); } else { if (!$result instanceof SS_HTTPResponse) { user_error("ModelAsController::getNestedController() returned bad object type '" . get_class($result) . "'", E_USER_WARNING); } } } catch (SS_HTTPResponse_Exception $responseException) { $result = $responseException->getResponse(); } $this->popCurrent(); return $result; }
private static function truncate_table($table) { if (ClassInfo::hasTable($table)) { if (method_exists(DB::getConn(), 'clearTable')) { DB::getConn()->clearTable($table); } else { DB::query("TRUNCATE \"{$table}\""); } } }
public function run($request) { HTTP::set_cache_age(0); increase_time_limit_to(); // This can be a time consuming task $conn = DB::getConn(); $classes = ClassInfo::subclassesFor('DataObject'); $dbTables = $conn->tableList(); $go = $request->getVar('go'); if (!$go) { echo 'Set ?go=1 to really delete the tables'; echo '<hr/>'; } //make all lowercase $dbTablesLc = array_map('strtolower', $dbTables); $dbTablesMap = array(); foreach ($dbTables as $k => $v) { $dbTablesMap[strtolower($v)] = $v; } foreach ($classes as $class) { if (ClassInfo::hasTable($class)) { $lcClass = strtolower($class); self::removeFromArray($lcClass, $dbTablesLc); //page modules self::removeFromArray($lcClass . '_live', $dbTablesLc); self::removeFromArray($lcClass . '_versions', $dbTablesLc); //relations $hasMany = Config::inst()->get($class, 'has_many'); $manyMany = Config::inst()->get($class, 'many_many'); if (!empty($hasMany)) { foreach ($hasMany as $rel => $obj) { self::removeFromArray($lcClass . '_' . strtolower($rel), $dbTablesLc); } } if (!empty($manyMany)) { foreach ($manyMany as $rel => $obj) { self::removeFromArray($lcClass . '_' . strtolower($rel), $dbTablesLc); } } } } //at this point, we should only have orphans table in dbTables var foreach ($dbTablesLc as $i => $lcTable) { $table = $dbTablesMap[$lcTable]; if ($go) { DB::query('DROP TABLE `' . $table . '`'); DB::alteration_message("Dropped {$table}", 'obsolete'); } else { DB::alteration_message("Would drop {$table}", 'obsolete'); } } }
public function handleRequest($request) { self::$is_at_root = true; $this->pushCurrent(); $this->init(); // If the basic database hasn't been created, then build it. if (!DB::isActive() || !ClassInfo::hasTable('SiteTree')) { $this->response = new HTTPResponse(); $this->redirect("dev/build?returnURL="); return $this->response; } $controller = new ModelAsController(); $request = new HTTPRequest("GET", self::get_homepage_urlsegment() . '/', $request->getVars(), $request->postVars()); $request->match('$URLSegment//$Action', true); $result = $controller->handleRequest($request); $this->popCurrent(); return $result; }
/** * Check catalogue URL's before we get to the CMS (if it exists) * * @param SS_HTTPRequest $request * @param DataModel|null $model * @return SS_HTTPResponse */ public function handleRequest(SS_HTTPRequest $request, DataModel $model) { $this->request = $request; $this->setDataModel($model); $catalogue_enabled = Catalogue::config()->enable_frontend; $this->pushCurrent(); // Create a response just in case init() decides to redirect $this->response = new SS_HTTPResponse(); $this->init(); // If we had a redirection or something, halt processing. if ($this->response->isFinished()) { $this->popCurrent(); return $this->response; } // If DB is not present, build if (!DB::isActive() || !ClassInfo::hasTable('CatalogueProduct') || !ClassInfo::hasTable('CatalogueCategory')) { return $this->response->redirect(Director::absoluteBaseURL() . 'dev/build?returnURL=' . (isset($_GET['url']) ? urlencode($_GET['url']) : null)); } $urlsegment = $request->param('URLSegment'); $this->extend('onBeforeInit'); $this->init(); $this->extend('onAfterInit'); // Find link, regardless of current locale settings if (class_exists('Translatable')) { Translatable::disable_locale_filter(); } $filter = array('URLSegment' => $urlsegment, 'Disabled' => 0); if ($catalogue_enabled && ($object = CatalogueProduct::get()->filter($filter)->first())) { $controller = $this->controller_for($object); } elseif ($catalogue_enabled && ($object = CatalogueCategory::get()->filter($filter)->first())) { $controller = $this->controller_for($object); } elseif (class_exists('ModelAsController')) { // If CMS installed $controller = ModelAsController::create(); } else { $controller = Controller::create(); } if (class_exists('Translatable')) { Translatable::enable_locale_filter(); } $result = $controller->handleRequest($request, $model); $this->popCurrent(); return $result; }
public function handleRequest($request) { $this->pushCurrent(); $this->urlParams = $request->allParams(); $this->init(); // If the basic database hasn't been created, then build it. if (!DB::isActive() || !ClassInfo::hasTable('SiteTree')) { $this->response = new HTTPResponse(); $this->redirect("dev/build?returnURL=" . (isset($_GET['url']) ? urlencode($_GET['url']) : '')); $this->popCurrent(); return $this->response; } $result = $this->getNestedController(); if (is_object($result) && $result instanceof RequestHandler) { $result = $result->handleRequest($request); } $this->popCurrent(); return $result; }
public function run($request) { $classes = ClassInfo::subclassesFor('DataObject'); $dbTables = DB::query(DB::getConn()->allTablesSQL())->column(); $go = $request->getVar('go'); if (!$go) { DB::alteration_message('Set ?go=1 to really delete the tables'); } //make all lowercase $dbTablesLc = array_map('strtolower', $dbTables); foreach ($classes as $class) { if (ClassInfo::hasTable($class)) { $lcClass = strtolower($class); self::removeFromArray($lcClass, $dbTablesLc); //page modules self::removeFromArray($lcClass . '_live', $dbTablesLc); self::removeFromArray($lcClass . '_versions', $dbTablesLc); //relations $hasMany = Config::inst()->get($class, 'has_many'); $manyMany = Config::inst()->get($class, 'many_many'); if (!empty($hasMany)) { foreach ($hasMany as $rel => $obj) { self::removeFromArray($lcClass . '_' . strtolower($rel), $dbTablesLc); } } if (!empty($manyMany)) { foreach ($manyMany as $rel => $obj) { self::removeFromArray($lcClass . '_' . strtolower($rel), $dbTablesLc); } } } } //at this point, we should only have orphans table in dbTables var foreach ($dbTablesLc as $i => $table) { if ($go) { DB::query('DROP TABLE `' . $table . '`'); DB::alteration_message("Dropped {$table}", 'obsolete'); } else { DB::alteration_message("Would drop {$table}", 'obsolete'); } } }
public function run($request) { HTTP::set_cache_age(0); increase_time_limit_to(); // This can be a time consuming task $classes = ClassInfo::dataClassesFor('DataObject'); $conn = DB::getConn(); $go = $request->getVar('go'); if (!$go) { echo 'Set ?go=1 to really delete the fields'; echo '<hr/>'; } foreach ($classes as $class) { $hasTable = ClassInfo::hasTable($class); if (!$hasTable) { continue; } $toDrop = array(); $fields = $class::database_fields($class); $list = $conn->fieldList($class); foreach ($list as $fieldName => $type) { if ($fieldName == 'ID') { continue; } if (!isset($fields[$fieldName])) { $toDrop[] = $fieldName; } } if (empty($toDrop)) { continue; } if ($go) { $this->dropColumns($class, $toDrop); DB::alteration_message("Dropped " . implode(',', $toDrop) . " for {$class}", "obsolete"); } else { DB::alteration_message("Would drop " . implode(',', $toDrop) . " for {$class}", "obsolete"); } } }
public function _getAnnouncement($render = true) { if (ClassInfo::hasTable('MediaHolder')) { $media = MediaHolder::get()->filter(array('MediaType.Title' => 'News')); $possible = $media->first(); // what about an announcement title'd page $page = $media->filter(array('Title' => 'Announcements'))->first(); if (!$page) { $page = $possible; } if ($page) { $announcement = MediaPage::get()->filter('ParentID', $page->ID)->sort('Date DESC')->first(); if ($announcement) { if (!$render) { return $announcement; } else { return ModelAsController::controller_for($announcement)->index(); } } } } }
function run($request) { //TODO: include decendant clases..incase some subclassing has been done somewhere if ($allorders = DataObject::get('Order')) { foreach ($allorders as $order) { $order->delete(); $order->destroy(); } } if ($allproducts = DataObject::get('Product')) { foreach ($allproducts as $product) { $product->deleteFromStage('Live'); $product->deleteFromStage('Stage'); $product->destroy(); //TODO: remove versions } } //TODO: use TRUNCATE instead? $basetables = array('Product', 'Product_Live', 'Product_versions', 'Product_ProductGroups', 'Product_OrderItem', 'Product_VariationAttributes', 'ProductVariation', 'ProductVariation_AttributeValues', 'ProductVariation_OrderItem', 'ProductVariation_versions', 'ProductAttributeType', 'ProductAttributeValue'); foreach ($basetables as $table) { if (!ClassInfo::hasTable($table)) { continue; } foreach (ClassInfo::subclassesFor($table) as $key => $class) { if (ClassInfo::hasTable($class)) { DB::query("DELETE FROM \"{$class}\" WHERE 1;"); echo "<p>Deleting all {$class}</p>"; } } } //partial empty queries echo "<p>Deleting all SiteTree</p>"; DB::query("DELETE FROM \"SiteTree\" WHERE ClassName = 'Product';"); //SiteTree DB::query("DELETE FROM \"SiteTree_Live\" WHERE ClassName = 'Product';"); //SiteTree DB::query("DELETE FROM \"SiteTree_versions\" WHERE ClassName = 'Product';"); //SiteTree }
public function handleRequest(SS_HTTPRequest $request, DataModel $model = null) { self::$is_at_root = true; $this->setDataModel($model); $this->pushCurrent(); $this->init(); $this->setRequest($request); // Check for existing routing parameters, redirecting to another locale automatically if necessary $locale = Fluent::get_request_locale(); if (empty($locale)) { // Determine if this user should be redirected $locale = $this->getRedirectLocale(); $this->extend('updateRedirectLocale', $locale); // Check if the user should be redirected $domainDefault = Fluent::default_locale(true); if (Fluent::is_locale($locale) && $locale !== $domainDefault) { // Check new traffic with detected locale return $this->redirect(Fluent::locale_baseurl($locale)); } // Reset parameters to act in the default locale $locale = $domainDefault; Fluent::set_persist_locale($locale); $params = $request->routeParams(); $params[Fluent::config()->query_param] = $locale; $request->setRouteParams($params); } if (!DB::isActive() || !ClassInfo::hasTable('SiteTree')) { $this->response = new SS_HTTPResponse(); $this->response->redirect(Director::absoluteBaseURL() . 'dev/build?returnURL=' . (isset($_GET['url']) ? urlencode($_GET['url']) : null)); return $this->response; } $localeURL = Fluent::alias($locale); $request->setUrl(self::fluent_homepage_link($localeURL)); $request->match($localeURL . '/$URLSegment//$Action', true); $controller = new ModelAsController(); $result = $controller->handleRequest($request, $model); $this->popCurrent(); return $result; }
function run($request) { if ($allorders = DataObject::get('Order')) { foreach ($allorders as $order) { //TODO: delete member(s)? $order->delete(); $order->destroy(); } } $basetables = array('Order', 'OrderAttribute', 'OrderStatusLog', 'Payment'); foreach ($basetables as $table) { if (!ClassInfo::hasTable($table)) { continue; } foreach (ClassInfo::subclassesFor($table) as $key => $class) { //TODO: empty all pivot(many_many) tables on this side of relationship if (ClassInfo::hasTable($class)) { DB::query("DELETE FROM \"{$class}\" WHERE 1;"); echo "<p>Deleting all {$class}</p>"; } } } }
public function run($request) { increase_time_limit_to(); $readingMode = Versioned::get_reading_mode(); Versioned::reading_stage('Stage'); // Make sure that we have something to migrate. if (!ClassInfo::hasTable('SolrSearchPage')) { echo "Nothing to Migrate!"; die; } // Retrieve the search tree relationships to migrate. $relationships = array(); if (DB::getConn()->hasTable('SolrSearchPage_SearchTrees')) { foreach (DB::query('SELECT * FROM SolrSearchPage_SearchTrees') as $relationship) { $relationships[$relationship['SolrSearchPageID']] = $relationship['PageID']; } } // Store the current live page migration state to avoid duplicates. $created = array(); // Migrate any live pages to begin with. $query = DB::query('SELECT * FROM SiteTree_Live st, SolrSearchPage_Live ssp WHERE st.ID = ssp.ID'); $queryCount = $query->numRecords(); $writeCount = 0; foreach ($query as $results) { $searchPage = ExtensibleSearchPage::create(); $searchPage->SearchEngine = 'SolrSearch'; // Migrate the key site tree and solr search fields across. $fields = array('ParentID', 'URLSegment', 'Title', 'MenuTitle', 'Content', 'ShowInMenus', 'ShowInSearch', 'Sort', 'ResultsPerPage', 'SortBy', 'BoostFieldsValue', 'SearchOnFieldsValue', 'SearchTypeValue', 'StartWithListing', 'QueryType', 'ListingTemplateID', 'FilterFieldsValue', 'MinFacetCount', 'FacetQueriesValue', 'FacetMappingValue', 'CustomFacetFieldsValue', 'FacetFieldsValue', 'BoostMatchFieldsValue'); foreach ($fields as $fname) { if (isset($results[$fname])) { $searchPage->{$fname} = $results[$fname]; } } // This field name no longer matches the original. if ($results['SortDir']) { $searchPage->SortDirection = $results['SortDir']; } if (isset($relationships[$results['ID']])) { $searchPage->SearchTrees()->add($relationships[$results['ID']]); } // Attempt to publish these new pages. $searchPage->doPublish(); if ($searchPage->ID) { echo "<strong>{$results['ID']}</strong> Published<br>"; $writeCount++; } $created[] = $results['ID']; } // Confirm that the current user had permission to publish these new pages. $this->checkPermissions($queryCount, $writeCount); // Migrate any remaining draft pages. $query = DB::query('SELECT * FROM SiteTree st, SolrSearchPage ssp WHERE st.ID = ssp.ID'); $queryCount = $query->numRecords(); $writeCount = 0; foreach ($query as $results) { // Make sure this search page doesn't already exist. if (!in_array($results['ID'], $created)) { $searchPage = ExtensibleSearchPage::create(); $searchPage->SearchEngine = 'SolrSearch'; // Migrate the key site tree and solr search fields across. $searchPage->ParentID = $results['ParentID']; $searchPage->URLSegment = $results['URLSegment']; $searchPage->Title = $results['Title']; $searchPage->MenuTitle = $results['MenuTitle']; $searchPage->Content = $results['Content']; $searchPage->ShowInMenus = $results['ShowInMenus']; $searchPage->ShowInSearch = $results['ShowInSearch']; $searchPage->Sort = $results['Sort']; $searchPage->ResultsPerPage = $results['ResultsPerPage']; $searchPage->SortBy = $results['SortBy']; $searchPage->SortDirection = $results['SortDir']; $searchPage->QueryType = $results['QueryType']; $searchPage->StartWithListing = $results['StartWithListing']; $searchPage->SearchTypeValue = $results['SearchTypeValue']; $searchPage->SearchOnFieldsValue = $results['SearchOnFieldsValue']; $searchPage->BoostFieldsValue = $results['BoostFieldsValue']; $searchPage->BoostMatchFieldsValue = $results['BoostMatchFieldsValue']; $searchPage->FacetFieldsValue = $results['FacetFieldsValue']; $searchPage->CustomFacetFieldsValue = $results['CustomFacetFieldsValue']; $searchPage->FacetMappingValue = $results['FacetMappingValue']; $searchPage->FacetQueriesValue = $results['FacetQueriesValue']; $searchPage->MinFacetCount = $results['MinFacetCount']; $searchPage->FilterFieldsValue = $results['FilterFieldsValue']; $searchPage->ListingTemplateID = $results['ListingTemplateID']; if (isset($relationships[$results['ID']])) { $searchPage->SearchTrees()->add($relationships[$results['ID']]); } $searchPage->write(); if ($searchPage->ID) { echo "<strong>{$results['ID']}</strong> Saved<br>"; $writeCount++; } } else { $writeCount++; } } // Confirm that the current user had permission to write these new pages. $this->checkPermissions($queryCount, $writeCount); // Remove the previous search page tables, as they are now obsolete (and may not be marked as such). $remove = array('SiteTree', 'SiteTree_Live', 'Page', 'Page_Live'); foreach ($remove as $table) { foreach ($created as $ID) { DB::query("DELETE FROM {$table} WHERE ID = {$ID}"); } } $remove = array('SolrSearchPage', 'SolrSearchPage_Live', 'SolrSearchPage_SearchTrees', 'SolrSearchPage_versions'); foreach ($remove as $table) { DB::query("DROP TABLE {$table}"); } Versioned::set_reading_mode($readingMode); echo 'Migration Complete!'; }
/** * Checks the database is in a state to perform security checks. * See {@link DatabaseAdmin->init()} for more information. * * @return bool */ public static function database_is_ready() { // Used for unit tests if (self::$force_database_is_ready !== NULL) { return self::$force_database_is_ready; } $requiredTables = ClassInfo::dataClassesFor('Member'); $requiredTables[] = 'Group'; $requiredTables[] = 'Permission'; foreach ($requiredTables as $table) { // if any of the tables aren't created in the database if (!ClassInfo::hasTable($table)) { return false; } // if any of the tables don't have all fields mapped as table columns $dbFields = DB::fieldList($table); if (!$dbFields) { return false; } $objFields = DataObject::database_fields($table); $missingFields = array_diff_key($objFields, $dbFields); if ($missingFields) { return false; } } return true; }
public function getDateJoin() { $join = "LEFT JOIN `CalendarDateTime` ON `CalendarDateTime`.EventID = `CalendarEvent`.ID"; if (is_subclass_of($this->getEventDateTimeObject(), "CalendarDateTime")) { $parents = array_reverse(ClassInfo::ancestry($this->getEventDateTimeClass())); foreach ($parents as $class) { if (ClassInfo::hasTable($class)) { if ($class == "CalendarDateTime") { break; } $join .= " LEFT JOIN `{$class}` ON `{$class}`.ID = `CalendarDateTime`.ID"; } } } return $join; }
/** * Determine if the DB is ready to use. * * @return bool * @throws Exception */ protected function isDatabaseReady() { // Such as during setup of testsession prior to DB connection. if (!DB::isActive()) { return false; } // If we have a DB of the wrong type then complain if (!DB::getConn() instanceof MySQLDatabase) { throw new Exception('HybridSessionStore currently only works with MySQL databases'); } // Prevent freakout during dev/build return ClassInfo::hasTable('HybridSessionDataObject'); }
/** * Checks the database is in a state to perform security checks. * @return bool */ public static function database_is_ready() { $requiredTables = ClassInfo::dataClassesFor('Member'); $requiredTables[] = 'Group'; $requiredTables[] = 'Permission'; foreach($requiredTables as $table) if(!ClassInfo::hasTable($table)) return false; return (($permissionFields = DB::fieldList('Permission')) && isset($permissionFields['Type'])) && (($memberFields = DB::fieldList('Member')) && isset($memberFields['RememberLoginToken'])); }
/** * Traverse the relationship fields, and add the table * mappings to the query object state. This has to be called * in any overloaded {@link SearchFilter->apply()} methods manually. * * @param String|array $relation The array/dot-syntax relation to follow * @return The model class of the related item */ public function applyRelation($relation) { // NO-OP if (!$relation) { return $this->dataClass; } if (is_string($relation)) { $relation = explode(".", $relation); } $modelClass = $this->dataClass; foreach ($relation as $rel) { $model = singleton($modelClass); if ($component = $model->has_one($rel)) { if (!$this->query->isJoinedTo($component)) { $foreignKey = $rel; $realModelClass = ClassInfo::table_for_object_field($modelClass, "{$foreignKey}ID"); $this->query->addLeftJoin($component, "\"{$component}\".\"ID\" = \"{$realModelClass}\".\"{$foreignKey}ID\""); /** * add join clause to the component's ancestry classes so that the search filter could search on * its ancestor fields. */ $ancestry = ClassInfo::ancestry($component, true); if (!empty($ancestry)) { $ancestry = array_reverse($ancestry); foreach ($ancestry as $ancestor) { if ($ancestor != $component) { $this->query->addInnerJoin($ancestor, "\"{$component}\".\"ID\" = \"{$ancestor}\".\"ID\""); } } } } $modelClass = $component; } elseif ($component = $model->has_many($rel)) { if (!$this->query->isJoinedTo($component)) { $ancestry = $model->getClassAncestry(); $foreignKey = $model->getRemoteJoinField($rel); $this->query->addLeftJoin($component, "\"{$component}\".\"{$foreignKey}\" = \"{$ancestry[0]}\".\"ID\""); /** * add join clause to the component's ancestry classes so that the search filter could search on * its ancestor fields. */ $ancestry = ClassInfo::ancestry($component, true); if (!empty($ancestry)) { $ancestry = array_reverse($ancestry); foreach ($ancestry as $ancestor) { if ($ancestor != $component) { $this->query->addInnerJoin($ancestor, "\"{$component}\".\"ID\" = \"{$ancestor}\".\"ID\""); } } } } $modelClass = $component; } elseif ($component = $model->many_many($rel)) { list($parentClass, $componentClass, $parentField, $componentField, $relationTable) = $component; $parentBaseClass = ClassInfo::baseDataClass($parentClass); $componentBaseClass = ClassInfo::baseDataClass($componentClass); $this->query->addInnerJoin($relationTable, "\"{$relationTable}\".\"{$parentField}\" = \"{$parentBaseClass}\".\"ID\""); $this->query->addLeftJoin($componentBaseClass, "\"{$relationTable}\".\"{$componentField}\" = \"{$componentBaseClass}\".\"ID\""); if (ClassInfo::hasTable($componentClass)) { $this->query->addLeftJoin($componentClass, "\"{$relationTable}\".\"{$componentField}\" = \"{$componentClass}\".\"ID\""); } $modelClass = $componentClass; } } return $modelClass; }
/** * Checks the database is in a state to perform security checks. * @return bool */ public static function database_is_ready() { return ClassInfo::hasTable('Member') && ClassInfo::hasTable('Group') && ClassInfo::hasTable('Permission') && (($permissionFields = DB::fieldList('Permission')) && isset($permissionFields['Type'])) && (($memberFields = DB::fieldList('Member')) && isset($memberFields['RememberLoginToken'])); }
/** * Return all data object visible attributes of the specified type, with optional filters. * * @parameter <{DATA_OBJECT_NAME}> string * @parameter <{LIMIT}> integer * @parameter <{SORT}> array(string, string) * @parameter <{FILTERS}> array * @return array */ public function retrieveValidated($class, $limit = null, $sort = null, $filters = null) { // Validate the data object class. $class = strtolower($class); if (in_array($class, array_map('strtolower', ClassInfo::subclassesFor('DataObject'))) && ($configuration = DataObjectOutputConfiguration::get_one('DataObjectOutputConfiguration', "LOWER(IsFor) = '" . Convert::raw2sql($class) . "'")) && ($temporaryClass = DataObject::get_one($class))) { $class = ClassInfo::baseDataClass($temporaryClass->ClassName); $visibility = $configuration->APIwesomeVisibility ? explode(',', $configuration->APIwesomeVisibility) : null; // Validate the sort and filters. $where = array(); $sortValid = is_array($sort) && count($sort) === 2 && ($order = strtoupper($sort[1])) && ($order === 'ASC' || $order === 'DESC'); $filterValid = is_array($filters) && count($filters); $sorting = array(); $filtering = array(); // Grab the appropriate attributes for this data object. if (is_subclass_of($class, 'SiteTree')) { $where[] = "ClassName = '{$class}'"; $class = 'SiteTree'; } else { if (is_subclass_of($class, 'File')) { $where[] = "ClassName = '{$class}'"; $class = 'File'; } } $columns = array(); $from = array(); foreach (ClassInfo::subclassesFor($class) as $subclass) { // Determine the tables to join. $subclassFields = DataObject::database_fields($subclass); if (ClassInfo::hasTable($subclass)) { // Determine the versioned table. $same = $subclass === $class; if ($subclass::has_extension('Versioned')) { $subclass = "{$subclass}_Live"; } if (!$same) { $from[] = $subclass; } } // Prepend the table names. $subclassColumns = array(); foreach ($subclassFields as $column => $type) { $subclassColumn = "{$subclass}.{$column}"; $subclassColumns[$subclassColumn] = $type; // Determine the tables to sort and filter on. if ($sortValid && $sort[0] === $column) { $sorting[] = "{$subclassColumn} {$order}"; } if ($filterValid && isset($filters[$column])) { $filtering[$subclassColumn] = is_numeric($filters[$column]) ? "{$subclassColumn} = " . (int) $filters[$column] : "LOWER({$subclassColumn}) = '" . Convert::raw2sql(strtolower($filters[$column])) . "'"; } } $columns = array_merge($columns, $subclassColumns); } array_shift($columns); // Determine the versioned table. if ($class::has_extension('Versioned')) { $class = "{$class}_Live"; } // Determine ID based sorting and filtering, as these aren't considered database fields. if ($sortValid && $sort[0] === 'ID') { $sorting[] = "{$class}.ID {$order}"; } if ($filterValid && isset($filters['ID'])) { $where[] = "{$class}.ID = " . (int) $filters['ID']; } // Make sure this data object type has visibility customisation. if ($visibility && count($visibility) === count($columns) && in_array('1', $visibility)) { // Apply any visibility customisation. $select = ' '; $iteration = 0; foreach ($columns as $attribute => $type) { if (isset($visibility[$iteration]) && $visibility[$iteration]) { $select .= $attribute . ', '; if (isset($filtering[$attribute])) { // Apply the filter if the matching attribute is visible. $where[] = $filtering[$attribute]; } } $iteration++; } if (isset($filtering["{$class}.ClassName"])) { $where[] = $filtering["{$class}.ClassName"]; } // Grab all data object visible attributes. $query = new SQLQuery("{$class}.ClassName,{$select}{$class}.ID", $class, $where, $sorting, array(), array(), is_numeric($limit) ? $limit : array()); // Determine the tables with visible attributes to join. foreach ($from as $join) { if (strpos($select, " {$join}.") !== false) { $query->addLeftJoin($join, "{$class}.ID = {$join}.ID"); } } $objects = array(); foreach ($query->execute() as $temporary) { // Return an array of data object maps. $object = array(); foreach ($temporary as $attribute => $value) { if ($value) { $object[$attribute] = $value; } } $objects[] = $object; } return $objects; } } // The specified data object type had no visibility customisation. return null; }
private static function truncate_table($table) { DB::alteration_message("Truncating Table {$table}", "deleted"); if (ClassInfo::hasTable($table)) { if (method_exists(DB::getConn(), 'clearTable')) { DB::getConn()->clearTable($table); } else { DB::query("TRUNCATE \"{$table}\""); } } }
/** * Traverse the relationship fields, and add the table * mappings to the query object state. This has to be called * in any overloaded {@link SearchFilter->apply()} methods manually. * * @todo try to make this implicitly triggered so it doesn't have to be manually called in child filters * @param SQLQuery $query * @return SQLQuery */ function applyRelation($query) { if (is_array($this->relation)) { foreach ($this->relation as $rel) { $model = singleton($this->model); if ($component = $model->has_one($rel)) { if (!$query->isJoinedTo($component)) { $foreignKey = $model->getReverseAssociation($component); $query->leftJoin($component, "\"{$component}\".\"ID\" = \"{$this->model}\".\"{$foreignKey}ID\""); /** * add join clause to the component's ancestry classes so that the search filter could search on its * ancester fields. */ $ancestry = ClassInfo::ancestry($component, true); if (!empty($ancestry)) { $ancestry = array_reverse($ancestry); foreach ($ancestry as $ancestor) { if ($ancestor != $component) { $query->innerJoin($ancestor, "\"{$component}\".\"ID\" = \"{$ancestor}\".\"ID\""); $component = $ancestor; } } } } $this->model = $component; } elseif ($component = $model->has_many($rel)) { if (!$query->isJoinedTo($component)) { $ancestry = $model->getClassAncestry(); $foreignKey = $model->getRemoteJoinField($rel); $query->leftJoin($component, "\"{$component}\".\"{$foreignKey}\" = \"{$ancestry[0]}\".\"ID\""); /** * add join clause to the component's ancestry classes so that the search filter could search on its * ancestor fields. */ $ancestry = ClassInfo::ancestry($component, true); if (!empty($ancestry)) { $ancestry = array_reverse($ancestry); foreach ($ancestry as $ancestor) { if ($ancestor != $component) { $query->innerJoin($ancestor, "\"{$component}\".\"ID\" = \"{$ancestor}\".\"ID\""); $component = $ancestor; } } } } $this->model = $component; } elseif ($component = $model->many_many($rel)) { list($parentClass, $componentClass, $parentField, $componentField, $relationTable) = $component; $parentBaseClass = ClassInfo::baseDataClass($parentClass); $componentBaseClass = ClassInfo::baseDataClass($componentClass); $query->innerJoin($relationTable, "\"{$relationTable}\".\"{$parentField}\" = \"{$parentBaseClass}\".\"ID\""); $query->leftJoin($componentBaseClass, "\"{$relationTable}\".\"{$componentField}\" = \"{$componentBaseClass}\".\"ID\""); if (ClassInfo::hasTable($componentClass)) { $query->leftJoin($componentClass, "\"{$relationTable}\".\"{$componentField}\" = \"{$componentClass}\".\"ID\""); } $this->model = $componentClass; // Experimental support for user-defined relationships via a "(relName)Query" method // This will likely be dropped in 2.4 for a system that makes use of Lazy Data Lists. } elseif ($model->hasMethod($rel . 'Query')) { // Get the query representing the join - it should have "$ID" in the filter $newQuery = $model->{"{$rel}Query"}(); if ($newQuery) { // Get the table to join to //DATABASE ABSTRACTION: I don't think we need this line anymore: $newModel = str_replace('`', '', array_shift($newQuery->from)); // Get the filter to use on the join $ancestry = $model->getClassAncestry(); $newFilter = "(" . str_replace('$ID', "\"{$ancestry[0]}\".\"ID\"", implode(") AND (", $newQuery->where)) . ")"; $query->leftJoin($newModel, $newFilter); $this->model = $newModel; } else { $this->name = "NULL"; return; } } } } return $query; }
/** * Checks the database is in a state to perform security checks. * See {@link DatabaseAdmin->init()} for more information. * * @return bool */ public static function database_is_ready() { // Used for unit tests if (self::$force_database_is_ready !== NULL) { return self::$force_database_is_ready; } if (self::$database_is_ready) { return self::$database_is_ready; } $requiredTables = ClassInfo::dataClassesFor('Member'); $requiredTables[] = 'Group'; $requiredTables[] = 'Permission'; foreach ($requiredTables as $table) { // Skip test classes, as not all test classes are scaffolded at once if (is_subclass_of($table, 'TestOnly')) { continue; } // if any of the tables aren't created in the database if (!ClassInfo::hasTable($table)) { return false; } // HACK: DataExtensions aren't applied until a class is instantiated for // the first time, so create an instance here. singleton($table); // if any of the tables don't have all fields mapped as table columns $dbFields = DB::field_list($table); if (!$dbFields) { return false; } $objFields = DataObject::database_fields($table, false); $missingFields = array_diff_key($objFields, $dbFields); if ($missingFields) { return false; } } self::$database_is_ready = true; return true; }
/** * Run the LinkCheckTask. * @todo Split functionality to separate methods */ public function process() { if (class_exists('SapphireTest', false) && !SapphireTest::is_running_test()) { echo "\r\n"; } if (!ClassInfo::hasTable('LinkCheckRun')) { if (!Director::is_ajax() && class_exists('SapphireTest', false) && !SapphireTest::is_running_test()) { echo "Database has not been built. Please run dev/build first!\r\n"; } return false; } // If there is already a LinkCheckRun that exists and is not complete, // don't allow a new run as it could run the server to the ground! // @todo we probably want some system that allows cancelling a check halfway through if (DataObject::get_one('LinkCheckRun', "\"IsComplete\" = 0")) { if (!Director::is_ajax() && class_exists('SapphireTest', false) && !SapphireTest::is_running_test()) { echo "There is already a link check running at the moment. Please wait for it to complete before starting a new one.\r\n"; } return false; } set_time_limit(0); ini_set('max_execution_time', 0); $goodLinks = 0; // 200-299 HTTP status codes $checkLinks = 0; // 300-399 HTTP status codes $brokenLinks = 0; // 400-599 HTTP status codes $pages = DataObject::get('SiteTree'); if (!$pages) { return false; } $run = new LinkCheckRun(); // We have started a new run, create the object and write it $run->write(); $pagesChecked = 0; foreach ($pages as $page) { // Skip this page if it shouldn't be checked if (isset(self::$exempt_classes[get_class($page)])) { continue; } $processor = new LinkCheckProcessor($page->AbsoluteLink()); if (Director::is_ajax()) { $processor->showMessages = false; } $results = $processor->run(); // Memory cleanup - we don't need the processor anymore unset($processor); if ($results) { foreach ($results as $result) { if ($result['Code'] >= 200 && $result['Code'] <= 299) { $goodLinks++; } elseif ($result['Code'] >= 300 && $result['Code'] <= 399) { $checkLinks++; } elseif ($result['Code'] >= 400 && $result['Code'] <= 599) { $brokenLinks++; } // If the result is "Bad" (broken), create a BrokenLink record if ($result['Code'] >= 400 && $result['Code'] <= 599) { $brokenLink = new BrokenLink(); $brokenLink->Link = substr($result['Link'], 0, 255); $brokenLink->Code = $result['Code']; $brokenLink->Status = substr($result['Status'], 0, 30); $brokenLink->LinkCheckRunID = $run->ID; $brokenLink->PageID = $page->ID; $brokenLink->write(); // Memory cleanup $brokenLink->destroy(); } } } // Memory cleanup $page->destroy(); $pagesChecked++; } // Memory cleanup unset($pages); // Mark as done - this is to indicate that the task has completed (for reporting in CMS) $run->FinishDate = date('Y-m-d H:i:s'); $run->IsComplete = 1; $run->PagesChecked = $pagesChecked; $run->write(); // Find the URL to the LinkCheckAdmin section in the CMS $linkcheckAdminLink = Director::absoluteBaseURL() . singleton('LinkCheckAdmin')->Link(); // Count the number of BrokenLink records created for this run $runBrokenLinks = $run->BrokenLinks()->Count() ? $run->BrokenLinks()->Count() : 0; if (Director::is_ajax()) { return array('Date' => $run->obj('Created')->Nice(), 'LinkCheckRunID' => $run->ID); } elseif (Director::is_cli()) { if (class_exists('SapphireTest', false) && SapphireTest::is_running_test()) { return; } echo "SilverStripe Link Checker results\n"; echo "---------------------------------\n\n"; echo "{$pagesChecked} pages were checked for broken links.\n"; echo "{$goodLinks} links were OK.\n"; echo "{$checkLinks} links were redirected.\n"; echo "{$brokenLinks} links were broken, and {$runBrokenLinks} BrokenLink records were generated for them.\n\n"; echo "LinkCheckRun ID #{$run->ID} was created with {$runBrokenLinks} BrokenLink related records.\n"; echo "Please visit {$linkcheckAdminLink} to see which broken links were found.\n\n"; } else { if (class_exists('SapphireTest', false) && SapphireTest::is_running_test()) { return; } echo "<h1>SilverStripe Link Checker results</h1>"; echo '<ul>'; echo "<li>{$pagesChecked} pages were checked for broken links.</li>"; echo "<li>{$goodLinks} links were OK.</li>"; echo "<li>{$checkLinks} links were redirected.</li>"; echo "<li>{$brokenLinks} links were broken, and {$runBrokenLinks} BrokenLink records were generated for them.</li>"; echo '</ul>'; echo "<p>LinkCheckRun ID #{$run->ID} was created with {$runBrokenLinks} BrokenLink related records.</p>"; echo "<p>Please visit <a href=\"{$linkcheckAdminLink}\">{$linkcheckAdminLink}</a> to see which broken links were found.</p>"; } }
/** * @param SS_HTTPRequest $request * @return SS_HTTPResponse */ public function handleRequest(SS_HTTPRequest $request, DataModel $model = null) { self::$is_at_root = true; $this->setDataModel($model); $this->pushCurrent(); $this->init(); if (!DB::isActive() || !ClassInfo::hasTable('SiteTree')) { $this->response = new SS_HTTPResponse(); $this->response->redirect(Director::absoluteBaseURL() . 'dev/build?returnURL=' . (isset($_GET['url']) ? urlencode($_GET['url']) : null)); return $this->response; } $request = new SS_HTTPRequest($request->httpMethod(), self::get_homepage_link() . '/', $request->getVars(), $request->postVars()); $request->match('$URLSegment//$Action', true); $controller = new ModelAsController(); $result = $controller->handleRequest($request, $model); $this->popCurrent(); return $result; }
public function handleRequest(SS_HTTPRequest $request, DataModel $model = null) { self::$is_at_root = true; $this->setDataModel($model); $this->pushCurrent(); $this->init(); if ($language = $request->param('Language')) { if (Config::inst()->get('MultilingualRootURLController', 'UseLocaleURL')) { if (Config::inst()->get('MultilingualRootURLController', 'UseDashLocale')) { //Language is missing a dash 404 if (strpos($language, '-') === false) { //Locale not found 404 if ($response = ErrorPage::response_for(404)) { return $response; } else { $this->httpError(404, 'The requested page could not be found.'); } return $this->response; } $locale = explode('-', $language); $locale[1] = strtoupper($locale[1]); //Make sure that the language is all lowercase if ($language == implode('-', $locale)) { //Locale not found 404 if ($response = ErrorPage::response_for(404)) { return $response; } else { $this->httpError(404, 'The requested page could not be found.'); } return $this->response; } $locale = implode('_', $locale); } else { $locale = $language; } } else { if (strpos($request->param('Language'), '_') !== false) { //Locale not found 404 if ($response = ErrorPage::response_for(404)) { return $response; } else { $this->httpError(404, 'The requested page could not be found.'); } return $this->response; } else { $locale = i18n::get_locale_from_lang($language); } } if (in_array($locale, Translatable::get_allowed_locales())) { Cookie::set('language', $language); Translatable::set_current_locale($locale); i18n::set_locale($locale); if (!DB::isActive() || !ClassInfo::hasTable('SiteTree')) { $this->response = new SS_HTTPResponse(); $this->response->redirect(Director::absoluteBaseURL() . 'dev/build?returnURL=' . (isset($_GET['url']) ? urlencode($_GET['url']) : null)); return $this->response; } $request->setUrl($language . '/' . self::get_homepage_link() . '/'); $request->match('$Language/$URLSegment//$Action', true); $controller = new MultilingualModelAsController(); $result = $controller->handleRequest($request, $model); $this->popCurrent(); return $result; } else { //URL Param Locale is not allowed so redirect to default $this->redirect(Controller::join_links(Director::baseURL(), Config::inst()->get('MultilingualRootURLController', 'UseLocaleURL') ? Translatable::default_locale() : Translatable::default_lang()) . '/', 301); $this->popCurrent(); return $this->response; } } //No Locale Param so detect browser language and redirect if ($locale = self::detect_browser_locale()) { if (Config::inst()->get('MultilingualRootURLController', 'UseLocaleURL')) { if (Config::inst()->get('MultilingualRootURLController', 'UseDashLocale')) { $language = str_replace('_', '-', strtolower($locale)); } else { $language = $locale; } } else { $language = i18n::get_lang_from_locale($locale); } Cookie::set('language', $language); $this->redirect(Controller::join_links(Director::baseURL(), $language) . '/', 301); $this->popCurrent(); return $this->response; } if (Config::inst()->get('MultilingualRootURLController', 'UseLocaleURL')) { if (Config::inst()->get('MultilingualRootURLController', 'UseDashLocale')) { $language = str_replace('_', '-', strtolower(Translatable::default_locale())); } else { $language = Translatable::default_locale(); } } else { $language = Translatable::default_lang(); } $this->redirect(Controller::join_links(Director::baseURL(), $language . '/'), 301); $this->popCurrent(); return $this->response; }
/** * Initialisation function that is run before any action on the controller is called. */ function init() { // Test and development sites should be secured, via basic-auth if (ClassInfo::hasTable("Group") && ClassInfo::hasTable("Member") && Director::isTest() && $this->basicAuthEnabled) { BasicAuth::requireLogin("SilverStripe test website. Use your CMS login", "ADMIN"); } // Cookie::set("PastVisitor", true); // ClassInfo::hasTable() called to ensure that we're not in a very-first-setup stage if (ClassInfo::hasTable("Group") && ClassInfo::hasTable("Member") && ($member = Member::currentUser())) { Cookie::set("PastMember", true); DB::query("UPDATE Member SET LastVisited = NOW() WHERE ID = {$member->ID}", null); } // This is used to test that subordinate controllers are actually calling parent::init() - a common bug $this->baseInitCalled = true; }
/** * Writes all changes to this object to the database. * - It will insert a record whenever ID isn't set, otherwise update. * - All relevant tables will be updated. * - $this->onBeforeWrite() gets called beforehand. * - Extensions such as Versioned will ammend the database-write to ensure that a version is saved. * - Calls to {@link DataObjectLog} can be used to see everything that's been changed. * * @param boolean $showDebug Show debugging information * @param boolean $forceInsert Run INSERT command rather than UPDATE, even if record already exists * @param boolean $forceWrite Write to database even if there are no changes * * @return int The ID of the record */ public function write($showDebug = false, $forceInsert = false, $forceWrite = false) { $firstWrite = false; $this->brokenOnWrite = true; $isNewRecord = false; $this->onBeforeWrite(); if ($this->brokenOnWrite) { user_error("{$this->class} has a broken onBeforeWrite() function. Make sure that you call parent::onBeforeWrite().", E_USER_ERROR); } // New record = everything has changed if ($this->ID && is_numeric($this->ID) && !$forceInsert) { $dbCommand = 'update'; } else { $dbCommand = 'insert'; $this->changed = array(); foreach ($this->record as $k => $v) { $this->changed[$k] = 2; } $firstWrite = true; } // No changes made if ($this->changed) { foreach ($this->getClassAncestry() as $ancestor) { if (ClassInfo::hasTable($ancestor)) { $ancestry[] = $ancestor; } } // Look for some changes to make unset($this->changed['ID']); $hasChanges = false; foreach ($this->changed as $fieldName => $changed) { if ($changed) { $hasChanges = true; break; } } if ($hasChanges || $forceWrite || !$this->record['ID']) { // New records have their insert into the base data table done first, so that they can pass the // generated primary key on to the rest of the manipulation if (!$this->record['ID'] && isset($ancestry[0])) { $baseTable = $ancestry[0]; DB::query("INSERT INTO `{$baseTable}` SET Created = NOW()"); $this->record['ID'] = DB::getGeneratedID($baseTable); $this->changed['ID'] = 2; $isNewRecord = true; } // Divvy up field saving into a number of database manipulations if (isset($ancestry) && is_array($ancestry)) { foreach ($ancestry as $idx => $class) { $classSingleton = singleton($class); foreach ($this->record as $fieldName => $value) { if (isset($this->changed[$fieldName]) && $this->changed[$fieldName] && ($fieldType = $classSingleton->fieldExists($fieldName))) { $manipulation[$class]['fields'][$fieldName] = $value ? "'" . addslashes($value) . "'" : singleton($fieldType)->nullValue(); } } // Add the class name to the base object if ($idx == 0) { $manipulation[$class]['fields']["LastEdited"] = "now()"; if ($dbCommand == 'insert') { $manipulation[$class]['fields']["Created"] = "now()"; //echo "<li>$this->class - " .get_class($this); $manipulation[$class]['fields']["ClassName"] = "'{$this->class}'"; } } // In cases where there are no fields, this 'stub' will get picked up on if (ClassInfo::hasTable($class)) { $manipulation[$class]['command'] = $dbCommand; $manipulation[$class]['id'] = $this->record['ID']; } else { unset($manipulation[$class]); } } } $this->extend('augmentWrite', $manipulation); // New records have their insert into the base data table done first, so that they can pass the // generated ID on to the rest of the manipulation if (isset($isNewRecord) && $isNewRecord && isset($manipulation[$baseTable])) { $manipulation[$baseTable]['command'] = 'update'; } DB::manipulate($manipulation); if (isset($isNewRecord) && $isNewRecord) { DataObjectLog::addedObject($this); } else { DataObjectLog::changedObject($this); } $this->changed = null; } elseif ($showDebug) { echo "<b>Debug:</b> no changes for DataObject<br />"; } // Clears the cache for this object so get_one returns the correct object. $this->flushCache(); if (!isset($this->record['Created'])) { $this->record['Created'] = date('Y-m-d H:i:s'); } $this->record['LastEdited'] = date('Y-m-d H:i:s'); } // Write ComponentSets as necessary if ($this->components) { foreach ($this->components as $component) { $component->write($firstWrite); } } return $this->record['ID']; }