isWritable() public static method

Determine if the given path is writable.
public static isWritable ( string $path ) : boolean
$path string
return boolean
Example #1
0
 /**
  * Tests File::isWritable
  */
 public function testIsWritable()
 {
     $spl = $this->getDeletableFileInfo();
     $file = new File($spl);
     $writable = $file->isWritable();
     unlink($spl->getPathname());
     $this->assertEquals(true, $writable);
 }
 public function systemConfig()
 {
     $sc = new SystemConfig();
     $workRoot = trim(Input::get('workRoot'));
     //        if (File::isWritable($workRoot)) {
     //            $sc->set(SystemConfig::WORK_ROOT_FIELD, $workRoot);
     //        } else {
     //            return Response::json(array(
     //                'res' => 1,
     //                'errMsg' => 'Work Root目录不可写!',
     //            ));
     //        }
     $sc->set(SystemConfig::WORK_ROOT_FIELD, $workRoot);
     return Response::json(array('res' => 0, 'write' => File::isWritable($workRoot)));
 }
 function action()
 {
     $FileManager =& $this->_Parent->ExtensionManager->create('filemanager');
     $file = new File(DOCROOT . $FileManager->getStartLocation() . $_GET['file']);
     if (isset($_POST['action']['save'])) {
         $fields = $_POST['fields'];
         $file->setName($fields['name']);
         if (isset($fields['contents'])) {
             $file->setContents($fields['contents']);
         }
         $file->setPermissions($fields['permissions']);
         $relpath = str_replace(DOCROOT . $FileManager->getStartLocation(), NULL, dirname($_GET['file']));
         if ($file->isWritable()) {
             redirect($FileManager->baseURL() . 'properties/?file=' . rtrim(dirname($_GET['file']), '/') . '/' . $file->name() . '&result=saved');
         } else {
             redirect($FileManager->baseURL() . 'browse/' . $relpath);
         }
     } elseif (isset($_POST['action']['delete'])) {
         General::deleteFile($file->path() . '/' . $file->name());
         $relpath = str_replace(DOCROOT . $FileManager->getStartLocation(), NULL, dirname($_GET['file']));
         redirect($FileManager->baseURL() . 'browse/' . $relpath);
     }
 }
    $tpl->set_filenames(array('show' => 'tpl/install/check_directories.tpl'));
    $tpl->assign_vars(array('LANGUAGE' => $config['language'], 'SESSION_ID' => session_id()));
    $checkDirs = array('work/', 'work/config/', 'work/log/', 'work/backup/');
    $isWritable = array();
    if (1 == $config['safe_mode']) {
        $tpl->assign_block_vars('SAFE_MODE_ON', array());
    }
    $i = 0;
    foreach ($checkDirs as $dir) {
        clearstatcache();
        if (!is_dir($dir)) {
            @mkdir($dir);
        }
        clearstatcache();
        $check = file_exists($dir);
        $isWritable[$i] = File::isWritable($dir, 0777);
        $tpl->assign_vars(array('LANGUAGE' => $config['language'], 'ICON_OK' => $icon['ok']));
        $tpl->assign_block_vars('DIR', array('NAME' => $config['paths']['root'] . $dir, 'CHMOD' => File::getChmod($dir), 'ICON_EXISTS' => $check === true ? $icon['ok'] : $icon['not_ok'] . ' ' . $check, 'ICON_IS_WRITABLE' => $isWritable[$i] ? $icon['ok'] : $icon['not_ok']));
        $i++;
    }
    if (in_array(false, $isWritable)) {
        $tpl->pparse('show');
        // something is wrong with the dirs -> show info
    } else {
        $phase = 2;
        // everything ok -> continue with db-param-input
    }
}
// Step 3: database parameters
if ($phase == 2) {
    $tpl = new MSDTemplate();
Example #5
0
 function perform()
 {
     // fetch the data needed from the request
     $this->_dbServer = $this->_request->getValue("dbServer");
     $this->_dbUser = $this->_request->getValue("dbUser");
     $this->_dbPassword = $this->_request->getValue("dbPassword");
     $this->_dbName = $this->_request->getValue("dbName");
     $this->_skipThis = $this->_request->getValue("skipDbInfo");
     $this->_dbPrefix = $this->_request->getValue("dbPrefix", DEFAULT_DB_PREFIX);
     // we should now save the data to the configuration file, just before
     // we read it
     $configFile = new ConfigFileStorage();
     // we expect everything to be fine
     $errors = false;
     // before doing anything, we should check of the configuration file is
     // writable by this script, or else, throw an error and bail out gracefully
     $configFileName = $configFile->getConfigFileName();
     if (!File::exists($configFileName)) {
         if (!File::touch($configFileName)) {
             $this->_view = new WizardView("intro");
             $message = "Could not create the pLog configuration file {$configFileName}. Please make sure\n                                that the file can be created by the user running the webserver. It is needed to\n                                store the database configuration settings.";
             $this->_view->setErrorMessage($message);
             $this->setCommonData(true);
             return false;
         } else {
             ConfigFileStorage::createConfigFile($configFileName);
         }
     }
     if (File::exists($configFileName) && !File::isWritable($configFileName)) {
         $this->_view = new WizardView("intro");
         $message = "Please make sure that the file {$configFileName} can be written by this script during\n                            the installation process. It is needed to store the database configuration settings. Once the\n                            installation is complete, please revert the permissions to no writing possible.";
         $this->_view->setErrorMessage($message);
         $this->setCommonData(true);
         return false;
     }
     // continue if everything went fine
     if (!$configFile->saveValue("db_username", $this->_dbUser) || !$configFile->saveValue("db_password", $this->_dbPassword) || !$configFile->saveValue("db_host", $this->_dbServer) || !$configFile->saveValue("db_database", $this->_dbName) || !$configFile->saveValue("db_prefix", $this->_dbPrefix)) {
         $errors = true;
     }
     if ($errors) {
         $message = "Could not save values to the configuration file. Please make sure it is available and\n                            that has write permissions for the user under your web server is running.";
         $this->_view = new WizardView("intro");
         $this->_view->setErrorMessage($message);
         return false;
     } else {
         $this->_view = new WizardView("step1");
         // now we better read the information from the config file to make sure that
         // it has been correctly saved
         $this->setCommonData(true);
         return true;
     }
 }
 /**
  * Removes a locale from the system. First the file containing it is deleted
  * and then we also remove its entry from the configuration.
  *
  * @param localeCode The code of the locale that we would like to delete.
  * @return Returns true if the locale was removed succesfully or false otherwise.
  */
 function removeLocale($localeCode)
 {
     $config =& Config::getConfig();
     // if we don't have permissions on the folder where the locale files
     // are stored, we don't even have to bother about it...
     if (!File::isWritable(Locale::getLocaleFolder())) {
         return false;
     }
     // does the locale really exist?
     if (!$this->isValidLocale($localeCode)) {
         return false;
     }
     $fileName = $this->getLocaleFilename($localeCode);
     if (File::exists($fileName)) {
         if (!unlink($fileName)) {
             return false;
         }
     }
     $availableLocales = $config->getValue("locales");
     $newLocaleList = array();
     foreach ($availableLocales as $locale) {
         if ($locale != $localeCode) {
             array_push($newLocaleList, $locale);
         }
     }
     $config->saveValue("locales", $newLocaleList);
     return true;
 }
Example #7
0
 /**
  * Execute the console command.
  *
  * @return mixed
  */
 public function fire()
 {
     // Certain stages of this installer will require variables
     // to be opulated, which will eventually be used to
     // write the resultant configuration file. Some
     // stages require testing too, so lets
     // prepare some defaults that will
     // be used.
     $configuration = array('mysql_hostname' => '127.0.0.1', 'mysql_database' => 'seat', 'mysql_username' => 'root', 'mysql_password' => '', 'redis_host' => '127.0.0.1', 'redis_port' => 6379, 'mail_driver' => 'mail', 'mail_from' => 'seatadmin@localhost', 'mail_from_name' => 'SeAT Administrator', 'smtp_hostname' => '127.0.0.1', 'smtp_username' => null, 'smtp_password' => null, 'smtp_port' => 25, 'smtp_encryption' => null);
     $this->info('[+] Welcome to the SeAT v' . \Config::get('seat.version') . ' installer!');
     $this->line('');
     // The very first thing we will be checking is the existence of
     // the .env.php file which contains the configuration of a
     // installed system. If this file exists, we will assume
     // that SeAT is already installed and exit.
     if (\File::exists(base_path() . '/.installed.lck')) {
         $this->error('[!] It appears as if SeAT is already installed. Exiting.');
         return;
     }
     // Next, we will check that we will eventually be able to write
     // to the .env.php in base_path() /.env.php.
     if (!\File::isWritable(base_path() . '/.env.php')) {
         $this->error('[!] The installer needs to be able to write a configuration file to ' . base_path() . '/.env.php, but it appears as though it can not write there.');
         return;
     }
     // Knowing that we can write the configuration file, we move on to
     // getting the details for the database. We will try with the
     // defaults, and if that fails, continue to ask for details
     // until we can connect
     $this->info('[+] Database setup...');
     $this->info('[+] Please enter the details for the MySQL database to use (enter to use default):');
     $error_count = 0;
     while (true) {
         // Ask for the MySQL credentials.
         $configuration['mysql_username'] = $this->ask('[?] Username (' . $configuration['mysql_username'] . '):') ?: $configuration['mysql_username'];
         $configuration['mysql_password'] = $this->secret('[?] Password:'******'mysql_password'];
         $configuration['mysql_hostname'] = $this->ask('[?] Hostname (' . $configuration['mysql_hostname'] . '):') ?: $configuration['mysql_hostname'];
         $configuration['mysql_database'] = $this->ask('[?] Database (' . $configuration['mysql_database'] . '):') ?: $configuration['mysql_database'];
         // Set the runtime configuration that we have
         \Config::set('database.connections.mysql.host', $configuration['mysql_hostname']);
         \Config::set('database.connections.mysql.database', $configuration['mysql_database']);
         \Config::set('database.connections.mysql.username', $configuration['mysql_username']);
         \Config::set('database.connections.mysql.password', $configuration['mysql_password']);
         // Test the database connection
         try {
             \DB::reconnect();
             \DB::connection()->getDatabaseName();
             $this->info('[+] Successfully connected to the MySQL database.');
             $this->line('');
             // If the connection worked, we don't have to ask for anything
             // and just move on to the next section
             break;
         } catch (\Exception $e) {
             $error_count++;
             // Check if we have had more than 3 errors now.
             if ($error_count >= 3) {
                 $this->error('[!] 3 attempts to connect to the database failed.');
                 $this->error('[!] Please ensure that you have a MySQL server with a database ready for SeAT to use before installation.');
                 return;
             }
             $this->error('[!] Unable to connect to the database with mysql://' . $configuration['mysql_username'] . '@' . $configuration['mysql_hostname'] . '/' . $configuration['mysql_database']);
             $this->error('[!] Please re-enter the configuration to try again.');
             $this->error('[!] MySQL said: ' . $e->getMessage());
             $this->line('');
             $this->info('[+] Please re-enter the MySQL details below:');
         }
     }
     // Now that we have a working database connection, move
     // on to the Redis configuration. We will follow a
     // similar path of a infinite loop until it works
     $this->info('[+] Redis cache setup...');
     $this->info('[+] Please enter the details for the Redis cache to use (enter to use default):');
     $error_count = 0;
     while (true) {
         // Ask for the Redis details.
         $configuration['redis_host'] = $this->ask('[?] Host (' . $configuration['redis_host'] . '):') ?: $configuration['redis_host'];
         $configuration['redis_port'] = $this->ask('[?] Port (' . $configuration['redis_port'] . '):') ?: $configuration['redis_port'];
         // Set the \Config for this one runtime to test the connection
         \Config::set('database.redis.default.host', $configuration['redis_host']);
         \Config::set('database.redis.default.port', $configuration['redis_port']);
         // Test that we can add and remove keys from the cache
         try {
             \Cache::put('installer_test', true, 60 * 24);
             \Cache::forget('installer_test');
             $this->info('[+] Successfully connected to the Redis cache.');
             $this->line('');
             // If the connection worked and we were able to place
             // and delete a key, move on to the next section
             break;
         } catch (\Exception $e) {
             $error_count++;
             // Check if we have had more than 3 errors now.
             if ($error_count >= 3) {
                 $this->error('[!] 3 attempts to connect to redis failed.');
                 $this->error('[!] Please ensure that you have a Redis server ready for SeAT to use before installation.');
                 return;
             }
             $this->error('[!] Unable to connect to the redis cache at tcp://' . $configuration['redis_host'] . ':' . $configuration['redis_port']);
             $this->error('[!] Please re-enter the configuration to try again.');
             $this->error('[!] Redis said: ' . $e->getMessage());
             $this->line('');
             $this->info('[+] Please re-enter the Redis details below:');
         }
     }
     // We now have MySQL + Redis setup and ready. Lets move on the
     // the email configurations. If we have mail/sendmail as
     // the config, its easy. However, if we use SMTP, we
     // need to ask the user for credentials too, incase
     // those are needed. We will also start another
     // infinite loop to allow the user to confirm
     // that the details they entered is correct.
     $this->info('[+] Mail setup...');
     $this->info('[+] Please enter the details for the email configuration to use (enter to use default):');
     while (true) {
         // Ask for the email details
         $configuration['mail_driver'] = $this->ask('[?] How are emails going to be sent? [mail/sendmail/smtp] (' . $configuration['mail_driver'] . '):') ?: $configuration['mail_driver'];
         // Check the option we got. If it is not in the array of
         // known configuration, we return to the question
         if (!in_array($configuration['mail_driver'], array('mail', 'sendmail', 'smtp'))) {
             $this->error('[!] The driver you have chosen is not recognized, please try again.');
             continue;
         }
         // Get the details about where emails will be coming from
         $configuration['mail_from'] = $this->ask('[?] Where will emails be coming from? (' . $configuration['mail_from'] . '):') ?: $configuration['mail_from'];
         $configuration['mail_from_name'] = $this->ask('[?] Who will emails be coming from? (' . $configuration['mail_from_name'] . '):') ?: $configuration['mail_from_name'];
         // If the configuration option is set as smtp, we need to
         // give the option to set the username and password
         if ($configuration['mail_driver'] == 'smtp') {
             $configuration['smtp_hostname'] = $this->ask('[?] SMTP Hostname (' . $configuration['smtp_hostname'] . '):') ?: $configuration['smtp_hostname'];
             $configuration['smtp_username'] = $this->ask('[?] SMTP Username (' . $configuration['smtp_username'] . '):') ?: $configuration['smtp_username'];
             $configuration['smtp_password'] = $this->secret('[?] SMTP Password:'******'smtp_password'];
             $configuration['smtp_port'] = $this->ask('[?] SMTP Port (' . $configuration['smtp_port'] . '):') ?: $configuration['smtp_port'];
             $configuration['smtp_encryption'] = $this->ask('[?] SMTP Encryption (' . $configuration['smtp_encryption'] . '):') ?: $configuration['smtp_encryption'];
         }
         // Print the values and get confirmation that they are correct
         $this->line('');
         $this->line('[+] Mail configuration summary:');
         $this->line('[+]    Mail Driver: ' . $configuration['mail_driver']);
         // If we are going to be using the SMTP driver, show the
         // values for the host/user/pass
         if ($configuration['mail_driver'] == 'smtp') {
             $this->line('[+]    SMTP Host: ' . $configuration['smtp_hostname']);
             $this->line('[+]    SMTP Username: '******'smtp_username']);
             $this->line('[+]    SMTP Password: '******'*', strlen($configuration['smtp_password'])));
             $this->line('[+]    SMTP Port: ' . $configuration['smtp_port']);
             $this->line('[+]    SMTP Encryption: ' . $configuration['smtp_encryption']);
         }
         $this->line('');
         if ($this->confirm('[?] Are the above mail settings correct? [yes/no]', true)) {
             break;
         } else {
             continue;
         }
     }
     // With the configuration done, lets attempt to write this to
     // to disk
     if (!$this->writeConfig($configuration)) {
         $this->error('[!] Writing the configuration file failed!');
         return;
     }
     $this->info('[+] Successfully wrote the configuration file');
     // With configuration in place, lets move on to preparing SeAT
     // for use. We have to do a few things for which most
     // already have commands. So, lets re-use those
     // meaning that if they change the intaller
     // is already up to date.
     // Run the database migrations
     $this->info('[+] Running the database migrations...');
     $this->call('migrate');
     // Run the database seeds
     $this->info('[+] Running the database seeds...');
     $this->call('db:seed');
     // Update the SDEs
     $this->info('[+] Updating to the latest EVE SDE\'s...');
     $this->call('seat:update-sde', array('--confirm' => null));
     // Configure the admin user
     $this->info('[+] Configuring the \'admin\' user...');
     $this->call('seat:reset');
     // Sync the access groups
     $this->info('[+] Syncing the access groups...');
     $this->call('seat:groupsync');
     $this->line('');
     // Regenerate the Application Encryption key
     $this->info('[+] Regenerating the Encryption Key');
     $this->call('key:generate');
     $this->line('');
     // Finally, write the installer lock file!
     $lock_file_write = \File::put(base_path() . '/.installed.lck', 'Installed ' . date('Y-m-d H:i:s'));
     // Check that we wrote the lock file successfully
     if (!$lock_file_write) {
         $this->error('[!] Was not able to write the installation lock file! Please touch \'installed.lck\'.');
     }
     $this->info('[+] Done!');
 }
 /**
  * Sets the folder where sessions should be saved, in case we'd like to save
  * them somewhere else. This class will check the config parameter <b>session_save_path</b>: if
  * it's not empty, it will use its value as the name of the folder where sessions should be saved, and it
  * will also take care of creating the folder if it does not exist. If the folder exists but it cannot
  * be read, it will throw an exception and quit (because this is a big issue)
  * If the value of this parameter is empty, it will not do anything and use PHP's default settings for this.
  *
  * @static
  */
 function setSessionSavePath()
 {
     $config =& Config::getConfig();
     $sessionFolder = $config->getValue("session_save_path");
     // do we need to do anything if we are using the default
     // session path?  PHP defaults to /tmp/, so there isn't
     // anything to do
     if (isset($sessionFolder)) {
         if (!File::exists($sessionFolder)) {
             // create folder with only user permissions
             // since we want to protect the session data
             if (!File::createDir($sessionFolder, 0700)) {
                 throw new Exception("Sessions should be " . "saved in {$sessionFolder} but it " . "doesn't exist and I can't create it!");
                 die;
             }
         }
         // check if the folder is accessible
         if (!File::isReadable($sessionFolder) || !File::isWritable($sessionFolder)) {
             if (!File::chMod($sessionFolder, 0700)) {
                 throw new Exception("Sessions should be " . "saved in {$sessionFolder} but it is " . "not accessible!");
                 die;
             }
         }
         // if everything ok, we can continue...
         session_save_path($sessionFolder);
     }
     return true;
 }
