io.inc in Patterns 7.2
Same filename and directory in other branches
Functions related to input/output operations.
File
includes/io/io.incView source
<?php
/**
* @file
* Functions related to input/output operations.
*/
/**
* Menu callback, returns source code of the requested pattern
* if the pattern is public.
*
* @param integer $pid
* The ID of the Pattern to be displayed.
*/
function patterns_io_get_pattern_service($pattern) {
// TODO: handling correctly different formats
$content_type = 'text/plain';
drupal_add_http_header('Content-Type', $content_type . '; charset=utf-8');
// GET service is available only if settings allows it
if (!variable_get('patterns_allow_publish', PATTERNS_ALLOW_PUBLISH_DEFAULT)) {
print 'Patterns GET service not available. Please try later.';
exit;
}
$pattern = patterns_get_pattern($pattern);
if (!$pattern) {
print 'Invalid pattern identifier.';
exit;
}
// Make sure pattern is public (published).
if (!$pattern->public) {
print 'Requested pattern is not available through GET service';
exit;
}
// Finally
print file_get_contents($pattern->file);
exit;
}
/**
* Loads the Patterns handlers (component) from the filesystem,
* if they are not already loaded.
*
* @param bool $reset (optional) If TRUE, always forces reloading
* the components from the file system. Defaults FALSE
* @param bool $dryrun (optional) If TRUE, it does not actually load
* the components, but just returns the paths to each of them.
* Defaults FALSE
*
* @return Array $components Array containing the paths of the
* components files.
*/
function patterns_io_load_components($reset = FALSE, $dryrun = FALSE) {
$components =& drupal_static(__FUNCTION__);
if (isset($paths) && !$reset) {
return $components;
}
$components = array();
// Get list of directories to scan for components.
$paths = module_invoke_all('patterns_components');
foreach ($paths as $path) {
foreach (file_scan_directory($path, '/\\.inc$/') as $file) {
$components[] = $file->uri;
if ($dryrun) {
continue;
}
require_once $file->uri;
}
}
return $components;
}
/**
* Scan directories looking for patterns files.
*
* Checks inside the directories defined by patterns_config_get_paths().
*
* @param boolean $verbose if TRUE, displays on the screen information
* about the scan.
*
* @see patterns_config_get_paths()
* @see patterns_io_analyze_scandir_messages()
*
* @return array An associative array of informative messages
*/
function patterns_io_scan_directories($verbose = FALSE) {
$existing = patterns_db_get_patterns_array('name', TRUE);
// Patterns already enabled won't be updated in the database
$enabled = patterns_db_get_enabled_patterns_array('name', TRUE);
$messages = array();
$messages['errors'] = array();
$messages['skipped'] = array();
$messages['permission'] = array();
$messages['found'] = array();
$messages['updated'] = array();
$messages['error_found'] = array();
$messages['error_updated'] = array();
// Get list of directories to scan for patterns.
$patterns_paths = patterns_path_get_patterns_dirs();
// Get valid file extensions.
$mask = '/.\\.(' . implode('|', patterns_parser_get_formats()) . ')$/';
// Prepare list of files/folders to be excluded.
// 'enabled' - Don't save enabled pattern backups.
$no_mask = array(
'.',
'..',
'CVS',
'.svn',
'.git',
'.bzr',
'enabled',
);
foreach ($patterns_paths as $path) {
foreach (file_scan_directory($path, $mask, $no_mask) as $file) {
$format = substr($file->filename, strlen($file->name) + 1);
if (in_array($file->filename, $existing)) {
if (!variable_get('patterns_update_db_from_fs', TRUE)) {
continue;
}
if (!patterns_db_is_pattern_updated($file->filename)) {
// the database is already updated to the most recent version
continue;
}
if (!empty($enabled)) {
if (in_array($file->filename, $enabled)) {
$messages['skipped'][] = $file->filename;
continue;
// Skip updating enabled patterns.
}
}
}
if (!is_readable($file->uri)) {
$messages['permission'][] = $file->filename;
continue;
}
// Choose appropriate function based on the file extension.
// Can be FALSE, if no parser is found.
$load_function = patterns_parser_get_parser_function($format, PATTERNS_PARSER_LOAD);
// Load and save pattern.
if (!$load_function || !($pattern = $load_function($file->uri))) {
$messages['errors'][] = $file->filename;
continue;
}
if (!patterns_validate_pattern($pattern, $format, PATTERNS_VALIDATE_SYNTAX)) {
$messages['invalid'][] = $file->filename;
continue;
}
// If everything was fine save it to the database
$result = patterns_db_save_pattern($pattern, $file->uri, $file->filename, $format);
if ($result && !in_array($file->filename, $existing)) {
$messages['found'][] = $file->filename;
continue;
}
if (!$result && !in_array($file->filename, $existing)) {
$messages['error_found'][] = $file->filename;
continue;
}
if ($result && in_array($file->filename, $existing)) {
$messages['updated'][] = $file->filename;
continue;
}
if (!$result && in_array($file->filename, $existing)) {
$messages['error_updated'][] = $file->filename;
continue;
}
}
}
variable_set('patterns_loaded', time());
if ($verbose) {
_patterns_io_analyze_scandir_messages($messages);
}
return $messages;
}
/**
* Analyzes the result of the patterns directories scan and
* displays relevant Drupal messages to the user.
*
* @see patterns_io_scan_directories()
*
*/
function _patterns_io_analyze_scandir_messages($messages = NULL) {
if (is_null($messages)) {
return;
}
// TODO: fix these to be proper t().
if (!empty($messages['found'])) {
drupal_set_message(t('New patterns were found and added to the database:') . ' <br/>' . implode('<br/>', $messages['found']) . '<br/><br/>');
}
if (!empty($messages['updated'])) {
drupal_set_message(t('The following patterns were updated to the newest version found in the file system:') . ' <br/>' . implode('<br/>', $messages['updated']) . '<br/><br/>');
}
if (!empty($messages['errors'])) {
drupal_set_message(t('A generic error occurred while trying to load the following files:') . ' <br/>' . implode('<br/>', $messages['errors']) . '<br/><br/>', 'warning');
}
if (!empty($messages['invalid'])) {
drupal_set_message(t('Some invalid patterns were found:') . ' <br/>' . implode('<br/>', $messages['invalid']) . '<br/><br/>', 'warning');
}
if (!empty($messages['skipped'])) {
drupal_set_message(t('Patterns that are already enabled are not updated against changes in the file system. Please verify the following ones:') . ' <br/>' . implode('<br/>', $messages['skipped']) . '<br/><br/>', 'warning');
}
if (!empty($messages['permission'])) {
drupal_set_message(t('Some pattern files could not be open for reading. Please verify the permission of:') . ' <br/>' . implode('<br/>', $messages['permission']) . '<br/><br/>', 'warning');
}
if (!empty($messages['error_found'])) {
drupal_set_message(t('New patterns were found, but could not be saved in the database:') . ' <br/>' . implode('<br/>', $messages['error_found']) . '<br/><br/>', 'warning');
}
if (!empty($messages['error_updated'])) {
drupal_set_message(t('A newer version of the following pattern was found, but could not be saved in the database:') . ' <br/>' . implode('<br/>', $messages['error_found']) . '<br/><br/>', 'warning');
}
}
/**
* Loads all the available patterns from the database.
* It also checks against the file system, and, if the
* patterns global configuration options allow it,
* updates the database with the latest modifications.
* Notice: enabled patterns will not be updated anyway.
*
* @param $reset if TRUE, it always updates the patterns in
* the database with the file system.
* @param bool $verbose If TRUE, only public patterns
* are returned
* @param bool $public If TRUE, verbose output is
* printed to screen
*
* @return array The array of available patterns.
*/
function _patterns_io_get_patterns($reset = TRUE, $verbose = TRUE, $public = FALSE) {
if ($reset || !variable_get('patterns_loaded', FALSE)) {
// Updates the patterns in the database
patterns_io_scan_directories($verbose);
}
// Get all the patterns from the database
$result = patterns_db_get_patterns();
$messages = array();
$patterns = array();
$patterns[PATTERNS_STATUS_OK] = array();
$patterns[PATTERNS_STATUS_TRASHED] = array();
foreach ($result as $pattern) {
// Skip pattern if its name is missing.
if (!isset($pattern->name)) {
continue;
}
if ($public && !$pattern->public) {
continue;
}
// Skip pattern if its file is missing.
if (!is_file($pattern->file)) {
$messages[] = patterns_utils_toString($pattern);
}
$status = $pattern->status;
if (in_array($status, array(
PATTERNS_STATUS_OK,
PATTERNS_STATUS_ENABLED,
PATTERNS_STATUS_INVALID,
))) {
$status = PATTERNS_STATUS_OK;
}
else {
$status = PATTERNS_STATUS_TRASHED;
}
$patterns[$status][$pattern->pid] = $pattern;
$data = unserialize($pattern->pattern);
$patterns[$status][$pattern->pid]->pattern = $data;
$patterns[$status][$pattern->pid]->info = $data['info'];
}
if (!empty($messages)) {
drupal_set_message(t("The following patterns were found in the database, but not in the file system:") . '<br/>' . implode('<br/>', $messages) . '<br/>', 'warning');
}
return $patterns;
}
/**
* Wrapper function for _patterns_io_get_patterns.
* @see _patterns_io_get_patterns()
*
* @return Array Associative array of public patterns.
*/
function patterns_io_get_public_patterns($reset = TRUE, $verbose = TRUE) {
return _patterns_io_get_patterns($reset, $verbose, TRUE);
}
/**
* Wrapper function for _patterns_io_get_patterns.
* @see _patterns_io_get_patterns()
*
* @return array The array of available patterns.
*/
function patterns_io_get_patterns($reset = TRUE, $verbose = TRUE) {
return _patterns_io_get_patterns($reset, $verbose, FALSE);
}
/**
* Prints an HTML formatted list of patterns.
*/
function patterns_io_get_patterns_service($type = 'ajax') {
if ($type == 'ajax') {
$patterns = patterns_io_get_patterns();
$output = '<div id="all_patterns_div"><strong>' . t('last update:') . '</strong>' . date(DATE_RFC822);
$output .= theme('patterns_list', $patterns);
$output .= '</div>';
print $output;
}
}
/**
* Checks if a .htaccess file exists to prevent downloads of pattern files.
*/
function _patterns_io_check_htaccess() {
$path = patterns_path_get_files_dir();
if (!is_file($path . '/.htaccess')) {
$content = '# Prevent downloading site patterns
<FilesMatch "\\.xml$">
Order allow,deny
</FilesMatch>
';
file_save_data($content, $path . '/.htaccess');
}
}
/**
* Checks if the patterns directory exists and is writable.
*/
function _patterns_io_is_patterns_dir_ready($patterns_files_dir = NULL, $flag = FILE_MODIFY_PERMISSIONS) {
// TODO: keep pattern_files_dir in a separate location. Warning: defining a constant creates an error.
if (empty($patterns_files_dir)) {
$patterns_files_dir = patterns_path_get_files_dir();
// TODO: move this out for performance
}
return file_prepare_directory($patterns_files_dir, $flag) ? TRUE : FALSE;
}
/**
* Lower level primitive for patterns_io_save_pattern.
* Includes an optional argument to force the UUUID
*
* Returns a patterns_results object with a message, instead of a boolean
*/
function _patterns_io_save_pattern($content = NULL, $name = NULL, $format = PATTERNS_FORMAT_YAML, $dir = NULL, $original = NULL, $username = NULL, $uuuid = NULL) {
$status = PATTERNS_SUCCESS;
$msg = NULL;
if (is_null($name)) {
$msg = t('Cannot save pattern with \'NULL\' identifier.');
return patterns_results(PATTERNS_ERR, $msg);
}
if (!patterns_validate_pattern($content, $format, PATTERNS_VALIDATE_SYNTAX)) {
$msg = t("Pattern '%name' could not be saved. Make sure edited code is well-formed.", array(
'%name' => $name,
));
return patterns_results(PATTERNS_ERR, $msg);
}
if (is_null($dir)) {
$dir = patterns_path_get_files_dir();
}
if (!file_prepare_directory($dir, FILE_CREATE_DIRECTORY)) {
$msg = t('Error: the pattern is not writable. Please check the file system permissions.');
return patterns_results(PATTERNS_ERR, $msg);
}
// Check if the file has a valid extension
// and in case add the format at the end
if (!_patterns_io_file_has_valid_extension($name)) {
$name = $name . '.' . $format;
}
$path_original = $dir . '/' . $name;
if (is_null($original)) {
if (is_array($content)) {
$original = patterns_parser_dump($content, $format);
}
else {
$original = $content;
}
}
$path = file_unmanaged_save_data($original, $path_original, FILE_EXISTS_REPLACE);
if (!$path) {
$msg = t('An error occurred while saving the file to %path. A file with the same name exists.', array(
'%path' => $path_original,
));
return patterns_results(PATTERNS_ERR, $msg);
}
// Load and save pattern.
$load_function = patterns_parser_get_parser_function($format, PATTERNS_PARSER_LOAD);
if (!$load_function) {
$msg = t('Could not find a parser for ', array(
'%name' => $name,
));
return patterns_results(PATTERNS_ERR, $msg);
}
$pattern = $load_function($path);
if (!$pattern) {
$msg = t("Pattern '%name' could not be saved into the database. Make sure edited code is well-formed.", array(
'%name' => $name,
));
patterns_io_remove_pattern_from_fs($path);
return patterns_results(PATTERNS_ERR, $msg);
}
patterns_db_save_pattern($pattern, $path, $name, $format, $username, $uuuid);
$link = l($name, 'admin/patterns/edit/' . $name);
$msg = t('Pattern !name was saved in %path.', array(
'!name' => $link,
'%path' => $path,
));
return patterns_results(PATTERNS_SUCCESS, $msg);
}
/**
* Saves a pattern string or array into the database AND in the file system.
*
* Updates the same file to the newer version, but does not replace an existing
* file (e.g. if we are moving a file to a new location).
*
* @param mixed $content The content of the pattern to be saved. Can be a string the
* or an array represeting the pattern. In the latter case @param $original
* can be contain the string. If @param $original is missing the pattern is
* saved as a PHP array.
* @param mixed $name The name of the pattern file.
* @param mixed $format file format (notice: it is not the extension!)
* (optional) The format of the pattern. Defaults to PATTERNS_FORMAT_YAML.
* @param mixed $dir the destination directory
* @param mixed $original the string representation of the pattern. Optional.
* @param mixed $username (Optional)
* A name for the author/uploader of the pattern. Defaults, logged user
* @param bool $verbose Optional. If TRUE, a message is displayed with drupal_set_message.
* Defaults, TRUE
*
* @return bool
* TRUE on success, FALSE otherwise.
*
* @see _patterns_io_save_pattern
*
* TODO: if content is array and original is null, dump the array into the
* correct original file
*
*/
function patterns_io_save_pattern($content = NULL, $name = NULL, $format = PATTERNS_FORMAT_YAML, $dir = NULL, $original = NULL, $username = NULL, $verbose = TRUE) {
if (is_null($username)) {
global $user;
$username = $user->name;
}
$result = _patterns_io_save_pattern($content, $name, $format, $dir, $original, $username);
if ($verbose && !empty($result['msg'])) {
$status = $result['status'] == PATTERNS_SUCCESS ? 'status' : 'error';
drupal_set_message($result['msg'], $status);
}
return $result['status'] == PATTERNS_SUCCESS ? TRUE : FALSE;
}
/**
* Removes a pattern both from the file system and the database.
*
* @param mixed $pattern_orig A pattern object, an array representing
* the pattern object, a numeric id or alphanumeric name of
* the pattern as it is in the database
* @param bool $verbose (optional) If TRUE notify the users a failure.
* Default FALSE.
*
* @return Boolean TRUE, if removal from the file system and the
* database is successful
*/
function patterns_io_remove_pattern($pattern_orig, $verbose = TRUE) {
// Retrieve
$pattern = _patterns_db_get_pattern($pattern_orig);
if (!$pattern) {
if ($verbose) {
drupal_set_message(t('Impossible to retrieve specified pattern: %pattern.', array(
'%pattern' => $pattern_orig,
)), 'error');
}
return FALSE;
}
if (!patterns_io_remove_pattern_from_fs($pattern->file, $verbose)) {
return FALSE;
}
$result = patterns_db_remove_pattern($pattern->pid);
if ($result) {
if ($verbose) {
drupal_set_message(t('Pattern %pattern was removed succesfully.', array(
'%pattern' => $pattern_orig,
)));
}
}
return $result;
}
function patterns_io_remove_pattern_from_fs($path, $verbose = TRUE) {
// Check permissions
if (!is_writable($path)) {
if ($verbose) {
drupal_set_message(t('Do not have permission to delete file %pattern. Aborting.', array(
'%pattern' => $path,
)), 'error');
}
return FALSE;
}
// Delete from FS
$result = unlink($path);
if (!$result) {
if ($verbose) {
drupal_set_message(t('An error occurred while deleting %pattern.', array(
'%pattern' => $path,
)), 'error');
}
return FALSE;
}
return TRUE;
}
/**
* Checks whether the extension of the specified file name
* matches one of the currently available parser formats.
*
*
* @param mixed $file String representing a file name.
* @param array $formats (optional) The array of valid formats,
* against which checking the file extension. If NULL, rebuilds
* the index of currently available formats.
*
* @return bool Boolean. TRUE, if the extension matches an available
* parser.
*/
function _patterns_io_file_has_valid_extension($file = NULL, $formats = NULL) {
if (is_null($file)) {
return FALSE;
}
if (is_null($formats)) {
$formats = patterns_parser_get_formats();
}
$ext = pathinfo($file, PATHINFO_EXTENSION);
return in_array($ext, $formats) ? TRUE : FALSE;
}
/**
* Returns a list with the names of the components.
*
* @param bool $reset (optional) If TRUE, always forces reloading
* the components from the file system. Defaults FALSE
*
* @return array Array containing the names of the patterns components
*
* @see patterns_io_load_components()
*/
function patterns_io_list_components_names($reset = FALSE) {
$components = patterns_io_load_components($reset, TRUE);
$func = create_function('$c', 'return pathinfo($c, PATHINFO_FILENAME);');
return array_map($func, $components);
}
/**
* Returns a list of uris to components files.
*
* Wrapper method for patterns_io_load_components($reset, TRUE);
*
* @param bool $reset (optional) If TRUE, always forces reloading
* the components from the file system. Defaults FALSE
*
* @see patterns_io_load_components()
*/
function patterns_io_list_components($reset = FALSE) {
return patterns_io_load_components($reset, TRUE);
}
/**
* Returns a list of all the directories containing
* Patterns components.
*
* @param bool $reset (optional) If TRUE, forces to
* reload the directory index.
*/
function patterns_io_list_components_dirs($reset = FALSE) {
$components_dirs =& drupal_static(__FUNCTION__);
if (isset($components_dirs) && !$reset) {
return $components_dirs;
}
// Get list of directories to scan for components.
return module_invoke_all('patterns_components');
}
/**
* Creates a pattern object from a file
*
* The returned object is as if the pattern would be loaded
* from the database with patterns_get_pattern()
*
* @param string $file
* The path to the file containing the pattern
* @param string $format Optional.
* A valid pattern format
* @return stdClass $pattern The pattern object.
*
*/
function patterns_io_load_pattern_from_file($file, $format = PATTERNS_FORMAT_UNKNOWN) {
$pattern = array();
$content = patterns_parser_load($file, $format);
if (!$content) {
return FALSE;
}
$pattern['pattern'] = $content;
$pattern = patterns_get_pattern_obj($pattern);
return $pattern;
}
Functions
Name | Description |
---|---|
patterns_io_get_patterns | Wrapper function for _patterns_io_get_patterns. |
patterns_io_get_patterns_service | Prints an HTML formatted list of patterns. |
patterns_io_get_pattern_service | Menu callback, returns source code of the requested pattern if the pattern is public. |
patterns_io_get_public_patterns | Wrapper function for _patterns_io_get_patterns. |
patterns_io_list_components | Returns a list of uris to components files. |
patterns_io_list_components_dirs | Returns a list of all the directories containing Patterns components. |
patterns_io_list_components_names | Returns a list with the names of the components. |
patterns_io_load_components | Loads the Patterns handlers (component) from the filesystem, if they are not already loaded. |
patterns_io_load_pattern_from_file | Creates a pattern object from a file |
patterns_io_remove_pattern | Removes a pattern both from the file system and the database. |
patterns_io_remove_pattern_from_fs | |
patterns_io_save_pattern | Saves a pattern string or array into the database AND in the file system. |
patterns_io_scan_directories | Scan directories looking for patterns files. |
_patterns_io_analyze_scandir_messages | Analyzes the result of the patterns directories scan and displays relevant Drupal messages to the user. |
_patterns_io_check_htaccess | Checks if a .htaccess file exists to prevent downloads of pattern files. |
_patterns_io_file_has_valid_extension | Checks whether the extension of the specified file name matches one of the currently available parser formats. |
_patterns_io_get_patterns | Loads all the available patterns from the database. It also checks against the file system, and, if the patterns global configuration options allow it, updates the database with the latest modifications. Notice: enabled patterns will not be updated… |
_patterns_io_is_patterns_dir_ready | Checks if the patterns directory exists and is writable. |
_patterns_io_save_pattern | Lower level primitive for patterns_io_save_pattern. Includes an optional argument to force the UUUID |