$option_value) { if (in_array($option_name, array('h', 'help'))) { echo "To clear the lock after a failed run, "; echo "pass in '--clearlock'" . PHP_EOL; echo 'If you have the configs [$file_checksums=true; $file_upload_block_duplicates=true;] set and would like to have duplicate resource information sent as a notification please run php staticsync.php --send-notifications' . PHP_EOL; exit(1); } if ( in_array($option_name, array('clearlock', 'c')) && is_process_lock("staticsync") ) { clear_process_lock("staticsync"); } if ('send-notifications' == $option_name) { $send_notification = true; } if ('suppress-output' == $option_name) { $suppress_output = true; } } if (isset($staticsync_userref)) { # If a user is specified, log them in. $userref = $staticsync_userref; $userdata = get_user($userref); if ($userdata === false) { echo 'Unable to get user.' . PHP_EOL; exit(1); } $userdata = array($userdata); setup_user($userdata[0]); } ob_end_clean(); if ($suppress_output) { ob_start(); } set_time_limit(60 * 60 * 40); echo "StaticSync started at " . date('Y-m-d H:i:s', time()) . PHP_EOL; # Check for a process lock if (is_process_lock("staticsync")) { echo 'Process lock is in place. Deferring.' . PHP_EOL; echo 'To clear the lock after a failed run use --clearlock flag.' . PHP_EOL; exit(); } set_process_lock("staticsync"); // Strip trailing slash if it has been left in $syncdir = rtrim($syncdir, "/"); echo "Preloading data... "; // Set options that don't make sense here $merge_filename_with_title = false; $count = 0; $done = $fcs_to_reorder = []; $errors = array(); $syncedresources = ps_query("SELECT ref, file_path, file_modified, archive FROM resource WHERE LENGTH(file_path)>0"); foreach ($syncedresources as $syncedresource) { $done[$syncedresource["file_path"]]["ref"] = $syncedresource["ref"]; $done[$syncedresource["file_path"]]["modified"] = $syncedresource["file_modified"]; $done[$syncedresource["file_path"]]["archive"] = $syncedresource["archive"]; } // Set up an array to monitor processing of new alternative files $alternativefiles = array(); $restypes = get_resource_types(); if (isset($numeric_alt_suffixes) && $numeric_alt_suffixes > 0) { // Add numeric suffixes to $staticsync_alt_suffix_array to support additional suffixes $newsuffixarray = array(); foreach ($staticsync_alt_suffix_array as $suffix => $description) { $newsuffixarray[$suffix] = $description; for ($i = 1; $i < $numeric_alt_suffixes; $i++) { $newsuffixarray[$suffix . $i] = $description . " (" . $i . ")"; } } $staticsync_alt_suffix_array = $newsuffixarray; } // Add all the synced alternative files to the list of completed if (isset($staticsync_alternative_file_text) && (!$staticsync_ingest || $staticsync_ingest_force)) { // Add any staticsynced alternative files to the array so we don't process them unnecessarily $syncedalternatives = ps_query("SELECT ref, file_name, resource, creation_date FROM resource_alt_files WHERE file_name like concat('%',?,'%')", ['s', $syncdir]); foreach ($syncedalternatives as $syncedalternative) { $shortpath = str_replace($syncdir . '/', '', $syncedalternative["file_name"]); $done[$shortpath]["ref"] = $syncedalternative["resource"]; $done[$shortpath]["modified"] = $syncedalternative["creation_date"]; $done[$shortpath]["alternative"] = $syncedalternative["ref"]; } } $lastsync = ps_value("SELECT value FROM sysvars WHERE name='lastsync'", array(), ""); $lastsync = (strlen($lastsync) > 0) ? strtotime($lastsync) : ''; echo "done." . PHP_EOL; echo "Looking for changes..." . PHP_EOL; # Pre-load the category tree, if configured. if (isset($staticsync_mapped_category_tree)) { $treefield = get_resource_type_field($staticsync_mapped_category_tree); migrate_resource_type_field_check($treefield); $tree = get_nodes($staticsync_mapped_category_tree, '', true); } function touch_category_tree_level($path_parts) { # For each level of the mapped category tree field, ensure that the matching path_parts path exists global $staticsync_mapped_category_tree, $tree; $parent_search = ''; $nodename = ''; $order_by = 10; $treenodes = array(); for ($n = 0; $n < count($path_parts); $n++) { $nodename = $path_parts[$n]; echo " - Looking for folder '" . $nodename . "' @ level " . $n . " in linked metadata field... "; # Look for this node in the tree. $found = false; foreach ($tree as $treenode) { if ($treenode["parent"] == $parent_search) { if ($treenode["name"] == $nodename) { # A match! echo " - FOUND" . PHP_EOL; $found = true; $treenodes[] = $treenode["ref"]; $parent_search = $treenode["ref"]; # Search for this as the parent node on the pass for the next level. } else { if ($order_by <= $treenode["order_by"]) { $order_by = $order_by + 10; } } } } if (!$found) { echo " - NOT FOUND. Updating tree field" . PHP_EOL; # Add this node $newnode = set_node(null, $staticsync_mapped_category_tree, $nodename, $parent_search, $order_by); $tree[] = array("ref" => $newnode,"parent" => $parent_search,"name" => $nodename,"order_by" => $order_by); $parent_search = $newnode; # Search for this as the parent node on the pass for the next level. $treenodes[] = $newnode; clear_query_cache("schema"); } } // Return the matching path nodes return $treenodes; } function ProcessFolder($folder) { global $lang, $syncdir, $nogo, $staticsync_max_files, $count, $done, $lastsync, $unoconv_extensions, $staticsync_autotheme, $FEATURED_COLLECTION_BG_IMG_SELECTION_OPTIONS, $staticsync_mapped_category_tree, $staticsync_title_includes_path, $staticsync_ingest, $staticsync_mapfolders, $staticsync_alternatives_suffix, $staticsync_defaultstate, $additional_archive_states, $staticsync_extension_mapping_append_values, $staticsync_deleted_state, $staticsync_alternative_file_text, $staticsync_filepath_to_field, $resource_deletion_state, $alternativefiles, $staticsync_revive_state, $enable_thumbnail_creation_on_upload, $staticsync_extension_mapping_append_values_fields, $staticsync_extension_mapping_append_separator, $view_title_field, $filename_field, $FIXED_LIST_FIELD_TYPES, $staticsync_whitelist_folders,$staticsync_ingest_force,$errors, $category_tree_add_parents, $staticsync_alt_suffixes, $staticsync_alt_suffix_array, $staticsync_file_minimum_age, $userref, $resource_type_extension_mapping_default, $resource_type_extension_mapping, $restypes; $collection = 0; $treeprocessed = false; if (!file_exists($folder)) { echo "Sync folder does not exist: " . $folder . PHP_EOL; return false; } echo "Processing Folder: " . $folder . PHP_EOL; # List all files in this folder. $directories_arr = array(); $files_arr = array(); $import_paths = array(); $dh = opendir($folder); while (($file = readdir($dh)) !== false) { if ($file == '.' || $file == '..') { continue; } $fullpath = "{$folder}/{$file}"; $filetype = filetype($fullpath); # Sort directory content so files are processed first. if ($filetype == 'dir' || $filetype == 'link') { $directories_arr[] = $fullpath; } if ($filetype == 'file') { $files_arr[] = $fullpath; } } $import_paths = array_merge($files_arr, $directories_arr); $fullpath = ''; foreach ($import_paths as $fullpath) { if (!is_readable($fullpath)) { echo "Warning: File '{$fullpath}' is unreadable!" . PHP_EOL; continue; } $skipfc_create = false; // Flag to prevent creation of new FC $filetype = filetype($fullpath); $shortpath = str_replace($syncdir . '/', '', $fullpath); $file = basename($fullpath); if ($staticsync_mapped_category_tree && !$treeprocessed) { $path_parts = explode("/", $shortpath); array_pop($path_parts); $treenodes = touch_category_tree_level($path_parts); $treeprocessed = true; } # -----FOLDERS------------- if ( ($filetype == 'dir' || $filetype == 'link') && count($staticsync_whitelist_folders) > 0 && !isPathWhitelisted($shortpath, $staticsync_whitelist_folders) ) { // Folders which are not whitelisted will not be processed any further continue; } if ( ($filetype == 'dir' || $filetype == 'link') && strpos($nogo, "[{$file}]") === false && strpos($file, $staticsync_alternatives_suffix) === false ) { // Recurse ProcessFolder("{$fullpath}"); } # -------FILES--------------- if (($filetype == "file") && (substr($file, 0, 1) != ".") && (strtolower($file) != "thumbs.db")) { if (isset($staticsync_file_minimum_age) && (time() - filectime($folder . "/" . $file) < $staticsync_file_minimum_age)) { // Don't process this file yet as it is too new echo " - " . $file . " is too new (" . (time() - filectime($folder . "/" . $file)) . " seconds), skipping\n"; continue; } # Work out extension $extension = mb_strcut(parse_filename_extension($file), 0, 10); $filename = pathinfo($file, PATHINFO_FILENAME); if (isset($staticsync_alternative_file_text) && strpos($file, $staticsync_alternative_file_text) !== false && !$staticsync_ingest_force) { // Set a flag so we can process this later in case we don't process this along with a primary resource file (it may be a new alternative file for an existing resource) $alternativefiles[] = $syncdir . '/' . $shortpath; continue; } elseif (isset($staticsync_alt_suffixes) && $staticsync_alt_suffixes && is_array($staticsync_alt_suffix_array)) { // Check if this is a file with a suffix defined in the $staticsync_alt_suffixes array and then process at the end foreach ($staticsync_alt_suffix_array as $altsfx => $altname) { $altsfxlen = mb_strlen($altsfx); $checksfx = substr($filename, -$altsfxlen) == $altsfx; if ($checksfx == $altsfx) { echo " - Adding to \$alternativefiles array " . $file . "\n"; $alternativefiles[] = $syncdir . '/' . $shortpath; continue 2; } } } $modified_extension = hook('staticsync_modify_extension', 'staticsync', array($fullpath, $shortpath, $extension)); if ($modified_extension !== false) { $extension = $modified_extension; } global $banned_extensions, $file_checksums, $file_upload_block_duplicates, $file_checksums_50k; # Check to see if extension is banned, do not add if it is banned if (array_search(strtolower($extension), array_map('strtolower', $banned_extensions)) !== false) { continue; } if ($count > $staticsync_max_files) { return true; } # Already exists or deleted/archived in which case we won't proceed? if (!isset($done[$shortpath])) { // Extra check to make sure we don't end up with duplicates $existing = ps_value("SELECT ref value FROM resource WHERE file_path = ?", array("s",$shortpath), 0); if ($existing > 0 || hook('staticsync_plugin_add_to_done')) { $done[$shortpath]["processed"] = true; $done[$shortpath]["modified"] = date('Y-m-d H:i:s', time()); continue; } # Check for duplicate files if ($file_upload_block_duplicates) { # Generate the ID if ($file_checksums_50k) { # Fetch the string used to generate the unique ID $use = filesize_unlimited($fullpath) . "_" . file_get_contents($fullpath, false, null, 0, 50000); $checksum = md5($use); } else { $checksum = md5_file($fullpath); } $duplicates = ps_array("select ref value from resource where file_checksum= ?", ['s', $checksum]); if (count($duplicates) > 0) { $message = str_replace("%resourceref%", implode(",", $duplicates), str_replace("%filename%", $fullpath, $lang['error-duplicatesfound'])); debug("STATICSYNC ERROR- " . $message); $errors[] = $message; continue; } } $count++; echo "Processing file: $fullpath" . PHP_EOL; if ($collection == 0 && $staticsync_autotheme && !$skipfc_create) { # Find or create a featured collection for this folder as required. $e = explode("/", $shortpath); $fallback_fc_categ_name = ucwords($e[0]); $name = (count($e) == 1) ? '' : $e[count($e) - 2]; echo " - Collection '{$name}'" . PHP_EOL; // The real featured collection will always be the last directory in the path $proposed_fc_categories = array_values(array_diff($e, array_slice($e, -2))); if (count($proposed_fc_categories) == 0) { if (count($e) > 1) { // This is a top level folder - this is needed to ensure no duplication of existing top level FCs echo " - File is in a top level folder" . PHP_EOL; $proposed_fc_categories = array($e[0]); } else { // This file is in the root folder, no FC needs to be created echo " - File is not in a folder, skipping FC creation" . PHP_EOL; $skipfc_create = true; } } if (!$skipfc_create) { echo " - Proposed Featured Collection Categories: " . join(" / ", $proposed_fc_categories) . PHP_EOL; // Build the tree first, if needed $proposed_branch_path = array(); for ($b = 0; $b < count($proposed_fc_categories); $b++) { $parent = ($b == 0 ? 0 : $proposed_branch_path[($b - 1)]); $fc_categ_name = ucwords($proposed_fc_categories[$b]); $params = []; if ($parent == 0) { $parent_sql = 'IS NULL'; } else { $parent_sql = '= ?'; $params[] = 'i'; $params[] = $parent; } $fc_categ_ref_sql = 'SELECT DISTINCT ref AS `value` FROM collection c LEFT JOIN collection_resource cr on c.ref = cr.collection WHERE parent ' . $parent_sql . ' AND type = ? AND name = ? GROUP BY c.ref HAVING count(DISTINCT cr.resource) = 0'; $fc_categ_ref = ps_value($fc_categ_ref_sql, array_merge($params, ['i', COLLECTION_TYPE_FEATURED, 's', $fc_categ_name]), 0); if ($fc_categ_ref == 0) { echo " - Creating new Featured Collection category named '{$fc_categ_name}'" . PHP_EOL; $fc_categ_ref = create_collection($userref, $fc_categ_name); echo " - Created '{$fc_categ_name}' with ref #{$fc_categ_ref}" . PHP_EOL; $updated_fc_category = save_collection( $fc_categ_ref, array( "featured_collections_changes" => array( "update_parent" => $parent, "force_featured_collection_type" => true, "thumbnail_selection_method" => $FEATURED_COLLECTION_BG_IMG_SELECTION_OPTIONS["most_popular_image"], ) ) ); if ($updated_fc_category === false) { echo " - Unable to update '{$fc_categ_name}' with ref #{$fc_categ_ref} to a Featured Collection Category" . PHP_EOL; } } $proposed_branch_path[] = $fc_categ_ref; } $collection_parent = array_pop($proposed_branch_path); if (is_null($collection_parent)) { // We don't have enough folders to create categories so the first one will do (legacy logic) $collection_parent = create_collection($userref, $fallback_fc_categ_name); save_collection( $collection_parent, array( "featured_collections_changes" => array( "update_parent" => 0, "force_featured_collection_type" => true, "thumbnail_selection_method" => $FEATURED_COLLECTION_BG_IMG_SELECTION_OPTIONS["most_popular_image"], ) ) ); } echo " - Collection parent should be ref #{$collection_parent}" . PHP_EOL; $params = []; if ($collection_parent == 0) { $parent_sql = 'IS NULL'; } else { $parent_sql = '= ?'; $params[] = 'i'; $params[] = $collection_parent; } $collection_sql = 'SELECT DISTINCT ref as `value` FROM collection c LEFT JOIN collection_resource cr on c.ref = cr.collection WHERE parent ' . $parent_sql . ' AND type = ? AND name = ? GROUP BY c.ref HAVING count(DISTINCT cr.resource) > 0'; $collection = ps_value($collection_sql, array_merge($params, ['i', COLLECTION_TYPE_FEATURED, 's', ucwords($name)]), 0); if ($collection == 0) { $collection = create_collection($userref, ucwords($name)); echo " - Created '{$name}' with ref #{$collection}" . PHP_EOL; $updated_fc_category = save_collection( $collection, array( "featured_collections_changes" => array( "update_parent" => $collection_parent, "force_featured_collection_type" => true, "thumbnail_selection_method" => $FEATURED_COLLECTION_BG_IMG_SELECTION_OPTIONS["most_popular_image"], ) ) ); if ($updated_fc_category === false) { echo " - Unable to update '{$name}' with ref #{$collection} to be a Featured Collection under parent ref #{$collection_parent}" . PHP_EOL; } } } } # Work out a resource type based on the extension. $type = (isset($GLOBALS['staticsync_extension_mapping_default']) ? $GLOBALS['staticsync_extension_mapping_default'] : $resource_type_extension_mapping_default); $rt_ext_mappings = (isset($GLOBALS['staticsync_extension_mapping']) ? $GLOBALS['staticsync_extension_mapping'] : $resource_type_extension_mapping); reset($rt_ext_mappings); foreach ($rt_ext_mappings as $rt => $extensions) { if (in_array(strtolower($extension), $extensions)) { $type = $rt; } } if (isset($staticsync_mapfolders)) { $field_nodes = array(); foreach ($staticsync_mapfolders as $mapfolder) { $match = $mapfolder["match"]; $field = $mapfolder["field"]; $level = $mapfolder["level"]; $path_parts = explode("/", $shortpath); if ( $field == 'resource_type' && (strpos("/" . $shortpath, $match) !== false) && $level < count($path_parts) ) { $value = $path_parts[$level - 1]; $typeidx = array_search($value, array_column($restypes, "name")); if ($typeidx !== false) { $type = $restypes[$typeidx]["ref"]; echo " - \$staticsync_mapfolders - set resource type to " . $value . " ($type)" . PHP_EOL; } } } } $modified_type = hook('modify_type', 'staticsync', array( $type )); if (is_numeric($modified_type)) { $type = $modified_type; } # Formulate a title if ($staticsync_title_includes_path && $view_title_field !== $filename_field) { $title_find = array('/', '_', ".$extension" ); $title_repl = array(' - ', ' ', ''); $title = ucfirst(str_ireplace($title_find, $title_repl, $shortpath)); } else { $title = str_ireplace(".$extension", '', $file); } $modified_title = hook('modify_title', 'staticsync', array( $title )); if ($modified_title !== false) { $title = $modified_title; } # Import this file $r = import_resource($shortpath, $type, $title, $staticsync_ingest, $extension); echo " - Created resource #" . $r . PHP_EOL; if ($r !== false) { # Add to mapped category tree (if configured) if (isset($staticsync_mapped_category_tree) && isset($treenodes) && count($treenodes) > 0) { // Add path nodes to resource add_resource_nodes($r, $treenodes); } # default access level. This may be overridden by metadata mapping. $accessval = 0; # StaticSync path / metadata mapping # Extract metadata from the file path as per $staticsync_mapfolders in config.php if (isset($staticsync_mapfolders)) { $field_nodes = array(); foreach ($staticsync_mapfolders as $mapfolder) { $match = $mapfolder["match"]; $field = $mapfolder["field"]; $level = $mapfolder["level"]; if (strpos("/" . $shortpath, $match) !== false) { # Match. Extract metadata. $path_parts = explode("/", $shortpath); if ($level < count($path_parts)) { // special cases first. if ($field == 'access') { # access level is a special case # first determine if the value matches a defined access level $value = $path_parts[$level - 1]; for ($n = 0; $n < 3; $n++) { # if we get an exact match or a match except for case if ($value == $lang["access" . $n] || strtoupper($value) == strtoupper($lang['access' . $n])) { $accessval = $n; echo " - Will set access level to " . $lang['access' . $n] . " ($n)" . PHP_EOL; } } } elseif ($field == 'archive') { # archive level is a special case # first determine if the value matches a defined archive level $value = $mapfolder["archive"]; $archive_array = array_merge(array(-2,-1,0,1,2,3), $additional_archive_states); if (in_array($value, $archive_array)) { $archiveval = $value; echo " - Will set archive level to " . $lang['status' . $value] . " ($archiveval)" . PHP_EOL; } } elseif (is_int_loose($field)) { # Save the value $value = $path_parts[$level - 1]; $modifiedval = hook('staticsync_mapvalue', '', array($r, $value)); if ($modifiedval) { $value = $modifiedval; } $field_info = get_resource_type_field($field); if (in_array($field_info['type'], $FIXED_LIST_FIELD_TYPES)) { $fieldnodes = get_nodes($field, null, $field_info['type'] == FIELD_TYPE_CATEGORY_TREE); if (in_array($value, array_column($fieldnodes, "name")) || ($field_info['type'] == FIELD_TYPE_DYNAMIC_KEYWORDS_LIST && !checkperm('bdk' . $field))) { // Add this to array of nodes to add if ($field_info['type'] == FIELD_TYPE_CATEGORY_TREE) { # Use value found in category tree $category_tree_values = array_filter($fieldnodes, function (array $fieldnodes) use ($value) { return $value == $fieldnodes['name']; }); $newnode = array_values($category_tree_values)[0]['ref']; # If multiple values found (category tree "leaves") we must pick one, taking first in array i.e. lowest node ref. echo " - Using category tree node $newnode - $value" . "\n"; } else { # Add new field for dynamic keywords list $newnode = set_node(null, $field, trim($value), null, null); echo " - Adding node" . trim($value) . "\n"; } $newnodes = array($newnode); if ($field_info['type'] == FIELD_TYPE_CATEGORY_TREE && $category_tree_add_parents) { // We also need to add all parent nodes for category trees $parent_nodes = get_parent_nodes($newnode); $newnodes = array_merge($newnodes, array_keys($parent_nodes)); } if ($staticsync_extension_mapping_append_values && !in_array($field_info['type'], array(FIELD_TYPE_DROP_DOWN_LIST,FIELD_TYPE_RADIO_BUTTONS)) && (!isset($staticsync_extension_mapping_append_values_fields) || in_array($field_info['ref'], $staticsync_extension_mapping_append_values_fields))) { // The $staticsync_extension_mapping_append_values variable actually refers to folder->metadata mapping, not the file extension $curnodes = get_resource_nodes($r, $field); $field_nodes[$field] = array_merge($curnodes, $newnodes, $field_nodes[$field] ?? []); } else { // We have got a new value for this field and we are not appending values, // replace any existing value the array $field_nodes[$field] = $newnodes; } } } else { if ( $staticsync_extension_mapping_append_values && (!isset($staticsync_extension_mapping_append_values_fields) || in_array($field_info['ref'], $staticsync_extension_mapping_append_values_fields)) && in_array( $field_info['type'], [ FIELD_TYPE_TEXT_BOX_SINGLE_LINE, FIELD_TYPE_TEXT_BOX_MULTI_LINE, FIELD_TYPE_TEXT_BOX_LARGE_MULTI_LINE, FIELD_TYPE_TEXT_BOX_FORMATTED_AND_TINYMCE, FIELD_TYPE_DATE,FIELD_TYPE_WARNING_MESSAGE, ] ) ) { // Append the values if possible $existing_value = get_data_by_field($r, $field); if ((string) $existing_value != "") { $values_to_add[$field] = $existing_value . ($staticsync_extension_mapping_append_separator ?? " ") . $value; } else { $values_to_add[$field] = $value; } } } } } } } if (count($field_nodes) > 0) { $nodes_to_add = array(); foreach ($field_nodes as $nodeids) { $nodes_to_add = array_merge($nodes_to_add, $nodeids); } } } # Update resource table $setvals = array(); $setvals["access"] = $accessval; if (isset($archiveval)) { $setvals["archive"] = $archiveval; } else { $setvals["archive"] = $staticsync_defaultstate; } if (!$enable_thumbnail_creation_on_upload) { $setvals["has_image"] = 0; $setvals["preview_attempts"] = 0; } $updatesql = array(); $params = []; foreach ($setvals as $name => $val) { $updatesql[] = $name . "= ? "; $params[] = 'i'; $params[] = $val; } $params[] = 'i'; $params[] = $r; ps_query("UPDATE resource SET " . implode(",", $updatesql) . " WHERE ref = ?", $params); unset($GLOBALS["get_resource_data_cache"][$r]); if (count($nodes_to_add ?? [])) { echo " - adding nodes " . implode(", ", $nodes_to_add) . "to resource: $r" . PHP_EOL; add_resource_nodes($r, $nodes_to_add); $joins = get_resource_table_joins(); foreach ($nodes_to_add as $node) { $node_data = []; if (get_node($node, $node_data) && in_array($node_data["resource_type_field"], $joins)) { update_resource_field_column($r, $node_data["resource_type_field"], $node_data["name"]); } } } if (count($values_to_add ?? [])) { foreach ($values_to_add as $field => $value) { echo " - adding value $value in field ref: $field to resource: $r" . PHP_EOL; $errors = []; $result = update_field($r, $field, $value); } } if (isset($staticsync_filepath_to_field)) { update_field($r, $staticsync_filepath_to_field, $shortpath); } # Add any alternative files $altpath = $fullpath . $staticsync_alternatives_suffix; if ($staticsync_ingest && file_exists($altpath)) { $adh = opendir($altpath); while (($altfile = readdir($adh)) !== false) { $filetype = filetype($altpath . "/" . $altfile); if (($filetype == "file") && (substr($file, 0, 1) != ".") && (strtolower($file) != "thumbs.db")) { # Create alternative file # Find extension $ext = explode(".", $altfile); $ext = $ext[count($ext) - 1]; $description = str_replace("?", strtoupper($ext), $lang["originalfileoftype"]); $file_size = filesize_unlimited($altpath . "/" . $altfile); $aref = add_alternative_file($r, $altfile, $description, $altfile, $ext, $file_size); $path = get_resource_path($r, true, '', true, $ext, -1, 1, false, '', $aref); $result = copy($altpath . "/" . $altfile, $path); // Copy alternative file instead of rename so that permissions of filestore will be used if ($result === false) { # The copy failed. debug(" - ERROR: Staticsync failed to copy alternative file from: " . $altpath . "/" . $altfile); return false; } $use_error_exception_cache = $GLOBALS["use_error_exception"] ?? false; $GLOBALS["use_error_exception"] = true; try { unlink($altpath . "/" . $altfile); try { chmod($path, 0777); } catch (Exception $e) { // Not fatal, just log debug(" - ERROR: Staticsync failed to set permissions on ingested alternative file: " . $path . PHP_EOL . " - Error message: " . $e->getMessage() . PHP_EOL); } } catch (Exception $e) { echo " - ERROR: failed to delete file from source. Please check correct permissions on: " . $syncdir . "/" . $shortpath . "\n - Error message: " . $e->getMessage() . PHP_EOL; return false; } $GLOBALS["use_error_exception"] = $use_error_exception_cache; } } } elseif (isset($staticsync_alternative_file_text)) { $basefilename = str_ireplace(".$extension", '', $file); $altfilematch = "/{$basefilename}{$staticsync_alternative_file_text}(.*)\.(.*)/"; echo " - Searching for alternative files for base file: " . $basefilename , PHP_EOL; echo " - Checking " . $altfilematch . PHP_EOL; $folder_files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($folder)); $altfiles = new RegexIterator($folder_files, $altfilematch, RecursiveRegexIterator::MATCH); foreach ($altfiles as $altfile) { staticsync_process_alt($altfile->getPathname(), $r); echo " - Processed alternative: " . $shortpath . PHP_EOL; } } # Add to collection if ($staticsync_autotheme && !$skipfc_create) { // Featured collection categories cannot contain resources. At this stage we need to distinguish // between categories and collections by checking for children collections. if (!is_featured_collection_category_by_children($collection)) { $test = ps_query("SELECT " . columns_in("collection_resource") . " FROM collection_resource WHERE collection= ? AND resource= ?", ['i', $collection, 'i', $r]); if (count($test) == 0) { ps_query("INSERT INTO collection_resource (collection, resource, date_added) VALUES (?, ?, NOW())", ['i', $collection, 'i', $r]); $featured_collection_parent = validate_collection_parent(get_collection($collection, false)); if (!in_array($featured_collection_parent, $GLOBALS['fcs_to_reorder'])) { $GLOBALS['fcs_to_reorder'][] = $featured_collection_parent; } } } else { echo " - Error: Unable to add resource to a featured collection category!" . PHP_EOL; exit(1); } } $done[$shortpath]["ref"] = $r; $done[$shortpath]["processed"] = true; $done[$shortpath]["modified"] = date('Y-m-d H:i:s', time()); update_disk_usage($r); } else { # Import failed - file still being uploaded? echo " *** Skipping file - it was not possible to move the file (still being imported/uploaded?)" . PHP_EOL; } } elseif ($staticsync_ingest_force) { // If the resource has a path but $ingest is true then the $ingest has been changed, need to copy original file into filestore global $get_resource_path_fpcache; $existing = $done[$shortpath]["ref"]; $alternative = isset($done[$shortpath]["alternative"]) ? $done[$shortpath]["alternative"] : -1; echo " - File already imported - $shortpath (resource #$existing, alternative #$alternative). Ingesting.." . PHP_EOL; $get_resource_path_fpcache[$existing] = ""; // Forces get_resource_path to ignore the syncdir file_path $destination = get_resource_path($existing, true, "", true, $extension, -1, 1, false, "", $alternative); $result = copy($syncdir . "/" . $shortpath, $destination); // Copy instead of rename so that permissions of filestore will be used if ($result === false) { # The copy failed. debug(" - ERROR: Staticsync failed to copy file from: " . $syncdir . "/" . $shortpath); return false; } $use_error_exception_cache = $GLOBALS["use_error_exception"] ?? false; $GLOBALS["use_error_exception"] = true; try { unlink($syncdir . "/" . $shortpath); try { chmod($destination, 0777); } catch (Exception $e) { // Not fatal, just log debug(" - ERROR: Staticsync failed to set permissions on ingested file: " . $destination . PHP_EOL . " - Error message: " . $e->getMessage()); } } catch (Exception $e) { echo " - ERROR: failed to delete file from source. Please check correct permissions on: " . $syncdir . "/" . $shortpath . PHP_EOL . " - Error message: " . $e->getMessage() . PHP_EOL; return false; } $GLOBALS["use_error_exception"] = $use_error_exception_cache; if ($alternative == -1) { ps_query("UPDATE resource SET file_path=NULL WHERE ref = ?", ['i', $existing]); } else { ps_query("UPDATE resource_alt_files SET file_name = ? WHERE resource = ? AND ref= ?", ['s', $file, 'i', $existing, 'i', $alternative]); } } elseif ( !isset($done[$shortpath]["archive"]) // Check modified times and and update previews if no existing archive state is set, || (isset($resource_deletion_state) && $done[$shortpath]["archive"] != $resource_deletion_state) // or if resource is not in system deleted state, || (isset($staticsync_revive_state) && $done[$shortpath]["archive"] == $staticsync_deleted_state) ) { // or resource is currently in staticsync deleted state and needs to be reinstated if (!file_exists($fullpath)) { echo " - Warning: File '{$fullpath}' does not exist anymore!"; continue; } $filemod = filemtime($fullpath); if (isset($done[$shortpath]["modified"]) && $filemod > strtotime($done[$shortpath]["modified"]) || (isset($staticsync_revive_state) && $done[$shortpath]["archive"] == $staticsync_deleted_state)) { $count++; # File has been modified since we last created previews. Create again. $rd = ps_query("SELECT ref, has_image, file_modified, file_extension, archive FROM resource WHERE file_path= ?", ['s', $shortpath]); if (count($rd) > 0) { $rd = $rd[0]; $rref = $rd["ref"]; echo " - Resource $rref has changed, regenerating previews: $fullpath" . PHP_EOL; extract_exif_comment($rref, $rd["file_extension"]); # extract text from documents (e.g. PDF, DOC). global $extracted_text_field; if (isset($extracted_text_field)) { if (isset($unoconv_path) && in_array($extension, $unoconv_extensions)) { // omit, since the unoconv process will do it during preview creation below } else { global $offline_job_queue, $offline_job_in_progress; if ($offline_job_queue && !$offline_job_in_progress) { $extract_text_job_data = array( 'ref' => $rref, 'extension' => $extension, ); job_queue_add('extract_text', $extract_text_job_data); } else { extract_text($rref, $extension); } } } # Store original filename in field, if set global $filename_field; if (isset($filename_field)) { update_field($rref, $filename_field, $file); } if ($enable_thumbnail_creation_on_upload) { create_previews($rref, false, $rd["file_extension"], false, false, -1, false, $staticsync_ingest); } $sql = ''; $params = []; if (isset($staticsync_revive_state) && ($rd["archive"] == $staticsync_deleted_state)) { $sql .= ", archive= ?"; $params[] = 'i'; $params[] = $staticsync_revive_state; } $params[] = 'i'; $params[] = $rref; ps_query("UPDATE resource SET file_modified=NOW() " . $sql . ((!$enable_thumbnail_creation_on_upload) ? ", has_image=0, preview_attempts=0 " : "") . " WHERE ref= ?", $params); if (isset($staticsync_revive_state) && ($rd["archive"] == $staticsync_deleted_state)) { # Log this resource_log($rref, LOG_CODE_STATUS_CHANGED, '', '', $staticsync_deleted_state, $staticsync_revive_state); } } } } } } closedir($dh); } function staticsync_process_alt($alternativefile, $ref = "", $alternative = "") { // Process an alternative file global $staticsync_alternative_file_text, $syncdir, $lang, $staticsync_ingest, $alternative_file_previews, $done, $filename_field, $view_title_field, $staticsync_title_includes_path, $staticsync_alt_suffixes, $staticsync_alt_suffix_array; $shortpath = str_replace($syncdir . '/', '', $alternativefile); if (!isset($done[$shortpath])) { $alt_parts = pathinfo($alternativefile); if (substr($alt_parts['filename'], 0, 1) == ".") { return false; } if (isset($staticsync_alternative_file_text) && strpos($alternativefile, $staticsync_alternative_file_text) !== false) { $altfilenameparts = explode($staticsync_alternative_file_text, $alt_parts['filename']); $altbasename = $altfilenameparts[0]; $altdesc = $altfilenameparts[1]; $altname = str_replace("?", strtoupper($alt_parts["extension"]), $lang["fileoftype"]); } elseif (isset($staticsync_alt_suffixes) && $staticsync_alt_suffixes && is_array($staticsync_alt_suffix_array)) { // Check for files with a suffix defined in the $staticsync_alt_suffixes array foreach ($staticsync_alt_suffix_array as $altsfx => $altname) { $altsfxlen = mb_strlen($altsfx); if (substr($alt_parts['filename'], -$altsfxlen) == $altsfx) { $altbasename = substr($alt_parts['filename'], 0, -$altsfxlen); $altdesc = strtoupper($alt_parts['extension']) . " " . $lang["file"]; break; } } } if ($ref == "") { // We need to find which resource this alternative file relates to echo " - Searching for primary resource related to " . $alternativefile . " in " . $alt_parts['dirname'] . '/' . $altbasename . "." . PHP_EOL; foreach ($done as $syncedfile => $synceddetails) { $syncedfile_parts = pathinfo($syncedfile); if ( strpos($syncdir . '/' . $syncedfile, $alt_parts['dirname'] . '/' . $altbasename . ".") !== false || (isset($altsfx) && $syncdir . '/' . $syncedfile_parts["filename"] . $altsfx . "." . $syncedfile_parts["extension"] == $alternativefile) ) { // This synced file has the same base name as the resource $ref = $synceddetails["ref"]; break; } } } if ($ref == "") { //Primary resource file may have been ingested on a previous run - try to locate it $ingested = ps_array( "SELECT resource value FROM resource_node rn LEFT JOIN node n ON n.ref=rn.node WHERE n.resource_type_field = ? AND rn.resource LIKE ?", ["i",$filename_field,"s",$altbasename . "%"] ); if (count($ingested) < 1) { echo " - No primary resource found for " . $alternativefile . ". Skipping file" . PHP_EOL; debug("staticsync - No primary resource found for " . $alternativefile . ". Skipping file"); return false; } if (count($ingested) == 1) { echo " - Found matching resource: " . $ingested[0] . PHP_EOL; $ref = $ingested[0]; } else { if ($staticsync_title_includes_path) { $title_find = array('/', '_'); $title_repl = array(' - ', ' '); $parentpath = ucfirst(str_ireplace($title_find, $title_repl, $shortpath)); echo " - This file has path: " . $parentpath . PHP_EOL; foreach ($ingested as $ingestedref) { $ingestedpath = get_data_by_field($ingestedref, $view_title_field); echo "Found resource with same name. Path: " . $ingestedpath . PHP_EOL; if (strpos($parentpath, $ingestedpath) !== false) { echo " - Found matching resource: " . $ingestedref . PHP_EOL; $ref = $ingestedref; break; } } } if ($ref == "") { echo " - Multiple possible primary resources found for " . $alternativefile . ". (Resource IDs: " . implode(",", $ingested) . "). Skipping file" . PHP_EOL; debug("staticsync - Multiple possible primary resources found for " . $alternativefile . ". (Resource IDs: " . implode(",", $ingested) . "). Skipping file"); return false; } } } echo " - Processing alternative file - '" . $alternativefile . "' for resource #" . $ref . PHP_EOL; if ($alternative == "") { // Create a new alternative file $alt["file_size"] = filesize_unlimited($alternativefile); $alt["extension"] = $alt_parts["extension"]; $alt["altdescription"] = $altdesc; $alt["name"] = $altname; $alt["ref"] = add_alternative_file($ref, $alt["name"], $alt["altdescription"], $alternativefile, $alt["extension"], $alt["file_size"]); $alternative = $alt["ref"]; echo " - Created a new alternative file - '" . $alt["ref"] . "' for resource #" . $ref . PHP_EOL; debug("Staticsync - Created a new alternative file - '" . $alt["ref"] . "' for resource #" . $ref); $alt["path"] = get_resource_path($ref, true, '', false, $alt["extension"], -1, 1, false, '', $alt["ref"]); echo " - Alternative file path - " . $alt["path"] . PHP_EOL; debug("Staticsync - alternative file path - " . $alt["path"]); $alt["basefilename"] = $altbasename; if ($staticsync_ingest) { echo " - Moving file to " . $alt["path"] . PHP_EOL; $result = copy($alternativefile, $alt["path"]); // Copy alternative file instead of rename so that permissions of filestore will be used if ($result === false) { debug(" - ERROR: Staticsync failed to copy alternative file from: {$alternativefile}"); return false; } $use_error_exception_cache = $GLOBALS["use_error_exception"] ?? false; $GLOBALS["use_error_exception"] = true; try { unlink($alternativefile); try { chmod($alt["path"], 0777); } catch (Exception $e) { // Not fatal, just log debug(" - ERROR: Staticsync failed to set permissions on ingested alternative file: " . $alt["path"] . PHP_EOL . " - Error message: " . $e->getMessage() . PHP_EOL); } } catch (Exception $e) { echo " - ERROR: failed to delete file from source. Please check correct permissions on: " . $alternativefile . PHP_EOL . " - Error message: " . $e->getMessage() . PHP_EOL; return false; } $GLOBALS["use_error_exception"] = $use_error_exception_cache; } if ($alternative_file_previews) { create_previews($ref, false, $alt["extension"], false, false, $alt["ref"], false, $staticsync_ingest); } hook("staticsync_after_alt", '', array($ref,$alt)); echo " - Added alternative file ref:" . $alt["ref"] . ", name: " . $alt["name"] . ". " . "(" . $alt["altdescription"] . ") Size: " . $alt["file_size"] . PHP_EOL; debug("Staticsync - added alternative file ref:" . $alt["ref"] . ", name: " . $alt["name"] . ". " . "(" . $alt["altdescription"] . ") Size: " . $alt["file_size"]); $done[$shortpath]["processed"] = true; } } elseif ($alternative != "" && $alternative_file_previews) { // An existing alternative file has changed, update previews if required debug("Alternative file changed, recreating previews"); create_previews($ref, false, pathinfo($alternativefile, PATHINFO_EXTENSION), false, false, $alternative, false, $staticsync_ingest); ps_query("UPDATE resource_alt_files SET creation_date=NOW() WHERE ref= ?", ['i', $alternative]); $done[$shortpath]["processed"] = true; } echo " - Completed path : " . $shortpath . PHP_EOL; $done[$shortpath]["ref"] = $ref; $done[$shortpath]["alternative"] = $alternative; set_process_lock("staticsync"); // Update the lock so we know it is still processing resources } # Recurse through the folder structure. ProcessFolder($syncdir); debug("StaticSync: \$done = " . json_encode($done)); echo " - Checking for alternative files that have not been processed" . PHP_EOL; foreach ($alternativefiles as $alternativefile) { $shortpath = str_replace($syncdir . "/", '', $alternativefile); echo " - Processing alternative file " . $shortpath . PHP_EOL; debug("Staticsync - Processing altfile " . $shortpath); if (array_key_exists($shortpath, $done) && isset($done[$shortpath]["alternative"]) && $done[$shortpath]["alternative"] > 0) { echo " - Alternative '{$shortpath}' has already been processed. Skipping" . PHP_EOL; continue; } if (!file_exists($alternativefile)) { echo " - Warning: File '{$alternativefile}' does not exist anymore!"; continue; } if (!isset($done[$shortpath])) { staticsync_process_alt($alternativefile); } elseif ($alternative_file_previews) { // File already synced but check if it has been modified as may need to update previews $altfilemod = filemtime($alternativefile); if (isset($done[$shortpath]["modified"]) && $altfilemod > strtotime($done[$shortpath]["modified"])) { // Update the alternative file staticsync_process_alt($alternativefile, $done[$shortpath]["resource"], $done[$shortpath]["alternative"]); } } } echo " - Checking deleted files" . PHP_EOL; if (!$staticsync_ingest) { # If not ingesting files, look for deleted files in the sync folder and archive the appropriate file from ResourceSpace. echo "Looking for deleted files..." . PHP_EOL; # For all resources with filepaths, check they still exist and archive if not. $resources_to_archive = array(); $n = 0; foreach ($done as $syncedfile => $synceddetails) { if (!isset($synceddetails["processed"]) && isset($synceddetails["archive"]) && !(isset($staticsync_ignore_deletion_states) && in_array($synceddetails["archive"], $staticsync_ignore_deletion_states)) && $synceddetails["archive"] != $staticsync_deleted_state || isset($synceddetails["alternative"])) { $resources_to_archive[$n]["file_path"] = $syncedfile; $resources_to_archive[$n]["ref"] = $synceddetails["ref"]; $resources_to_archive[$n]["archive"] = isset($synceddetails["archive"]) ? $synceddetails["archive"] : ""; if (isset($synceddetails["alternative"])) { $resources_to_archive[$n]["alternative"] = $synceddetails["alternative"]; } $n++; } } # ***for modified syncdir directories: $syncdonemodified = hook("modifysyncdonerf"); if (!empty($syncdonemodified)) { $resources_to_archive = $syncdonemodified; } // Get all the featured collections (including categories) that hold these resources $fc_branches = get_featured_collections_by_resources(array_column($resources_to_archive, "ref")); foreach ($resources_to_archive as $rf) { $fp = $syncdir . '/' . $rf["file_path"]; if (isset($rf['syncdir']) && $rf['syncdir'] != '') { # ***for modified syncdir directories: $fp = $rf['syncdir'] . $rf["file_path"]; } if ($fp != "" && !file_exists($fp)) { // Additional check - make sure the archive state hasn't changed since the start of the script $cas = ps_value("SELECT archive value FROM resource where ref = ?", ['i', $rf['ref']], 0); if (isset($staticsync_ignore_deletion_states) && !in_array($cas, $staticsync_ignore_deletion_states)) { if (!isset($rf["alternative"])) { echo " - File no longer exists: " . $rf["ref"] . " " . $fp . PHP_EOL; # Set to archived, unless state hasn't changed since script started. if (isset($staticsync_deleted_state)) { ps_query("UPDATE resource SET archive= ? WHERE ref= ?", ['i', $staticsync_deleted_state, 'i', $rf['ref']]); } else { delete_resource($rf["ref"]); } if (isset($resource_deletion_state) && $staticsync_deleted_state == $resource_deletion_state) { // Only remove from collections if we are really deleting this. Some configurations may have a separate state or synced resources may be temporarily absent ps_query("DELETE FROM collection_resource WHERE resource= ?", ['i', $rf['ref']]); } # Log this resource_log($rf['ref'], LOG_CODE_STATUS_CHANGED, '', '', $rf["archive"], $staticsync_deleted_state); } else { echo " - Alternative file no longer exists: resource " . $rf["ref"] . " alt:" . $rf["alternative"] . " " . $fp . PHP_EOL; ps_query("DELETE FROM resource_alt_files WHERE ref= ?", ['i', $rf['alternative']]); } } } } # Remove any themes that are now empty as a result of deleted files. echo " - Checking for empty featured collections" . PHP_EOL; foreach ($fc_branches as $fc_branch) { // Reverse the branch path to start from the leaf node. This way, when you reach the category you won't have any // children nodes (ie a normal FC) left (if it will be the case) and we'll be able to delete the FC category. $reversed_branch_path = array_reverse($fc_branch); foreach ($reversed_branch_path as $fc) { if (!can_delete_featured_collection($fc["ref"])) { continue; } if (delete_collection($fc["ref"]) === false) { echo " -- Unable to delete featured collection #{$fc["ref"]}" . PHP_EOL; } else { echo " -- Deleted featured collection #{$fc["ref"]}" . PHP_EOL; } } } echo " - Checking if featured collections have to be re-ordered (e.g if a category has become just a featured collection)" . PHP_EOL; foreach ($fcs_to_reorder as $fc_parent) { $new_fcs_order = reorder_all_featured_collections_with_parent($fc_parent); log_activity("via Static Sync, re-ordering for parent #{$fc_parent}", LOG_CODE_REORDERED, implode(', ', $new_fcs_order), 'collection'); } } if (count($errors) > 0) { echo PHP_EOL . "ERRORS: -" . PHP_EOL; echo implode(PHP_EOL, $errors) . PHP_EOL; if ($send_notification) { $notify_users = get_notification_users("SYSTEM_ADMIN"); foreach ($notify_users as $notify_user) { $admin_notify_users[] = $notify_user["ref"]; } $message = "STATICSYNC ERRORS FOUND: - " . PHP_EOL . implode(PHP_EOL, $errors); message_add($admin_notify_users, $message); } } echo "\nStaticSync completed at " . date('Y-m-d H:i:s', time()) . PHP_EOL; if ($suppress_output) { ob_clean(); } ps_query("UPDATE sysvars SET value=now() WHERE name='lastsync'"); clear_query_cache("sysvars"); clear_process_lock("staticsync");