Example #9
0
     $fieldset_data = $fs->get_data();
     $data['fields'] = $fieldset_data['fields'];
 } else {
     # not set.
     $data['fields'] = array();
 }
 /*
 |--------------------------------------------------------------------------
 | Check if file is writable
 |--------------------------------------------------------------------------
 |
 | We now have a file name. Let's check if we can write to this thing.
 | If not, throw an error page
 |
 */
 if (!Statamic::is_content_writable() || File::exists($file) && !File::isWritable($file)) {
     $url = $admin_app->urlFor('error') . "?code=content_not_writable";
     $admin_app->redirect($url);
 }
 /*
 |--------------------------------------------------------------------------
 | Fieldset defaults
 |--------------------------------------------------------------------------
 |
 | We need to bring in the fieldset so we know what we're working with
 |
 */
 $fieldset = null;
 $field_settings = array();
 if (count($data['fields']) < 1 && File::exists("{$content_root}/{$folder}/fields.yaml")) {
     $fields_raw = File::get("{$content_root}/{$folder}/fields.yaml");
Example #10
0
File: cache.php Project: nob/joi
 /**
  * Updates the internal content cache
  *
  * @return boolean
  */
 public static function update()
 {
     // track if any files have changed
     $files_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 file we'll use
     $cache_file = BASE_PATH . "/_cache/_app/content/content.php";
     $time_file = BASE_PATH . "/_cache/_app/content/last.php";
     $now = time();
     // grab the existing cache
     $cache = unserialize(File::get($cache_file));
     if (!is_array($cache)) {
         $cache = array("urls" => array(), "content" => array(), "taxonomies" => array());
     }
     $last = File::get($time_file);
     // grab a list of all files
     $finder = new Finder();
     $files = $finder->files()->name("*." . Config::getContentType())->in(Config::getContentRoot());
     // grab a separate list of files that have changed since last check
     $updated_files = clone $files;
     $updated = array();
     if ($last) {
         $updated_files->date(">= " . Date::format("Y-m-d H:i:s", $last));
         foreach ($updated_files as $file) {
             // we don't want directories, they may show up as being modified
             // if a file inside them has changed or been renamed
             if (is_dir($file)) {
                 continue;
             }
             // this isn't a directory, add it to the list
             $updated[] = Path::trimFilesystem(Path::standardize($file->getRealPath()));
         }
     }
     // loop over current files
     $current_files = array();
     foreach ($files as $file) {
         $current_files[] = Path::trimFilesystem(Path::standardize($file->getRealPath()));
     }
     // get a diff of files we know about and files currently existing
     $new_files = array_diff($current_files, $cache['urls']);
     // create a master list of files that need updating
     $changed_files = array_unique(array_merge($new_files, $updated));
     // 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::trimFilesystem($file);
             $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));
             // parse data
             $data = YAML::parse($front_matter);
             // 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'] = false;
             $data['_is_draft'] = false;
             // folder
             $data['_folder'] = preg_replace(Pattern::ORDER_KEY, "", str_replace($full_content_root, "", $data['_file']));
             $slash = strrpos($data['_folder'], "/");
             $data['_folder'] = $slash === 0 ? "" : substr($data['_folder'], 1, $slash - 1);
             // fix hidden/draft files
             $slug = basename($file, "." . $content_type);
             if (substr($slug, 0, 2) === "__") {
                 $data['_is_hidden'] = true;
                 $data['slug'] = substr($slug, 2);
             } elseif (substr($slug, 0, 1) === "_") {
                 $data['_is_draft'] = true;
                 $data['slug'] = substr($slug, 1);
             } else {
                 $data['slug'] = $slug;
             }
             $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
             } elseif (preg_match(Pattern::DATE_OR_DATETIME, $data['_basename'], $matches)) {
                 $date = $matches[1] . '-' . $matches[2] . '-' . $matches[3];
                 $time = NULL;
                 if (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;
             } elseif (preg_match(Pattern::NUMERIC, $data['_basename'], $matches)) {
                 $data['_order_key'] = $matches[1];
                 $data['numeric'] = $data['_order_key'];
                 $data['slug'] = substr($data['slug'], strlen($matches[1]) + 1);
             } else {
                 $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']);
             // add to cache file
             $cache['content'][$local_path] = $data;
             $cache['urls'][$data['url']] = $local_path;
         }
     }
     // 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 $local_path => $data) {
         if (File::exists($full_content_root . $local_path)) {
             continue;
         }
         $files_changed = TRUE;
         // remove from content cache
         unset($cache['content'][$local_path]);
         // remove from url cache
         $url = array_search($local_path, $cache['urls']);
         if ($url !== FALSE) {
             unset($cache['urls'][$url]);
         }
     }
     // 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();
         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 $file => $data) {
                 // do not grab anything not public
                 if (array_get($data, '_is_hidden', FALSE) || array_get($data, '_is_draft', FALSE)) {
                     continue;
                 }
                 // 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']);
                         }
                     }
                 }
             }
         }
         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;
         }
     }
     File::put($time_file, $now);
     return true;
 }
