Files
resourcespace/plugins/action_dates/hooks/all.php
2025-07-18 16:20:14 +07:00

447 lines
23 KiB
PHP
Executable File

<?php
function HookAction_datesAllInitialise()
{
global $action_dates_fieldvars;
config_register_core_fieldvars("Action dates plugin", $action_dates_fieldvars);
}
function HookAction_datesCronCron()
{
global $lang, $action_dates_restrictfield,$action_dates_deletefield, $resource_deletion_state,
$action_dates_reallydelete, $action_dates_email_admin_days, $email_notify, $email_from,
$applicationname, $action_dates_new_state, $action_dates_remove_from_collection,
$action_dates_extra_config, $DATE_FIELD_TYPES, $action_dates_email_for_state,
$action_dates_email_for_restrict, $action_dates_eligible_states, $action_dates_weekdays, $action_dates_workflow_actions;
global $baseurl,$baseurl_short;
$validfieldtypes = [FIELD_TYPE_DATE_AND_OPTIONAL_TIME,FIELD_TYPE_EXPIRY_DATE,FIELD_TYPE_DATE];
# Save the resource_deletion_state because it can be manipulated during primary action processing
$saved_resource_deletion_state = $resource_deletion_state;
if (PHP_SAPI == "cli") {
echo "action_dates: Running cron tasks on " . date("Y-m-d h:i:s") . PHP_EOL;
}
// Don't run more than once every 24 hours
$last_action_dates_cron = get_sysvar('last_action_dates_cron', '1970-01-01');
# No need to run if already run in last 24 hours.
if (time() - strtotime($last_action_dates_cron) < 24 * 60 * 60) {
if ('cli' == PHP_SAPI) {
echo " - Skipping action date cron - last run: " . $last_action_dates_cron . PHP_EOL;
}
return false;
}
# Store time to update last run date/time after completion
$this_run_start = date("Y-m-d H:i:s");
// Check for correct day of week
if (!in_array(date("w"), $action_dates_weekdays)) {
if ('cli' == PHP_SAPI) {
echo "action_dates: not correct weekday to run" . PHP_EOL;
}
return true;
}
# Reset any residual userref from earlier cron tasks
global $userref;
$userref = 0;
$eligible_states = array();
if (isset($action_dates_eligible_states) && is_array($action_dates_eligible_states)) {
$eligible_states = $action_dates_eligible_states;
}
$allowable_fields = ps_array("SELECT ref AS value FROM resource_type_field WHERE type in (" . ps_param_insert(count($validfieldtypes)) . ")", ps_param_fill($validfieldtypes, "i"), "schema");
$email_state_refs = array(); # List of refs which are due to undergo state change (including full deletion) in n days
$email_state_days = array(); # List of days due to undergo state change (including full deletion) in n days
$email_restrict_refs = array(); # List of refs which are due to be restricted in n days
$email_restrict_days = array(); # List of days due to be restricted in n days
$state_change_notify = array(); # List of refs whose state has changed (excluding full deletion)
$action_date_current = date_create(date("Y-m-d")); # Date of this run
# Process resource access restriction if a restriction date has been configured
# The restriction date will be processed if it is full date or a partial date because either will yield viable timestamps
if (in_array($action_dates_restrictfield, $allowable_fields)) {
if (PHP_SAPI == "cli") {
echo "action_dates: Checking restrict action field $action_dates_restrictfield." . PHP_EOL;
}
$sql = "SELECT rn.resource, n.name AS value FROM resource_node rn LEFT JOIN node n ON n.ref=rn.node LEFT JOIN resource r ON r.ref=rn.resource ";
$sql_params = array();
if (!empty($eligible_states)) {
$sql .= "AND r.archive IN (" . ps_param_insert(count($eligible_states)) . ") ";
$sql_params = array_merge($sql_params, ps_param_fill($eligible_states, "i"));
}
$sql .= "WHERE r.ref > 0 AND r.access=0 AND n.resource_type_field = ?";
$sql_params = array_merge($sql_params, array('i',$action_dates_restrictfield));
$restrict_resources = ps_query($sql, $sql_params);
foreach ($restrict_resources as $resource) {
$ref = $resource["resource"];
$restrict_date_target = date_create($resource["value"]); # Value of the restrict date from metadata
# Candidate restriction date reached or passed
if ($action_date_current >= $restrict_date_target) {
# Restrict access to the resource
$existing_access = ps_value("SELECT access AS value FROM resource WHERE ref = ?", ["i",$ref], "");
if ($existing_access == 0) { # Only apply to resources that are currently open
if (PHP_SAPI == "cli") {
echo " - Restricting resource {$ref}" . PHP_EOL;
}
ps_query("UPDATE resource SET access=1 WHERE ref = ?", ["i",$ref]);
resource_log($ref, 'a', '', $lang['action_dates_restrict_logtext'], $existing_access, 1);
}
} else {
# Due to restrict in n days
if ($action_dates_email_admin_days != "") { # Set up email notification to admin of expiring resources
$restrict_interval = date_diff($action_date_current, $restrict_date_target);
$days_before_restrict = (int) $restrict_interval->format('%R%a');
# Check due number of days within range for notification
if ($days_before_restrict <= $action_dates_email_admin_days) {
$email_restrict_refs[] = $ref;
$email_restrict_days[] = $days_before_restrict;
}
}
}
}
}
# Process resource deletion or statechange if designated date has been configured
# The designated date will be processed if it is a valid date field
if (in_array($action_dates_deletefield, $allowable_fields)) {
$change_archive_state = false;
$fieldinfo = get_resource_type_field($action_dates_deletefield);
if (PHP_SAPI == "cli") {
echo "action_dates: Checking state action field $action_dates_deletefield." . PHP_EOL;
}
$validrestypes = false;
if ($fieldinfo["global"] == 0) {
$validrestypes = ps_array("SELECT resource_type value FROM resource_type_field_resource_type WHERE resource_type_field = ?", ["i",$action_dates_deletefield]);
}
if ($action_dates_reallydelete) {
# FULL DELETION - Build candidate list of resources which have the deletion date field populated
$sql = "SELECT rn.resource, n.name AS value FROM resource_node rn LEFT JOIN node n ON n.ref=rn.node LEFT JOIN resource r ON r.ref=rn.resource ";
$sql_params = array();
if (!empty($eligible_states)) {
$sql .= "AND r.archive IN (" . ps_param_insert(count($eligible_states)) . ") ";
$sql_params = array_merge($sql_params, ps_param_fill($eligible_states, "i"));
}
$sql .= "WHERE r.ref > 0 AND n.resource_type_field = ?";
$sql_params = array_merge($sql_params, array("i",$action_dates_deletefield));
// Filter resource types that shouldn't have access to the field
if (is_array($validrestypes)) {
$sql .= " AND r.resource_type IN (" . ps_param_insert(count($validrestypes)) . ") ";
$sql_params = array_merge($sql_params, ps_param_fill($validrestypes, "i"));
}
$candidate_resources = ps_query($sql, $sql_params);
} else {
# NOT FULL DELETION - If not already configured, establish the default resource deletion state
if (!isset($resource_deletion_state)) {
$resource_deletion_state = 3;
}
# NOT FULL DELETION - Build candidate list of resources which have the deletion date field populated
# and which are neither in the resource deletion state nor in the action dates new state
$sql = "SELECT rn.resource, n.name AS value FROM resource_node rn LEFT JOIN node n ON n.ref=rn.node LEFT JOIN resource r ON r.ref=rn.resource WHERE r.ref > 0 AND n.resource_type_field = ? ";
$sql_params = ["i",$action_dates_deletefield];
if (!empty($eligible_states)) {
$sql .= "AND r.archive IN (" . ps_param_insert(count($eligible_states)) . ") ";
$sql_params = array_merge($sql_params, ps_param_fill($eligible_states, "i"));
}
$sql .= " AND r.archive NOT IN (?,?) ";
$sql_params = array_merge($sql_params, ["i",$resource_deletion_state,"i",$action_dates_new_state]);
// Filter resource types that shouldn't have access to the field
if (is_array($validrestypes)) {
$sql .= " AND r.resource_type IN (" . ps_param_insert(count($validrestypes)) . ") ";
$sql_params = array_merge($sql_params, ps_param_fill($validrestypes, "i"));
}
$candidate_resources = ps_query($sql, $sql_params);
# NOT FULL DELETION - Resolve the target archive state to which candidate resources are to be moved
# If the new state differs from the default resource deletion state, it means we only want to move resources to that state
if ($action_dates_new_state != $resource_deletion_state) {
$resource_deletion_state = $action_dates_new_state;
$change_archive_state = true;
}
# The resource deletion state now represents the target archive state
}
# Process the list of candidates
foreach ($candidate_resources as $resource) {
$ref = $resource['resource'];
$action_date_target = date_create($resource["value"]); # Value of the restrict date from metadata
# Candidate deletion date reached or passed
if ($action_date_current >= $action_date_target) {
if (PHP_SAPI == "cli") {
if (!$change_archive_state) {
// Delete the resource as date has been reached
echo " - Deleting resource {$ref}" . PHP_EOL;
} else {
echo " - Moving resource {$ref} to archive state '{$resource_deletion_state}'" . PHP_EOL;
}
}
if ($action_dates_reallydelete) {
# FULL DELETION
# Unset deletion state to force the resource to be fully deleted
$original_deletion_state = $GLOBALS['resource_deletion_state'];
unset($GLOBALS['resource_deletion_state']);
delete_resource($ref);
$GLOBALS['resource_deletion_state'] = $original_deletion_state;
} else {
# NOT FULL DELETION - Update resources to the target archive state
ps_query("UPDATE resource SET archive = ? WHERE ref = ?", ["i",$resource_deletion_state,"i",$ref]);
$state_change_notify[] = $ref;
if ($action_dates_remove_from_collection) {
// Remove the resource from any collections
$ref_containing_collections = ps_array("SELECT collection AS `value` FROM collection_resource where resource = ?", array('i', $ref));
foreach ($ref_containing_collections as $collection) {
collection_log($collection, LOG_CODE_COLLECTION_REMOVED_RESOURCE, $ref, $lang['action_dates_delete_logtext']);
}
ps_query("DELETE FROM collection_resource WHERE resource=?", ["i",$ref]);
}
}
resource_log($ref, 'x', '', $lang['action_dates_delete_logtext']);
} else {
# Due to be deleted (or ortherwise actioned) in n days
if ($action_dates_email_admin_days != "") { # Set up email notification to admin of resources changing state
$action_interval = date_diff($action_date_current, $action_date_target);
$days_before_action = (int) $action_interval->format('%R%a');
if ($days_before_action <= $action_dates_email_admin_days) {
$email_state_refs[] = $ref;
$email_state_days[] = $days_before_action;
}
}
}
}
if ($action_dates_workflow_actions == "1" && count($state_change_notify) > 0) {
hook('after_update_archive_status', '', array($state_change_notify, $resource_deletion_state,""));
}
}
// Only allow email for actions configured.
if ($action_dates_restrictfield == "") {
$action_dates_email_for_restrict = "0";
}
if ($action_dates_deletefield == "") {
$action_dates_email_for_state = "0";
}
$message_state = "";
$message_restrict = "";
$message_combined = "";
$subject_state = "";
$subject_restrict = "";
$subject_combined = "";
$subject_state = $lang['action_dates_email_subject_state'];
if (count($email_state_days) == 0 || (min($email_state_days) == max($email_state_days))) {
$message_state = str_replace("[days]", (count($email_state_days) > 0 ? min($email_state_days) : $action_dates_email_admin_days), $lang['action_dates_email_text_state']);
} else {
$message_state = str_replace("[days_min]", (count($email_state_days) > 0 ? min($email_state_days) : $action_dates_email_admin_days), $lang['action_dates_email_range_state']);
$message_state = str_replace("[days_max]", (count($email_state_days) > 0 ? max($email_state_days) : $action_dates_email_admin_days), $message_state);
}
$subject_restrict = $lang['action_dates_email_subject_restrict'];
if (count($email_restrict_days) == 0 || (min($email_restrict_days) == max($email_restrict_days))) {
$message_restrict = str_replace("[days]", (count($email_restrict_days) > 0 ? min($email_restrict_days) : $action_dates_email_admin_days), $lang['action_dates_email_text_restrict']) . "\r\n";
} else {
$message_restrict = str_replace("[days_min]", (count($email_restrict_days) > 0 ? min($email_restrict_days) : $action_dates_email_admin_days), $lang['action_dates_email_range_restrict']);
$message_restrict = str_replace("[days_max]", (count($email_restrict_days) > 0 ? max($email_restrict_days) : $action_dates_email_admin_days), $message_restrict);
}
# Determine how and to whom notifications are to be sent
$admin_notify_emails = array();
$admin_notify_users = array();
$notify_users = get_notification_users("RESOURCE_ADMIN");
foreach ($notify_users as $notify_user) {
get_config_option(['user' => $notify_user['ref'], 'usergroup' => $notify_user['usergroup']], 'user_pref_resource_notifications', $send_message);
if (!$send_message) {
# If this user doesn't want notifications they won't get any messages or emails
continue;
}
# Notification is required; it will either be sent as an email only or as a message with a possible additional email
get_config_option(['user' => $notify_user['ref'], 'usergroup' => $notify_user['usergroup']], 'email_user_notifications', $send_email);
if ($send_email && $notify_user["email"] != "") {
$admin_notify_emails[] = $notify_user['email'];
} else {
$admin_notify_users[] = $notify_user["ref"];
}
}
# Prepare and send combined restrict and/or state change notifications if necessary
if (
(count($email_restrict_refs) > 0 && count($email_state_refs) > 0)
&& ($action_dates_email_for_restrict == "1" && $action_dates_email_for_state == "1")
) {
# Notification is for the resources whose dates are within the specified number of days
$subject_combined = $lang['action_dates_email_subject'];
$action_combined_days = array_merge($email_restrict_days, $email_state_days);
if (min($action_combined_days) == max($action_combined_days)) {
$message_combined = str_replace("[days]", min($action_combined_days), $lang['action_dates_email_text']) . "\r\n";
} else {
$message_combined = str_replace("[days_min]", min($action_combined_days), $lang['action_dates_email_range']);
$message_combined = str_replace("[days_max]", max($action_combined_days), $message_combined) . "\r\n";
}
$notification_restrict = $message_restrict;
$notification_state = $message_state;
# Reconstruct combined message for purposes of emailing
$message_combined = $message_restrict . $baseurl . "?r=" . implode("\r\n" . $baseurl . "?r=", $email_restrict_refs) . "\r\n";
$message_combined .= $message_state . $baseurl . "?r=" . implode("\r\n" . $baseurl . "?r=", $email_state_refs) . "\r\n";
$templatevars['message'] = $message_combined;
# Construct url lists for message_add function
$url_restrict = build_specialsearch_list_urls($email_restrict_refs);
$url_state = build_specialsearch_list_urls($email_state_refs);
foreach ($admin_notify_emails as $admin_notify_email) {
send_mail($admin_notify_email, $applicationname . ": " . $subject_combined, $message_combined, "", "", "emailproposedchanges", $templatevars);
}
if (count($admin_notify_users) > 0) {
if (PHP_SAPI == "cli") {
echo "Sending notification to user refs: " . implode(",", $admin_notify_users) . PHP_EOL;
}
# Note that message_add can also send an additional email
message_add($admin_notify_users, $notification_restrict . $url_restrict['multiple'], $url_restrict['single'], 0);
message_add($admin_notify_users, $notification_state . $url_state['multiple'], $url_state['single'], 0);
}
# Now empty the arrays to prevent separate notifications because they have already been dealt with here
$email_state_refs = array();
$email_restrict_refs = array();
}
# Prepare and send separate state change notifications
if (count($email_state_refs) > 0 && $action_dates_email_for_state == "1") {
# Send a notification for the resources whose date is within the specified number of days
$notification_state = $message_state;
$message_state .= $baseurl . "?r=" . implode("\r\n" . $baseurl . "?r=", $email_state_refs) . "\r\n";
$url = build_specialsearch_list_urls($email_state_refs);
$templatevars['message'] = $message_state;
foreach ($admin_notify_emails as $admin_notify_email) {
send_mail($admin_notify_email, $applicationname . ": " . $subject_state, $message_state, "", "", "emailproposedchanges", $templatevars);
}
if (count($admin_notify_users) > 0) {
if (PHP_SAPI == "cli") {
echo "Sending notification to user refs: " . implode(",", $admin_notify_users) . PHP_EOL;
}
# Note that message_add can also send an additional email
message_add($admin_notify_users, $notification_state . $url['multiple'], $url['single'], 0);
}
}
# Prepare and send separate access restrict notifications
if (count($email_restrict_refs) > 0 && $action_dates_email_for_restrict == "1") {
# Send a notification for the resources whose date is within the specified number of days
$notification_restrict = $message_restrict;
$message_restrict .= $baseurl . "?r=" . implode("\r\n" . $baseurl . "?r=", $email_restrict_refs) . "\r\n";
$url = build_specialsearch_list_urls($email_restrict_refs);
$templatevars['message'] = $message_restrict;
foreach ($admin_notify_emails as $admin_notify_email) {
send_mail($admin_notify_email, $applicationname . ": " . $subject_restrict, $message_restrict, "", "", "emailproposedchanges", $templatevars);
}
if (count($admin_notify_users) > 0) {
if (PHP_SAPI == "cli") {
echo "Sending notification to user refs: " . implode(",", $admin_notify_users) . PHP_EOL;
}
# Note that message_add can also send an additional email
message_add($admin_notify_users, $notification_restrict . $url['multiple'], $url['single'], 0);
}
}
# Restore the resource_deletion_state which may have been manipulated during primary action processing
$resource_deletion_state = $saved_resource_deletion_state;
# Perform additional actions if configured
foreach ($action_dates_extra_config as $action_dates_extra_config_setting) {
$datefield = get_resource_type_field($action_dates_extra_config_setting["field"]);
$field = $datefield["ref"];
$validrestypes = false;
if ($datefield["global"] == 0) {
$validrestypes = ps_array("SELECT resource_type value FROM resource_type_field_resource_type WHERE resource_type_field = ?", ["i",$field]);
}
$newstatus = $action_dates_extra_config_setting["status"];
if (in_array($datefield['type'], $DATE_FIELD_TYPES)) {
if (PHP_SAPI == "cli") {
echo "action_dates: Checking extra action dates for field " . $datefield["ref"] . "." . PHP_EOL;
}
$sql = "SELECT
rn.resource,
n.name AS value
FROM resource_node rn
LEFT JOIN resource r ON r.ref=rn.resource
LEFT JOIN node n ON n.ref=rn.node
WHERE r.ref > 0
AND n.resource_type_field = ?
AND r.archive<> ?";
$sql_params = array(
"i",$field,
"i",$newstatus,
);
if (!is_null($resource_deletion_state)) {
$sql .= " AND r.archive <> ?";
$sql_params[] = "i";
$sql_params[] = $resource_deletion_state;
}
// Filter resource types that shouldn't have access to the field
if (is_array($validrestypes)) {
$sql .= " AND r.resource_type IN (" . ps_param_insert(count($validrestypes)) . ") ";
$sql_params = array_merge($sql_params, ps_param_fill($validrestypes, "i"));
}
$additional_resources = ps_query($sql, $sql_params);
foreach ($additional_resources as $resource) {
$ref = $resource["resource"];
if (time() >= strtotime($resource["value"])) {
if (PHP_SAPI == "cli") {
echo "action_dates: Moving resource {$ref} to archive state " . $lang["status" . $newstatus] . PHP_EOL;
}
update_archive_status($ref, $newstatus);
}
}
}
}
# Update last run date/time.
set_sysvar("last_action_dates_cron", $this_run_start);
}
// This is required if cron task is run via pages/tools/cron_copy_hitcount.php
function HookAction_datesCron_copy_hitcountCron()
{
HookAction_datesCronCron();
}