/** * Filters the current content set down based on the filters given * * @param array $filters Filters to use to narrow down content * @return void * @throws Exception */ public function filter($filters) { $hash = Debug::markStart('content', 'filtering'); $filters = Helper::ensureArray($filters); // nothing to filter, abort if (!$this->count()) { return; } $since_date = null; $until_date = null; $remove_hidden = null; $remove_drafts = null; $keep_type = "all"; $folders = null; $conditions = null; $located = false; $where = null; // standardize filters // ------------------- $given_filters = $filters; $filters = array('show_hidden' => isset($given_filters['show_hidden']) ? $given_filters['show_hidden'] : null, 'show_drafts' => isset($given_filters['show_drafts']) ? $given_filters['show_drafts'] : null, 'since' => isset($given_filters['since']) ? $given_filters['since'] : null, 'until' => isset($given_filters['until']) ? $given_filters['until'] : null, 'show_past' => isset($given_filters['show_past']) ? $given_filters['show_past'] : null, 'show_future' => isset($given_filters['show_future']) ? $given_filters['show_future'] : null, 'type' => isset($given_filters['type']) ? strtolower($given_filters['type']) : null, 'folders' => isset($given_filters['folders']) ? $given_filters['folders'] : null, 'conditions' => isset($given_filters['conditions']) ? $given_filters['conditions'] : null, 'located' => isset($given_filters['located']) ? $given_filters['located'] : null, 'where' => isset($given_filters['where']) ? $given_filters['where'] : null); // determine filters // ----------------- if (!is_null($filters['show_hidden'])) { $remove_hidden = !(bool) $filters['show_hidden']; } if (!is_null($filters['show_drafts'])) { $remove_drafts = !(bool) $filters['show_drafts']; } if ($filters['since']) { $since_date = Date::resolve($filters['since']); } if ($filters['show_past'] === false && (!$since_date || $since_date < time())) { $since_date = Config::getEntryTimestamps() ? time() : Date::resolve("today midnight"); } if ($filters['until']) { $until_date = Date::resolve($filters['until']); } if ($filters['show_future'] === false && (!$until_date || $until_date > time())) { $until_date = Config::getEntryTimestamps() ? time() : Date::resolve("tomorrow midnight") - 1; } if ($filters['type'] === "entries" || $filters['type'] === "pages") { $keep_type = $filters['type']; } if ($filters['folders']) { $folders = Parse::pipeList($filters['folders']); } if ($filters['conditions']) { $conditions = Parse::conditions($filters['conditions']); } if ($filters['located']) { $located = true; } if ($filters['where']) { $where = $filters['where']; } // before we run filters, we need to look through conditions if they // were set to see if we're going to need content or content_raw // ----------- if ($conditions) { // check for conditions involving content $uses_content = false; foreach ($conditions as $field => $instructions) { if (strtolower($field) === 'content') { $uses_content = true; break; } } // this uses content, which means we need to load it for all content if ($uses_content) { $this->prepare(true, false); $this->content_parsed = false; } } // run filters // ----------- foreach ($this->content as $key => $data) { // entry or page removal if ($keep_type === "pages" && !$data['_is_page']) { unset($this->content[$key]); continue; } elseif ($keep_type === "entries" && !$data['_is_entry']) { unset($this->content[$key]); continue; } // check for non-public content if ($remove_drafts && $data['_is_draft']) { unset($this->content[$key]); continue; } if ($remove_hidden && $data['_is_hidden']) { unset($this->content[$key]); continue; } // folder if ($folders) { $keep = false; foreach ($folders as $folder) { if ($folder === "*" || $folder === "/*") { // include all $keep = true; break; } elseif (substr($folder, -1) === "*") { // wildcard check if (strpos($data['_folder'], substr($folder, 0, -1)) === 0) { $keep = true; break; } } else { // plain check if ($folder == $data['_folder']) { $keep = true; break; } } } if (!$keep) { unset($this->content[$key]); continue; } } // since & show past if ($since_date && $data['datestamp'] && $data['datestamp'] < $since_date) { unset($this->content[$key]); continue; } // until & show future if ($until_date && $data['datestamp'] && $data['datestamp'] > $until_date) { unset($this->content[$key]); continue; } // where if ($where && !(bool) Parse::template("{{ if " . $where . " }}1{{ else }}0{{ endif }}", $data)) { unset($this->content[$key]); continue; } // conditions if ($conditions) { $case_sensitive_taxonomies = Config::getTaxonomyCaseSensitive(); foreach ($conditions as $field => $instructions) { $optional = substr($field, -1, 1) == '?'; $field = $optional ? trim(substr($field, 0, -1)) : $field; try { // are we looking for existence? if ($instructions['kind'] === "existence") { if ($instructions['type'] === "has") { if (!isset($data[$field]) || !$data[$field]) { throw new Exception("Does not fit condition"); } } elseif ($instructions['type'] === "lacks") { if (isset($data[$field]) && $data[$field]) { throw new Exception("Does not fit condition"); } } else { throw new Exception("Unknown existence type"); } // are we looking for a comparison? } elseif ($instructions['kind'] === "comparison") { $is_taxonomy = Taxonomy::isTaxonomy($field); $case_sensitive = $is_taxonomy && $case_sensitive_taxonomies; if (!isset($data[$field])) { $field = false; $values = null; } else { if ($case_sensitive) { $field = $data[$field]; $values = $instructions['value']; } else { $field = is_array($data[$field]) ? array_map('strtolower', $data[$field]) : strtolower($data[$field]); $values = is_array($instructions['value']) ? array_map('strtolower', $instructions['value']) : strtolower($instructions['value']); } } // convert boolean-like statements to boolean values if (is_array($values)) { foreach ($values as $item => $value) { if ($value == "true" || $value == "yes") { $values[$item] = true; } elseif ($value == "false" || $value == "no") { $values[$item] = false; } } } else { if ($values == "true" || $values == "yes") { $values = true; } elseif ($values == "false" || $values == "no") { $values = false; } } // convert date-like statements to timestamps for qualitative comparisons (not equals) if (in_array($instructions['type'], array('greater than or equal to', 'greater than', 'less than or equal to', 'less than'))) { if (!is_array($field) && !is_numeric($field) && Date::resolve($field) !== false) { $field = Date::resolve($field); } if (!is_array($values) && !is_numeric($values) && Date::resolve($values) !== false) { $values = Date::resolve($values); } } // equal comparisons if ($instructions['type'] == "equal") { // if this isn't set, it's not equal if (!$field) { throw new Exception("Field not set", 1); } if (!is_array($field)) { if ($field != $values) { throw new Exception("Does not fit condition", 0); } } elseif (!in_array($values, $field)) { throw new Exception("Does not fit condition", 0); } // greater than or equal to comparisons } elseif ($instructions['type'] == "greater than or equal to") { // if this isn't set, it's not greater than or equal to if (!$field) { throw new Exception("Field not set", 1); } if (is_array($field) || is_array($values)) { throw new Exception("Does not fit condition", 0); } if (!is_numeric($field) && Date::resolve($field) !== false) { $field = Date::resolve($field); } if (!is_numeric($values) && Date::resolve($values) !== false) { $values = Date::resolve($values); } if (!is_numeric($field) || !is_numeric($values) || $this->toNumber($field) < $this->toNumber($values)) { throw new Exception("Does not fit condition", 0); } // greater than to comparisons } elseif ($instructions['type'] == "greater than") { // if this isn't set, it's not less than if (!$field) { throw new Exception("Field not set", 1); } if (is_array($field) || is_array($values)) { throw new Exception("Does not fit condition", 0); } if (!is_numeric($field) && Date::resolve($field) !== false) { $field = Date::resolve($field); } if (!is_numeric($values) && Date::resolve($values) !== false) { $values = Date::resolve($values); } if (!is_numeric($field) || !is_numeric($values) || $this->toNumber($field) <= $this->toNumber($values)) { throw new Exception("Does not fit condition", 0); } // less than or equal to comparisons } elseif ($instructions['type'] == "less than or equal to") { // if this isn't set, it's not less than or equal to if (!$field) { throw new Exception("Field not set", 1); } if (is_array($field) || is_array($values)) { throw new Exception("Does not fit condition", 0); } if (!is_numeric($field) && Date::resolve($field) !== false) { $field = Date::resolve($field); } if (!is_numeric($values) && Date::resolve($values) !== false) { $values = Date::resolve($values); } if (!is_numeric($field) || !is_numeric($values) || $this->toNumber($field) > $this->toNumber($values)) { throw new Exception("Does not fit condition", 0); } // less than to comparisons } elseif ($instructions['type'] == "less than") { // if this isn't set, it's not less than if (!$field) { throw new Exception("Field not set", 1); } if (is_array($field) || is_array($values)) { throw new Exception("Does not fit condition", 0); } if (!is_numeric($field) && Date::resolve($field) !== false) { $field = Date::resolve($field); } if (!is_numeric($values) && Date::resolve($values) !== false) { $values = Date::resolve($values); } if (!is_numeric($field) || !is_numeric($values) || $this->toNumber($field) >= $this->toNumber($values)) { throw new Exception("Does not fit condition", 0); } // not-equal comparisons } elseif ($instructions['type'] == "not equal") { // if this isn't set, it's not equal, continue if (!$field) { continue; } if (!is_array($field)) { if ($field == $values) { throw new Exception("Does not fit condition", 0); } } elseif (in_array($values, $field)) { throw new Exception("Does not fit condition", 0); } // contains array comparisons } elseif ($instructions['type'] == "in") { if (!$field) { throw new Exception("Field not set", 1); } if (!count(array_intersect(Helper::ensureArray($field), $values))) { throw new Exception("Does not fit condition", 0); } // doesn't contain array comparisons } elseif ($instructions['type'] == "not in") { if (!$field) { throw new Exception("Field not set", 1); } if (count(array_intersect(Helper::ensureArray($field), $values))) { throw new Exception("Does not fit condition", 0); } // contains contains-text comparisons } elseif ($instructions['type'] == 'contains text') { if (!$field) { throw new Exception("Field not set", 1); } $field = Helper::ensureArray($field); $found = false; foreach ($field as $option) { // do we need to loop through values? if (is_array($values)) { // we do foreach ($values as $value) { if (strpos($option, strtolower($value)) !== false) { $found = true; break; } } if ($found) { break; } } else { // we don't if (strpos($option, strtolower($values)) !== false) { $found = true; break; } } } if (!$found) { throw new Exception("Does not fit condition", 0); } } // we don't know what this is } else { throw new Exception("Unknown kind of condition", -1); } } catch (Exception $e) { if ($optional && $e->getCode() === 1) { // we were only making the comparison if the field exists, // otherwise, this is ok continue; } // this was not an optional field, and something went wrong unset($this->content[$key]); continue; } } } // located if ($located && !isset($data['coordinates'])) { unset($this->content[$key]); continue; } } Debug::markEnd($hash); }
?> </label> <div class="field"> <span class="ss-icon">calendar</span> <input name="page[meta][publish-date]" tabindex="<?php print tabindex(); ?> " type="text" id="publish-date" value="<?php print date("Y-m-d", $datestamp); ?> " class="datepicker" /> </div> </div> <?php if (Config::getEntryTimestamps()) { ?> <div class="input-block input-time time required bootstrap-timepicker<?php if (array_get($fields, 'time:hide', false) === true) { ?> hidden<?php } ?> " data-date="<?php print date("h:i a", $timestamp); ?> " data-date-format="h:i a"> <label><?php echo Localization::fetch('publish_time'); ?> </label>
/** * Cleans up a given $path, removing any order keys (date-based or number-based) * * @param string $path Path to clean * @return string */ public static function clean($path) { // remove draft and hidden flags $path = preg_replace("#/_[_]?#", "/", $path); // if we don't want entry timestamps, handle things manually if (!Config::getEntryTimestamps()) { $file = substr($path, strrpos($path, "/")); // trim path if needed if (-strlen($file) + 1 !== 0) { $path = substr($path, 0, -strlen($file) + 1); } $path = preg_replace(Pattern::ORDER_KEY, "", $path); $pattern = preg_match(Pattern::DATE, $file) ? Pattern::DATE : Pattern::ORDER_KEY; $file = preg_replace($pattern, "", $file); return Path::tidy($path . $file); } // otherwise, just remove all order-keys return preg_replace(Pattern::ORDER_KEY, "", $path); }
public static function get_content_tree($directory = '/', $depth = 1, $max_depth = 5, $folders_only = false, $include_entries = false, $hide_hidden = true, $include_content = false, $site_root = false) { // $folders_only = true only page.md // folders_only = false includes any numbered or non-numbered page (excluding anything with a fields.yaml file) // if include_entries is true then any numbered files are included $content_root = Config::getContentRoot(); $content_type = Config::getContentType(); $site_root = $site_root ? $site_root : Config::getSiteRoot(); $current_url = Path::tidy($site_root . '/' . Request::getResourceURI()); $taxonomy_url = false; if (Taxonomy::isTaxonomyURL($current_url)) { list($taxonomy_type, $taxonomy_name) = Taxonomy::getCriteria($current_url); $taxonomy_url = self::remove_taxonomy_from_path($current_url, $taxonomy_type, $taxonomy_name); } $directory = '/' . $directory . '/'; #ensure proper slashing if ($directory != '/') { $base = Path::tidy("{$content_root}/{$directory}"); } elseif ($directory == '/') { $base = "{$content_root}"; } else { $base = "{$content_root}"; } $files = glob("{$base}/*"); $data = array(); if ($files) { foreach ($files as $path) { $current_name = basename($path); if (!Pattern::endsWith($current_name, '.yaml')) { // Hidden page that should be removed if ($hide_hidden && Pattern::startsWith($current_name, '_')) { continue; } $node = array(); $file = substr($path, strlen($base) + 1, strlen($path) - strlen($base) - strlen($content_type) - 2); if (is_dir($path)) { $folder = substr($path, strlen($base) + 1); $node['type'] = 'folder'; $node['slug'] = basename($folder); $node['title'] = ucwords(basename($folder)); $node['numeric'] = Slug::getOrderNumber($folder); $node['file_path'] = Path::tidy($site_root . '/' . $directory . '/' . $folder . '/page'); if (Slug::isNumeric($folder)) { $pos = strpos($folder, "."); if ($pos !== false) { $node['raw_url'] = Path::tidy(Path::clean($site_root . '/' . $directory . '/' . $folder)); $node['url'] = Path::clean($node['raw_url']); $node['title'] = ucwords(basename(substr($folder, $pos + 1))); } else { $node['title'] = ucwords(basename($folder)); $node['raw_url'] = Path::tidy($site_root . '/' . $directory . '/' . $folder); $node['url'] = Path::clean($node['raw_url']); } } else { $node['title'] = ucwords(basename($folder)); $node['raw_url'] = Path::tidy($site_root . '/' . $directory . '/' . $folder); $node['url'] = Path::clean($node['raw_url']); } $node['depth'] = $depth; $node['children'] = $depth < $max_depth ? self::get_content_tree($directory . $folder . '/', $depth + 1, $max_depth, $folders_only, $include_entries, $hide_hidden, $include_content, $site_root) : null; $node['is_current'] = $node['raw_url'] == $current_url || $node['url'] == $current_url ? true : false; $node['is_parent'] = false; if ($node['url'] == URL::popLastSegment($current_url) || $taxonomy_url && $node['url'] == $taxonomy_url) { $node['is_parent'] = true; } $node['has_children'] = $node['children'] ? true : false; // has entries? if (File::exists(Path::tidy($path . "/fields.yaml"))) { $node['has_entries'] = true; } else { $node['has_entries'] = false; } $meta = self::get_content_meta("page", Path::tidy($directory . "/" . $folder), false, true); //$meta = self::get_content_meta("page", Statamic_Helper::reduce_double_slashes($directory."/".$folder)); if (isset($meta['title'])) { $node['title'] = $meta['title']; } if (isset($meta['last_modified'])) { $node['last_modified'] = $meta['last_modified']; } if ($hide_hidden === true && (isset($meta['status']) && ($meta['status'] == 'hidden' || $meta['status'] == 'draft'))) { // placeholder condition } else { $data[] = $include_content ? array_merge($meta, $node) : $node; // print_r($data); } } else { if (Pattern::endsWith($path, $content_type)) { if ($folders_only == false) { if ($file == 'page' || $file == 'feed' || $file == '404') { // $node['url'] = $directory; // $node['title'] = basename($directory); // $meta = self::get_content_meta('page', substr($directory, 1)); // $node['depth'] = $depth; } else { $include = true; // date based is never included if (Config::getEntryTimestamps() && Slug::isDateTime(basename($path))) { $include = false; } elseif (Slug::isDate(basename($path))) { $include = false; } elseif (Slug::isNumeric(basename($path))) { if ($include_entries == false) { if (File::exists(Path::tidy(dirname($path) . "/fields.yaml"))) { $include = false; } } } if ($include) { $node['type'] = 'file'; $node['raw_url'] = Path::tidy($directory) . basename($path); $pretty_url = Path::clean($node['raw_url']); $node['url'] = substr($pretty_url, 0, -1 * (strlen($content_type) + 1)); $node['is_current'] = $node['url'] == $current_url || $node['url'] == $current_url ? true : false; $node['slug'] = substr(basename($path), 0, -1 * (strlen($content_type) + 1)); $meta = self::get_content_meta(substr(basename($path), 0, -1 * (strlen($content_type) + 1)), substr($directory, 1), false, true); //$node['meta'] = $meta; if (isset($meta['title'])) { $node['title'] = $meta['title']; } $node['depth'] = $depth; if ($hide_hidden === true && (isset($meta['status']) && ($meta['status'] == 'hidden' || $meta['status'] == 'draft'))) { } else { $data[] = $include_content ? array_merge($meta, $node) : $node; } } } } } } } } } return $data; }
$data['fields'] = $fieldset_data['fields']; } else { # not set. $data['fields'] = array(); } $data['type'] = isset($fields_data['type']) && !is_array($fields_data['type']) ? $fields_data['type'] : $fields_data['type']['prefix']; // Slug if (Slug::isDraft($page)) { $slug = substr($page, 2); } elseif (Slug::isHidden($page)) { $slug = substr($page, 1); } else { $slug = $page; } if ($data['type'] == 'date') { if (Config::getEntryTimestamps() && Slug::isDateTime($page)) { $data['full_slug'] = $folder; $data['original_slug'] = substr($slug, 16); $data['slug'] = substr($slug, 16); $data['original_datestamp'] = substr($slug, 0, 10); $data['original_timestamp'] = substr($slug, 11, 4); if (!$new) { $data['datestamp'] = strtotime(substr($slug, 0, 10)); $data['timestamp'] = strtotime(substr($slug, 0, 10) . " " . substr($slug, 11, 4)); $data['full_slug'] = $folder . "/" . $data['original_slug']; } } else { $data['full_slug'] = $folder; $data['original_slug'] = substr($slug, 11); $data['slug'] = substr($slug, 11); $data['original_datestamp'] = substr($slug, 0, 10);
/** * Updates the internal content cache * * @return boolean */ public static function update() { // start measuring $content_hash = Debug::markStart('caching', 'content'); // track if any files have changed $files_changed = false; $settings_changed = false; $members_changed = false; // grab length of content type extension $content_type = Config::getContentType(); $full_content_root = rtrim(Path::tidy(BASE_PATH . "/" . Config::getContentRoot()), "/"); $content_type_length = strlen($content_type) + 1; // the cache files we'll use $cache_file = BASE_PATH . '/_cache/_app/content/content.php'; $settings_file = BASE_PATH . '/_cache/_app/content/settings.php'; $structure_file = BASE_PATH . '/_cache/_app/content/structure.php'; $time_file = BASE_PATH . '/_cache/_app/content/last.php'; $members_file = BASE_PATH . '/_cache/_app/members/members.php'; $now = time(); // start measuring settings hash $settings_hash = Debug::markStart('caching', 'settings'); // check for current and new settings $settings = unserialize(File::get($settings_file)); if (!is_array($settings)) { $settings = array('site_root' => '', 'site_url' => '', 'timezone' => '', 'date_format' => '', 'time_format' => '', 'content_type' => '', 'taxonomy' => '', 'taxonomy_case_sensitive' => '', 'taxonomy_force_lowercase' => '', 'entry_timestamps' => '', 'base_path' => '', 'app_version' => ''); } // look up current settings $current_settings = array('site_root' => Config::getSiteRoot(), 'site_url' => Config::getSiteURL(), 'timezone' => Config::get('timezone'), 'date_format' => Config::get('date_format'), 'time_format' => Config::get('time_format'), 'content_type' => Config::get('content_type'), 'taxonomy' => Config::getTaxonomies(), 'taxonomy_case_sensitive' => Config::getTaxonomyCaseSensitive(), 'taxonomy_force_lowercase' => Config::getTaxonomyForceLowercase(), 'entry_timestamps' => Config::getEntryTimestamps(), 'base_path' => BASE_PATH, 'app_version' => STATAMIC_VERSION); // have cache-altering settings changed? if ($settings !== $current_settings) { // settings have changed $settings_changed = true; // clear the cache and set current settings $cache = self::getCleanCacheArray(); $settings = $current_settings; $last = null; } else { // grab the existing cache $cache = unserialize(File::get($cache_file)); if (!is_array($cache)) { $cache = self::getCleanCacheArray(); } $last = File::get($time_file); } // mark end of settings hash measuring Debug::markEnd($settings_hash); // grab a list of all content files $files = File::globRecursively(Path::tidy(BASE_PATH . '/' . Config::getContentRoot() . '/*'), Config::getContentType()); // grab a separate list of files that have changed since last check $updated = array(); $current_files = array(); // loop through files, getting local paths and checking for updated files foreach ($files as $file) { $local_file = Path::trimFilesystemFromContent(Path::standardize($file)); // add to current files $current_files[] = $local_file; // is this updated? if ($last && File::getLastModified($file) >= $last) { $updated[] = $local_file; } } // get a diff of files we know about and files currently existing $known_files = array(); foreach ($cache['urls'] as $url_data) { array_push($known_files, $url_data['path']); } $new_files = array_diff($current_files, $known_files); // create a master list of files that need updating $changed_files = array_unique(array_merge($new_files, $updated)); // store a list of changed URLs $changed_urls = array(); // add to the cache if files have been updated if (count($changed_files)) { $files_changed = true; // build content cache foreach ($changed_files as $file) { $file = $full_content_root . $file; $local_path = Path::trimFilesystemFromContent($file); // before cleaning anything, check for hidden or draft content $is_hidden = Path::isHidden($local_path); $is_draft = Path::isDraft($local_path); // now clean up the path $local_filename = Path::clean($local_path); // file parsing $content = substr(File::get($file), 3); $divide = strpos($content, "\n---"); $front_matter = trim(substr($content, 0, $divide)); $content_raw = trim(substr($content, $divide + 4)); // parse data $data = YAML::parse($front_matter); if ($content_raw) { $data['content'] = 'true'; $data['content_raw'] = 'true'; } // set additional information $data['_file'] = $file; $data['_local_path'] = $local_path; $data['_order_key'] = null; $data['datetimestamp'] = null; // legacy $data['datestamp'] = null; $data['date'] = null; $data['time'] = null; $data['numeric'] = null; $data['last_modified'] = filemtime($file); $data['_is_hidden'] = $is_hidden; $data['_is_draft'] = $is_draft; // get initial slug (may be changed below) $data['slug'] = ltrim(basename($file, "." . $content_type), "_"); // folder $instance = $data['slug'] == 'page' ? 1 : 0; $data['_folder'] = Path::clean($data['_local_path']); $slash = Helper::strrpos_count($data['_folder'], '/', $instance); $data['_folder'] = !$slash ? '' : substr($data['_folder'], 1, $slash - 1); $data['_folder'] = !strlen($data['_folder']) ? "/" : $data['_folder']; $data['_basename'] = $data['slug'] . '.' . $content_type; $data['_filename'] = $data['slug']; $data['_is_entry'] = preg_match(Pattern::ENTRY_FILEPATH, $data['_basename']); $data['_is_page'] = preg_match(Pattern::PAGE_FILEPATH, $data['_basename']); // 404 is special if ($data['_local_path'] === "/404.{$content_type}") { $local_filename = $local_path; // order key: date or datetime } elseif (preg_match(Pattern::DATE_OR_DATETIME, $data['_basename'], $matches)) { // order key: date or datetime $date = $matches[1] . '-' . $matches[2] . '-' . $matches[3]; $time = null; if (Config::getEntryTimestamps() && isset($matches[4])) { $time = substr($matches[4], 0, 2) . ":" . substr($matches[4], 2); $date = $date . " " . $time; $data['slug'] = substr($data['slug'], 16); $data['datetimestamp'] = $data['_order_key']; } else { $data['slug'] = substr($data['slug'], 11); } $data['_order_key'] = strtotime($date); $data['datestamp'] = $data['_order_key']; $data['date'] = Date::format(Config::getDateFormat(), $data['_order_key']); $data['time'] = $time ? Date::format(Config::getTimeFormat(), $data['_order_key']) : null; // order key: slug is page, back up a level } elseif ($data['slug'] == 'page' && preg_match(Pattern::NUMERIC, substr($data['_local_path'], Helper::strrpos_count($data['_local_path'], '/', 1)), $matches)) { // order key: slug is page, back up a level $data['_order_key'] = $matches[1]; $data['numeric'] = $data['_order_key']; // order key: numeric } elseif (preg_match(Pattern::NUMERIC, $data['_basename'], $matches)) { // order key: numeric $data['_order_key'] = $matches[1]; $data['numeric'] = $data['_order_key']; $data['slug'] = substr($data['slug'], strlen($matches[1]) + 1); // order key: other } else { // order key: other $data['_order_key'] = $data['_basename']; } // determine url $data['url'] = preg_replace('#/__?#', '/', $local_filename); // remove any content type extensions from the end of filename if (substr($data['url'], -$content_type_length) === '.' . $content_type) { $data['url'] = substr($data['url'], 0, strlen($data['url']) - $content_type_length); } // remove any base pages from filename if (substr($data['url'], -5) == '/page') { $data['url'] = substr($data['url'], 0, strlen($data['url']) - 5); } // add the site root $data['url'] = Path::tidy(Config::getSiteRoot() . $data['url']); // add the site URL to get the permalink $data['permalink'] = Path::tidy(Config::getSiteURL() . $data['url']); // new content if (!isset($cache['content'][$data['_folder']]) || !is_array($cache['content'][$data['_folder']])) { $cache['content'][$data['_folder']] = array(); } $slug_with_extension = $data['_filename'] == 'page' ? substr($data['url'], strrpos($data['url'], '/') + 1) . '/' . $data['_filename'] . "." . $content_type : $data['_filename'] . "." . $content_type; $cache['content'][$data['_folder']][$slug_with_extension] = array('folder' => $data['_folder'], 'path' => $local_path, 'file' => $slug_with_extension, 'url' => $data['url'], 'data' => $data); $cache['urls'][$data['url']] = array('folder' => $data['_folder'], 'path' => $local_path, 'file' => $slug_with_extension); $changed_urls[$data['url']] = true; } } // loop through all cached content for deleted files // this isn't as expensive as you'd think in real-world situations foreach ($cache['content'] as $folder => $folder_contents) { foreach ($folder_contents as $path => $data) { if (File::exists($full_content_root . $data['path'])) { // still here, keep it continue; } $files_changed = true; // get URL $url = isset($cache['content'][$folder][$path]['url']) ? $cache['content'][$folder][$path]['url'] : null; // only remove from URLs list if not in changed URLs list if (!isset($changed_urls[$url]) && !is_null($url)) { // remove from url cache unset($cache['urls'][$url]); } // remove from content cache unset($cache['content'][$folder][$path]); } } // build taxonomy cache // only happens if files were added, updated, or deleted above if ($files_changed) { $taxonomies = Config::getTaxonomies(); $force_lowercase = Config::getTaxonomyForceLowercase(); $case_sensitive = Config::getTaxonomyCaseSensitive(); $cache['taxonomies'] = array(); // rebuild taxonomies if (count($taxonomies)) { // set up taxonomy array foreach ($taxonomies as $taxonomy) { $cache['taxonomies'][$taxonomy] = array(); } // loop through content to build cached array foreach ($cache['content'] as $pages) { foreach ($pages as $item) { $data = $item['data']; // loop through the types of taxonomies foreach ($taxonomies as $taxonomy) { // if this file contains this type of taxonomy if (isset($data[$taxonomy])) { $values = Helper::ensureArray($data[$taxonomy]); // add the file name to the list of found files for a given taxonomy value foreach ($values as $value) { if (!$value) { continue; } $key = !$case_sensitive ? strtolower($value) : $value; if (!isset($cache['taxonomies'][$taxonomy][$key])) { $cache['taxonomies'][$taxonomy][$key] = array('name' => $force_lowercase ? strtolower($value) : $value, 'files' => array()); } array_push($cache['taxonomies'][$taxonomy][$key]['files'], $data['url']); } } } } } } // build structure cache $structure = array(); $home = Path::tidy('/' . Config::getSiteRoot() . '/'); foreach ($cache['content'] as $pages) { foreach ($pages as $item) { // set up base variables $parent = null; // Trim off home and any /page.md ending so that all URLs are treated // equally regardless of page type. $order_key = str_replace('/page.md', '', str_replace($home, '', $item['path'])); $sub_order_key = $item['data']['_order_key']; // does this have a parent (and if so, what is it?) if ($item['url'] !== $home) { $parent = $home; $depth = substr_count(str_replace($home, '/', $item['url']), '/'); $last_slash = strrpos($item['url'], '/', 1); $last_order_slash = strrpos($order_key, '/', 0); if ($last_slash !== false) { $parent = substr($item['url'], 0, $last_slash); } if ($last_order_slash !== false) { $order_key = substr($order_key, 0, $last_order_slash); } if ($item['data']['_is_page']) { $type = $item['data']['slug'] == 'page' ? 'folder' : 'page'; } else { $type = 'entry'; } } else { $depth = 0; $type = 'folder'; $order_key = $home; } $structure[$item['url']] = array('parent' => $parent, 'is_entry' => $item['data']['_is_entry'], 'is_page' => $item['data']['_is_page'], 'is_hidden' => $item['data']['_is_hidden'], 'is_draft' => $item['data']['_is_draft'], 'depth' => $depth, 'order_key' => $order_key ? $order_key : $sub_order_key, 'sub_order_key' => $sub_order_key, 'type' => $type); } } } // mark ending of content cache measuring Debug::markEnd($content_hash); if (!Config::get('disable_member_cache')) { // build member cache // ---------------------------------------------------------------- // start measuring $member_hash = Debug::markStart('caching', 'member'); // grab a list of existing members $users = File::globRecursively(Path::tidy(Config::getConfigPath() . '/users/*'), 'yaml'); // clone for reuse, set up our list of updated users $updated = array(); $current_users = array(); foreach ($users as $user) { $local_file = Path::trimFilesystemFromContent(Path::standardize($user)); // add to current users $current_users[] = $local_file; // is this updated? if ($last && File::getLastModified($user) >= $last) { $updated[] = $local_file; } } // get users from the file $members = unserialize(File::get($members_file)); // get a diff of users we know about and files currently existing $known_users = array(); if (!empty($members)) { foreach ($members as $username => $member_data) { $known_users[$username] = $member_data['_path']; } } // create a master list of users that need updating $changed_users = array_unique(array_merge(array_diff($current_users, $known_users), $updated)); $removed_users = array_diff($known_users, $current_users); if (count($changed_users)) { $members_changed = true; foreach ($changed_users as $user_file) { // file parsing $last_slash = strrpos($user_file, '/') + 1; $last_dot = strrpos($user_file, '.'); $username = substr($user_file, $last_slash, $last_dot - $last_slash); $content = substr(File::get($user_file), 3); $divide = strpos($content, "\n---"); $data = YAML::parse(trim(substr($content, 0, $divide))); $bio_raw = trim(substr($content, $divide + 4)); $data['_path'] = $user_file; if ($bio_raw) { $data['biography'] = 'true'; $data['biography_raw'] = 'true'; } $members[$username] = $data; } } // loop through all cached content for deleted files // this isn't as expensive as you'd think in real-world situations if (!empty($removed_users)) { $members_changed = true; $members = array_diff_key($members, $removed_users); } // mark ending of member cache measuring Debug::markEnd($member_hash); } // write to caches // -------------------------------------------------------------------- // add file-writing to content-cache actions $content_hash = Debug::markStart('caching', 'content'); if ($files_changed) { // store the content cache if (File::put($cache_file, serialize($cache)) === false) { if (!File::isWritable($cache_file)) { Log::fatal('Cache folder is not writable.', 'core', 'content-cache'); } Log::fatal('Could not write to the cache.', 'core', 'content-cache'); return false; } // store the structure cache if (File::put($structure_file, serialize($structure)) === false) { if (!File::isWritable($structure_file)) { Log::fatal('Structure cache file is not writable.', 'core', 'structure-cache'); } Log::fatal('Could not write to the structure cache.', 'core', 'structure-cache'); return false; } } // mark ending of content cache file write measuring Debug::markEnd($content_hash); // add file-writing to settings-cache actions $settings_hash = Debug::markStart('caching', 'settings'); // store the settings cache if ($settings_changed) { if (File::put($settings_file, serialize($settings)) === false) { if (!File::isWritable($settings_file)) { Log::fatal('Settings cache file is not writable.', 'core', 'settings-cache'); } Log::fatal('Could not write to the settings cache file.', 'core', 'settings-cache'); return false; } } // mark ending of settings cache file write measuring Debug::markEnd($settings_hash); if (!Config::get('disable_member_cache')) { // add file-writing to settings-cache actions $member_hash = Debug::markStart('caching', 'member'); // store the members cache if ($members_changed) { if (File::put($members_file, serialize($members)) === false) { if (!File::isWritable($members_file)) { Log::fatal('Member cache file is not writable.', 'core', 'member-cache'); } Log::fatal('Could not write to the member cache file.', 'core', 'member-cache'); return false; } } // mark ending of member cache file write measuring Debug::markEnd($member_hash); } File::put($time_file, $now - 1); return true; }
/** * Finds a given path on the server, adding in any ordering elements missing * * @param string $path Path to resolve * @return string */ public static function resolve($path) { $content_root = Config::getContentRoot(); $content_type = Config::getContentType(); if (strpos($path, "/") === 0) { $parts = explode("/", substr($path, 1)); } else { $parts = explode("/", $path); } $fixedpath = "/"; foreach ($parts as $part) { if (!File::exists(URL::assemble($content_root, $path . '.' . $content_type)) && !is_dir(URL::assemble($content_root, $part))) { // check folders $list = Statamic::get_content_tree($fixedpath, 1, 1, FALSE, TRUE, FALSE); foreach ($list as $item) { $t = basename($item['slug']); if (Slug::isNumeric($t)) { $nl = strlen(Slug::getOrderNumber($t)) + 1; if (strlen($part) >= strlen($item['slug']) - $nl && Pattern::endsWith($item['slug'], $part)) { $part = $item['slug']; break; } } else { if (Pattern::endsWith($item['slug'], $part)) { if (strlen($part) >= strlen($t)) { $part = $item['slug']; break; } } } } // check files $list = Statamic::get_file_list($fixedpath); foreach ($list as $key => $item) { if (Pattern::endsWith($key, $part)) { $t = basename($item); $offset = 0; if (Pattern::startsWith($key, '__')) { $offset = 2; } elseif (Pattern::startsWith($key, '_')) { $offset = 1; } if (Config::getEntryTimestamps() && Slug::isDateTime($t)) { if (strlen($part) >= strlen($key) - 16 - $offset) { $part = $key; break; } } elseif (Slug::isDate($t)) { if (strlen($part) >= strlen($key) - 12 - $offset) { $part = $key; break; } } elseif (Slug::isNumeric($t)) { $nl = strlen(Slug::getOrderNumber($key)) + 1; if (strlen($part) >= strlen($key) - $nl - $offset) { $part = $key; break; } } else { $t = basename($item); if (strlen($part) >= strlen($t) - $offset) { $part = $key; break; } } } } } if ($fixedpath != '/') { $fixedpath .= '/'; } $fixedpath .= $part; } // /2-blog/hidden return $fixedpath; }