Example #11
0
 /**
  * Execute the console command.
  *
  * @return mixed
  */
 public function fire()
 {
     // Start by warning the user about the command that will be run
     $this->comment('Warning! This Laravel command uses exec() to execute a mysql shell command to import a extracted dump.');
     $this->comment('Due to the way the command is constructed, should someone view the current running processes of your server, they will be able to see your SeAT database users password.');
     $this->line('');
     $this->line('Ensure that you understand this before continuing.');
     // Test that we have valid Database details. An exception
     // will be thrown if this fails.
     \DB::connection()->getDatabaseName();
     if ($this->option('confirm') || $this->confirm('Are you sure you want to update to the latest EVE SDE? [yes|no]', true)) {
         $this->info('Checking for SDE updates at https://raw.githubusercontent.com/eve-seat/seat/resources/sde_version.json ...');
         // Check the current SDE version from Github in the eve-seat/seat/resources
         // repository. First though, we setup some default headers and options
         // that will be used in Requests in this script
         $headers = array('Accept' => 'application/json');
         $options = array('timeout' => 120, 'useragent' => 'SeAT-Updater/' . \Config::get('seat.version'));
         // Now, attempt the actual request.
         $request = \Requests::get('https://raw.githubusercontent.com/eve-seat/seat/resources/sde_version.json', $headers, $options);
         // If the request failed, return as there is nothing we can do about that
         if (!$request->success) {
             $this->error('Warning: Failed to retreive the latest SDE information from the eve-seat/resources branch.');
             return;
         }
         // Read the response JSON
         $sde_data = json_decode($request->body);
         // If it was not possible to parse the JSON, return as there is nothing
         // we can go any further
         if (is_null($sde_data)) {
             $this->error('Warning: Failed to parse the response JSON.');
             return;
         }
         // Show some debugging information about what we got from the resources branch
         $this->line('The current SDE version is ' . $sde_data->version);
         $this->line(count($sde_data->tables) . ' dumps will be downloaded from ' . $sde_data->url . ' in ' . $sde_data->format . ' format and imported into mysql://' . \Config::get('database.connections.mysql.host') . '/' . \Config::get('database.connections.mysql.database'));
         // Prepare a final confirmation before work comences for the update
         if ($this->option('confirm') || $this->confirm('Does the above look OK? [yes|no]', true)) {
             // Prepare the filesystem for the dumps
             $storage = storage_path() . '/sde/' . $sde_data->version . '/';
             $this->line('Preparing ' . $storage . ' to store the dumps.');
             // Check that we are able to write to the storage_path()
             if (\File::isWritable(storage_path())) {
                 // Check that the path exists
                 if (!\File::exists($storage)) {
                     \File::makeDirectory($storage, 0755, true);
                 }
                 // With the disk storage ok, we continue to download the
                 // archives. To try and save some badnwidth, we will
                 // check that the archive does not already exist.
                 $this->line('Downloading SDE data...');
                 // Loop over the tables as defined in the JSON
                 foreach ($sde_data->tables as $table) {
                     $full_path = $storage . $table . $sde_data->format;
                     $full_url = $sde_data->url . $table . $sde_data->format;
                     // Check if we don't already have this file. If it does
                     // exist, check that it is larger than 0 bytes.
                     if (\File::exists($full_path)) {
                         if (\File::size($full_path) > 0) {
                             continue;
                         }
                     }
                     // Attempt to download the file
                     $file = \Requests::get($full_url, $options, $options);
                     if ($file->success) {
                         // Save the dump to disk
                         \File::put($full_path, $file->body);
                         $this->info('[OK] ' . $full_url);
                     } else {
                         $this->error('Failed to download ' . $full_url . '. The HTTP error was ' . $file->status_code);
                     }
                 }
                 // Next, we will read the bz2 archives, and attempt to run the
                 // SQL queries in them.
                 // Unfortunately, at least until I learn a better way, I seems like
                 // running the imports with a exec() is the only way I can get
                 // this to work, for now. =(
                 $this->line('Importing the SQL...');
                 foreach ($sde_data->tables as $table) {
                     // Prepare the full path and check its validity
                     $full_path = $storage . $table . $sde_data->format;
                     if (!\File::exists($full_path) || !\File::size($full_path) > 0) {
                         $this->error('Warning: ' . $full_path . ' does not appear to be valid. Maybe the download failed? Skipping import for this file.');
                         continue;
                     }
                     // Now that we know that the source .bz2 exists, check if
                     // the archive is possibly already extracted. Of so,
                     // skip the extraction process completely.
                     $full_extracted_path = $storage . $table . '.sql';
                     if (!\File::exists($full_extracted_path) || !\File::size($full_extracted_path) > 0) {
                         // Get 2 handles ready for both the in and out files
                         $input_file = bzopen($full_path, 'r');
                         $output_file = fopen($full_extracted_path, 'w');
                         // Write the $output_file in chunks
                         while ($chunk = bzread($input_file, 4096)) {
                             fwrite($output_file, $chunk, 4096);
                         }
                         // Close the input file
                         bzclose($input_file);
                         fclose($output_file);
                     }
                     // With the output file ready, prepare the scary exec() command
                     // that should be run. A sample $import_command is:
                     // mysql -u root -h 127.0.0.1 seat < /tmp/sample.sql
                     $import_command = 'mysql -u ' . \Config::get('database.connections.mysql.username') . (strlen(\Config::get('database.connections.mysql.password')) ? ' -p' : '') . \Config::get('database.connections.mysql.password') . ' -h ' . \Config::get('database.connections.mysql.host') . ' ' . \Config::get('database.connections.mysql.database') . ' < ' . $full_extracted_path;
                     // Run the command... (*scared_face*)
                     exec($import_command, $output, $exit_code);
                     // If the command failed, we should have a $exit_code of
                     // not 0. If thats the case, read $output and print
                     // that as debugging information
                     if ($exit_code !== 0) {
                         $this->error('Warning: Import failed with exit code ' . $exit_code . ' and command outut: ' . implode('\\n', $output));
                     }
                     // Write the OK
                     $this->info('[OK] ' . $table . $sde_data->format . ' [' . number_format(\File::size($full_path) / 1048576, 2) . 'MB]');
                 }
                 $this->line('SDE update to ' . $sde_data->version . ' completed successfully.');
             } else {
                 $this->error('Warning: Unable to write to the storage path ' . storage_path() . ' to store the dumps.');
             }
         } else {
             $this->comment('Warning: SDE Update aborted');
         }
     }
 }
Example #12
0
 /**
  * Execute the console command.
  *
  * @return mixed
  */
 public function fire()
 {
     // Prepare some welcome information
     $this->info('[+] Welcome to the SeAT v' . \Config::get('seat.version') . ' updater!');
     $this->line('');
     $this->comment('[+] Warning: Please ensure that you read any potential upgrade specifics prior to upgrading your installation.');
     $this->comment('[+] Upgrade guides can be found here: https://github.com/eve-seat/seat/tree/master/docs/upgrade_specifics');
     $this->line('');
     $this->comment('[+] Note: Any local changes to SeAT will be overriden with this upgrade. Ensure you have the required backups first.');
     $this->line('');
     $this->line('[+] Checking if we have everything required to attempt an update...');
     // Lets check if we have everything that we need to update,
     //and that we are infact out of date
     // Start with the git command
     $git_command = exec('which git');
     if (strlen($git_command) <= 0 || !\File::exists($git_command)) {
         $this->error('[!] Error: `git` command not found. Please ensure that you have Git installed and in your $PATH');
         return;
     }
     $this->info('[+] `git` found at `' . $git_command . '`');
     // Move on to composer. If we did not get the path
     // to where composer is installed, attempt to
     // find it. We will first check $PATH, then
     // the ./ and finally error out
     $composer_command = $this->option('composer');
     if (!is_null($composer_command)) {
         if (!\File::exists($composer_command)) {
             $this->error('[!] Error: Composer was not found at `' . $composer_command . '`. Please specify a valid path to `composer.phar`.');
             return;
         }
     } else {
         // So we did not get the path to composer.phar, lets
         // try and find it.
         $composer_command = exec('which composer');
         if (strlen($composer_command) <= 0 || !\File::exists($composer_command)) {
             $composer_command = exec('which composer.phar');
             if (strlen($composer_command) <= 0 || !\File::exists($composer_command)) {
                 $composer_command = base_path() . '/composer';
                 if (strlen($composer_command) <= 0 || !\File::exists($composer_command)) {
                     $composer_command = base_path() . '/composer.phar';
                     if (strlen($composer_command) <= 0 || !\File::exists($composer_command)) {
                         $this->error('[!] Error: Unable to find `composer.phar`. Please specify a valid path to `composer.phar` as a command argument.');
                         return;
                     }
                 }
             }
         }
     }
     $this->info('[+] `composer.phar` found at `' . $composer_command . '`');
     // Next ensure that the path where SeAT is installed is
     // writable to us now
     if (!\File::isWritable(base_path())) {
         $this->error('[!] Error: ' . base_path() . ' is not writable to the current user that is running this command.');
         return;
     }
     $this->info('[+] ' . base_path() . ' is writable for the upgrade.');
     $this->line('[+] Checking version information against the Github repository...');
     // Check the version information
     $version_information = $this->checkVersion();
     // If we didnt get the flag to skip the version
     // check, check it.
     if (!$this->option('no-version')) {
         // If we are on the latest, simply exit
         if ($version_information['versions_behind'] == 0) {
             $this->comment('[+] You are running SeAT v' . \Config::get('seat.version') . ' which is the latest.');
             return;
         }
     }
     // Ask the user if they want to upgrade.
     if (!$this->confirm('[?] Are you sure you want to upgrade from SeAT v' . \Config::get('seat.version') . ' to ' . $version_information['release_data'][0]->tag_name . '?', true)) {
         $this->error('[+] Error: User cancelled upgrade.');
         return;
     }
     // We have everything we need to continue the
     // upgrade. Bring the application down so
     // that we can continue.
     $this->call('down');
     if (!\App::isDownForMaintenance()) {
         $this->error('[!] Error: Dropping SeAT into maintenance mode failed.');
         return;
     }
     // Move on to calling git to pull down the latest
     // code from eve-seat/seat.
     // git fetch -f
     $this->line('[+] Running: `' . $git_command . ' fetch -f`');
     system($git_command . ' fetch -f', $exit_code);
     // If the command failed, we should have a $exit_code of
     // not 0. If thats the case, read $output and print
     // that as debugging information
     if ($exit_code !== 0) {
         $this->error('[!] Error: git fetch failed with exit code ' . $exit_code);
         return;
     }
     // git pull -f
     $this->line('[+] Running: `' . $git_command . ' pull -f`');
     system($git_command . ' pull -f', $exit_code);
     // If the command failed, we should have a $exit_code of
     // not 0. If thats the case, read $output and print
     // that as debugging information
     if ($exit_code !== 0) {
         $this->error('[!] Error: git pull failed with exit code ' . $exit_code);
         return;
     }
     // git checkout -f master if we did not get the --dev option
     if (!$this->option('dev')) {
         $this->line('[+] Running: `' . $git_command . ' checkout -f master`');
         system($git_command . ' checkout -f master', $exit_code);
         // If the command failed, we should have a $exit_code of
         // not 0. If thats the case, read $output and print
         // that as debugging information
         if ($exit_code !== 0) {
             $this->error('[!] Error: git checkout -f master failed with exit code ' . $exit_code);
             return;
         }
     }
     // composer self-update
     $this->line('[+] Running: `' . $composer_command . ' self-update`');
     system($composer_command . ' self-update', $exit_code);
     // If the command failed, we should have a $exit_code of
     // not 0. If thats the case, read $output and print
     // that as debugging information
     if ($exit_code !== 0) {
         $this->error('[!] Error: composer self-update failed with exit code ' . $exit_code);
         return;
     }
     // composer update
     $this->line('[+] Running: `' . $composer_command . ' update`');
     system($composer_command . ' update', $exit_code);
     // If the command failed, we should have a $exit_code of
     // not 0. If thats the case, read $output and print
     // that as debugging information
     if ($exit_code !== 0) {
         $this->error('[!] Error: composer update failed with exit code ' . $exit_code);
         return;
     }
     // composer dump-autoload
     $this->line('[+] Running: `' . $composer_command . ' dump-autoload`');
     system($composer_command . ' dump-autoload', $exit_code);
     // If the command failed, we should have a $exit_code of
     // not 0. If thats the case, read $output and print
     // that as debugging information
     if ($exit_code !== 0) {
         $this->error('[!] Error: composer dump-autoload failed with exit code ' . $exit_code);
         return;
     }
     // Database migrations
     $this->line('[+] Running database migrations.');
     $this->call('migrate');
     // SDE Updates
     if (!$this->option('no-sde')) {
         $this->line('[+] Running EVE SDE Updates');
         $this->call('seat:update-sde', array('--confirm' => null));
     }
     // With everything done, bring the application back up
     $this->call('up');
     $this->line('');
     $this->info('[+] Upgrade done! It is recommended that you have a look at the `laravel.log` file for any potential errors.');
     $this->info('[+] You can view the log file with `php artisan tail`, or responding with [y] to the next question.');
     // Ask the user if they want to upgrade.
     if ($this->confirm('[?] Do you want to tail the log file now?', true)) {
         $this->call('tail');
     }
 }
 /**
  * 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;
 }
     $call = '?a=get_language_file&v=' . $version . '&l=' . $languageToLoad;
     $call .= '&f=' . $file;
     $fileData = getFileDataFromURL($updateUrl . $call);
     if (false === $fileData || $fileData == '') {
         $msg = 'Fatal error: error downloading file \'' . $file . '\'!';
         $msg .= ' Please try again.';
         $message[] = Html::getErrorMsg($msg);
     } else {
         // save file to disk
         $file = $path . '/' . $file . '.php';
         $fp = @fopen($file, 'wb');
         if ($fp) {
             fwrite($fp, $fileData);
             fclose($fp);
             if (!File::isWritable($file, 0644)) {
                 File::isWritable($file, 0777);
             }
             $msg = ' File \'' . $file . '\' saved succesfully.';
             $message[] = Html::getOkMsg($msg);
             // remove file from todo list
             $fileIndex = count($_SESSION['get_language']) - 1;
             unset($_SESSION['get_language'][$fileIndex]);
         } else {
             $msg = 'Fatal error: couldn\'t write file \'' . $file;
             $msg .= '\' to \'' . $path . '\'';
             $message[] = Html::getErrorMsg($msg);
         }
     }
 } else {
     $inProgress = 0;
     $msg = 'Finished installing language \'' . $languageToLoad;