Loading safedelete.module +123 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ use Drupal\menu_link_content\Entity\MenuLinkContent; use Drupal\media\Entity\Media; use Drupal\file\Entity\File; use Drupal\Core\Link; use Drupal\Core\Render\Markup; /** Loading Loading @@ -611,3 +612,125 @@ function safedelete_cron() { $database = \Drupal::database(); AdminHelper::listOrphanedNodes($reporteddate, $orphanedpages_results,$filedirectory, $database); } /** * Implements hook_moderated_content_bulk_publish_verify_archived(). * * @param \Drupal\moderated_content_bulk_publish\HookObject. * An object with properties by reference. */ function safedelete_moderated_content_bulk_publish_verify_archived($hookObject) { $state = 'archived'; $limit = 20; $nid = $hookObject->nid; $bundle = $hookObject->bundle; $show_button = $hookObject->show_button; $markup = $hookObject->markup; $error_message = $hookObject->error_message; AdminHelper::checkNodeReferencesMessage($nid, $bundle, $show_button, $markup, $state, $limit); // Pass references back. $hookObject->show_button = $show_button; $hookObject->markup = $markup; //echo print_r($hookObject, TRUE); if (!$hookObject->show_button) { $hookObject->error_message = t('Unable to archive due to other content linking to this content. See warning message(s)'); } } /** * Implements hook_moderated_content_bulk_publish_verify_publish(). * * @param \Drupal\moderated_content_bulk_publish\HookObject. * An object with properties by reference. */ function safedelete_moderated_content_bulk_publish_verify_publish($hookObject) { $state = 'publish'; $body_field_val = $hookObject->body_field_val; $nid = $hookObject->nid; AdminHelper::checkUrlToken($body_field_val, $found_token_href, $msgdetail_isToken); AdminHelper::checkPagePublished($body_field_val, $found_unpublished_node, $msgdetail_isPublished); AdminHelper::checkAbsoluteUrl($body_field_val, $found_absolute_url, $msgdetail_isAbsoluteURL); if ($found_token_href || $found_unpublished_node || $found_absolute_url) { $msgtitle = t('Unable to publish node %nid. See warning message(s).',['%nid' =>$nid]); $hookObject->error_message = $msgtitle; $hookObject->msgdetail_isToken = $msgdetail_isToken; $hookObject->msgdetail_isPublished = $msgdetail_isPublished; $hookObject->msgdetail_isAbsoluteURL = $msgdetail_isAbsoluteURL; $hookObject->validate_failure = TRUE; } } /** * Implements hook_preprocess_status_messages(). * * Updates or removes the status message (type = status), * and displays the warning message (type = warning) if bulk action process fails. */ function safedelete_preprocess_status_messages(&$variables) { $msg = ''; $msgCount = 0; $action = 'Archive current revision'; if (isset($variables['message_list']['status'])){ $msgCount = count($variables['message_list']['status']); if ($msgCount > 0) { // Get the last item of Status Message for the archive bulk action. $msg = $variables['message_list']['status'][($msgCount-1)]; }; } // Only handles Archive current revision action if (isset($variables['message_list']['warning']) && (stripos($msg, $action) !== false)) { $warning_messages = $variables['message_list']['warning']; $warningCount = count($variables['message_list']['warning']); $totalCount = 0; if ($warningCount > 0) { $int_str = preg_replace('/[^0-9]/', '', $msg); $totalCount = intval($int_str); $diffCount = $totalCount - ($warningCount - 1); $diff_str = strval($diffCount); if ($diffCount == 0 ) { unset($variables['message_list']['status']); } else if ($diffCount == 1) { $new_msg = t(str_replace($int_str . ' items', $diff_str . ' item', $msg)); $variables['message_list']['status'][($msgCount-1)] = Markup::create($new_msg); } else { $diff_str = strval($diffCount); $new_msg = t(str_replace($int_str ,$diff_str, $msg)); $variables['message_list']['status'][($msgCount-1)] = Markup::create($new_msg); } } } $bulk_publish_action = 'Publish latest revision'; // Only handles Publish latest revision action if (isset($variables['message_list']['warning']) && (stripos($msg, $bulk_publish_action) !== false)) { $warningCount = 0; $warning_messages = $variables['message_list']['warning']; foreach($warning_messages as $delta => $message) { $pattern = '/Unable to publish node \<em class=\"placeholder\"\>\d*\<\/em\>./'; if(preg_match_all($pattern, $message, $matches)) { $warningCount = $warningCount + 1; } } if ($warningCount > 0) { $int_str = preg_replace('/[^0-9]/', '', $msg); $totalCount = intval($int_str); $diffCount = $totalCount - $warningCount; $diff_str = strval($diffCount); if ($diffCount == 0 ) { unset($variables['message_list']['status']); } else if ($diffCount == 1) { $new_msg = t(str_replace($int_str . ' items', $diff_str . ' item', $msg)); $variables['message_list']['status'][0] = Markup::create($new_msg); } else { $diff_str = strval($diffCount); $new_msg = t(str_replace($int_str ,$diff_str, $msg)); $variables['message_list']['status'][0] = Markup::create($new_msg); } } } } src/AdminHelper.php +140 −4 Original line number Diff line number Diff line Loading @@ -11,6 +11,7 @@ use Drupal\file\Entity\File; use Drupal\Core\Link; use Drupal\Core\Url; use Drupal\Core\Database\Connection; use Drupal\Component\Utility\UrlHelper; class AdminHelper { Loading Loading @@ -375,6 +376,7 @@ EOT; * Safe delete validation and message prep. */ static public function checkNodeReferencesMessage($nid = null, &$bundle = 'page', &$show_button = TRUE, &$markup = '', $type = 'delete', $limit = 20) { $root_nid = $nid; $action_result = 'deleted'; if ($type == 'archived') { $action_result = $type; Loading Loading @@ -434,8 +436,11 @@ EOT; if (count($result) > 0) { $show_button = FALSE; $msg_type = t('Safe Delete verification check status'); $temp = 'This node is being used in entities and cannot be %action. Please edit the following content first and remove the link:'; $markup = t($temp, ['%action' => $action_result]) . '<ul>'; $temp = 'This node %nid is being used in entities and cannot be %action. Please edit the following content first and remove the link:'; $markup = t($temp, [ '%nid' => $root_nid, '%action' => $action_result, ]) . '<ul>'; $list_count = 0; foreach ($result as $entity_type => $results) { Loading Loading @@ -697,6 +702,19 @@ EOT; public static function getUrlForNode($nid, $lang = 'en') { $host_path = \Drupal::request()->getSchemeAndHttpHost(); $base_path = \Drupal::request()->getBasePath(); $is_multilingual = (int) \Drupal::languageManager()->isMultilingual(); $has_langprefix = FALSE; if ($is_multilingual) { static::addToLog($is_multilingual . ' = multilingual', FALSE); $config = \Drupal::configFactory()->get('language.negotiation'); $prefix_config = $config->get('url.prefixes'); if (isset($prefix_config[$lang])) { if ($prefix_config[$lang] == $lang) { $has_langprefix = TRUE; } } } if (strlen($base_path) > 0) { if (stripos(strval($base_path), '/') < 0) { $host_path = $host_path . '/' . $base_path; Loading @@ -707,12 +725,130 @@ EOT; } $relative = \Drupal::service('path_alias.manager')->getAliasByPath('/node/' . $nid, $lang); if (isset($relative['alias'])) { if ($has_langprefix && isset($relative['alias'])) { $url = $host_path . '/' . $lang . $relative['alias']; } else { else if ($has_langprefix) { $url = $host_path . '/' . $lang . '/node/' . $nid; } else { $url = $host_path . '/node/' . $nid; } return $url; } /** * Verify if the current access unpublished hashtoken is found. */ public static function checkUrlToken($bodyval, &$foundtokenhref = FALSE, &$markup = '') { $regex_token = '/\?auHash=/'; preg_match_all($regex_token, $bodyval, $matches, PREG_SET_ORDER); foreach ($matches as $match_token) { $tokenhref = reset($match_token); if (isset($tokenhref) && strlen($tokenhref) == 8) { if (!$foundtokenhref) { $foundtokenhref = TRUE; $msg = t('Please remove any links to Preview/Draft pages (pages whose URL contains "auHash").'); $markup = '<p style="color:#e0ac00; font-size:16px;"><strong>' . $msg .'</strong></p>'; break; } } } } /** * Verify if the page is published or not. */ public static function checkPagePublished($formbody, &$foundunpublishingnode = FALSE, &$markup ='') { $temparry = []; $regex_mediaobj = "/data-entity-type=\"node\" data-entity-uuid=\"([a-z]|[0-9]){8}-(([0-9]|[a-z]){4}-){3}([0-9]|[a-z]){12}\"/"; $regex_uuid = "/([a-z]|[0-9]){8}-(([0-9]|[a-z]){4}-){3}([0-9]|[a-z]){12}/"; preg_match_all($regex_mediaobj, $formbody, $matches, PREG_SET_ORDER); foreach ($matches as $match) { $tmpmsg = ''; $node = reset($match); preg_match_all($regex_uuid, $node, $id_match, PREG_SET_ORDER); $nodeuuid = $id_match[0][0]; $nentity = \Drupal::service('entity.repository')->loadEntityByUuid('node', $nodeuuid); if (is_null($nentity)) { continue; } $nodeid = $nentity->id(); $nmstate = $nentity->get('moderation_state')->getValue(); $nmstatetmp = array_pop($nmstate); $nmstateStr = array_pop($nmstatetmp); if ($nmstateStr != 'published') { if (!$foundunpublishingnode) { $foundunpublishingnode = TRUE; $msg = t('Please publish the identified pages first and then publish the current one.'); $markup = '<p style="color:#e0ac00; font-size:16px;"><strong>' . $msg .'</strong></p>'; $markup = $markup . '<ul>'; } $tmpmsg = '<li>' . $node . " /node/" . $nodeid . '</li>'; if (!in_array($tmpmsg, $temparry, true)) { $markup = $markup . $tmpmsg; array_push($temparry, $tmpmsg); } } } if ($foundunpublishingnode) { $markup =$markup . '</ul>'; } } /** * Verify if the url in the form body is absolute or not. * * Returns (bool) * True or false. */ public static function checkAbsoluteUrl($formbody, &$found_absolute_url = FALSE, &$markup ='') { $regex_tokenabspath = '/href=\".*\"/m'; preg_match_all($regex_tokenabspath, $formbody, $pathmatches, PREG_SET_ORDER); $temparry = []; foreach ($pathmatches as $match) { $tmpmsg = ''; $hrefpath = reset($match); // Ignore the token case. $tokenhead = "?auHash="; if (strpos($hrefpath, $tokenhead) == FALSE) { $path = str_replace("href=\"", "", $hrefpath); $path = str_replace("\"", "", $path); if (UrlHelper::isExternal($path) && UrlHelper::isValid($path, TRUE)) { if (UrlHelper::externalIsLocal($path, \Drupal::request()->getSchemeAndHttpHost())) { $absolutepathstring = $path; $base_path = \Drupal::request()->getBasePath(); $host = parse_url($path, PHP_URL_HOST); $host_end = strpos($path, $host) + strlen($host) + strlen($base_path); $path = substr($path, $host_end); $path = urldecode(trim($path, '/')); $path_args = explode('/', $path); $prefix = array_shift($path_args); $path = '/' . implode('/', $path_args); $nodpath = \Drupal::service('path_alias.manager')->getPathByAlias($path, $prefix); if (preg_match('/node\/(\d+)/', $nodpath, $matches)) { $hrefnode = Node::load($matches[1]); if (isset($hrefnode)) { if (!$found_absolute_url) { $found_absolute_url = TRUE; $msg = t('Please use Linkit to complete the reference.'); $markup = '<p style="color:#e0ac00; font-size:16px;"><strong>' . $msg .'</strong></p>'; $markup = $markup . '<ul>'; } $tmpmsg = '<li>' . $absolutepathstring . '</li>'; if (!in_array($tmpmsg, $temparry, true)) { $markup = $markup . $tmpmsg; array_push($temparry, $tmpmsg); } } } } } } } if ($found_absolute_url) { $markup = $markup . '</ul>'; } } } Loading
safedelete.module +123 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ use Drupal\menu_link_content\Entity\MenuLinkContent; use Drupal\media\Entity\Media; use Drupal\file\Entity\File; use Drupal\Core\Link; use Drupal\Core\Render\Markup; /** Loading Loading @@ -611,3 +612,125 @@ function safedelete_cron() { $database = \Drupal::database(); AdminHelper::listOrphanedNodes($reporteddate, $orphanedpages_results,$filedirectory, $database); } /** * Implements hook_moderated_content_bulk_publish_verify_archived(). * * @param \Drupal\moderated_content_bulk_publish\HookObject. * An object with properties by reference. */ function safedelete_moderated_content_bulk_publish_verify_archived($hookObject) { $state = 'archived'; $limit = 20; $nid = $hookObject->nid; $bundle = $hookObject->bundle; $show_button = $hookObject->show_button; $markup = $hookObject->markup; $error_message = $hookObject->error_message; AdminHelper::checkNodeReferencesMessage($nid, $bundle, $show_button, $markup, $state, $limit); // Pass references back. $hookObject->show_button = $show_button; $hookObject->markup = $markup; //echo print_r($hookObject, TRUE); if (!$hookObject->show_button) { $hookObject->error_message = t('Unable to archive due to other content linking to this content. See warning message(s)'); } } /** * Implements hook_moderated_content_bulk_publish_verify_publish(). * * @param \Drupal\moderated_content_bulk_publish\HookObject. * An object with properties by reference. */ function safedelete_moderated_content_bulk_publish_verify_publish($hookObject) { $state = 'publish'; $body_field_val = $hookObject->body_field_val; $nid = $hookObject->nid; AdminHelper::checkUrlToken($body_field_val, $found_token_href, $msgdetail_isToken); AdminHelper::checkPagePublished($body_field_val, $found_unpublished_node, $msgdetail_isPublished); AdminHelper::checkAbsoluteUrl($body_field_val, $found_absolute_url, $msgdetail_isAbsoluteURL); if ($found_token_href || $found_unpublished_node || $found_absolute_url) { $msgtitle = t('Unable to publish node %nid. See warning message(s).',['%nid' =>$nid]); $hookObject->error_message = $msgtitle; $hookObject->msgdetail_isToken = $msgdetail_isToken; $hookObject->msgdetail_isPublished = $msgdetail_isPublished; $hookObject->msgdetail_isAbsoluteURL = $msgdetail_isAbsoluteURL; $hookObject->validate_failure = TRUE; } } /** * Implements hook_preprocess_status_messages(). * * Updates or removes the status message (type = status), * and displays the warning message (type = warning) if bulk action process fails. */ function safedelete_preprocess_status_messages(&$variables) { $msg = ''; $msgCount = 0; $action = 'Archive current revision'; if (isset($variables['message_list']['status'])){ $msgCount = count($variables['message_list']['status']); if ($msgCount > 0) { // Get the last item of Status Message for the archive bulk action. $msg = $variables['message_list']['status'][($msgCount-1)]; }; } // Only handles Archive current revision action if (isset($variables['message_list']['warning']) && (stripos($msg, $action) !== false)) { $warning_messages = $variables['message_list']['warning']; $warningCount = count($variables['message_list']['warning']); $totalCount = 0; if ($warningCount > 0) { $int_str = preg_replace('/[^0-9]/', '', $msg); $totalCount = intval($int_str); $diffCount = $totalCount - ($warningCount - 1); $diff_str = strval($diffCount); if ($diffCount == 0 ) { unset($variables['message_list']['status']); } else if ($diffCount == 1) { $new_msg = t(str_replace($int_str . ' items', $diff_str . ' item', $msg)); $variables['message_list']['status'][($msgCount-1)] = Markup::create($new_msg); } else { $diff_str = strval($diffCount); $new_msg = t(str_replace($int_str ,$diff_str, $msg)); $variables['message_list']['status'][($msgCount-1)] = Markup::create($new_msg); } } } $bulk_publish_action = 'Publish latest revision'; // Only handles Publish latest revision action if (isset($variables['message_list']['warning']) && (stripos($msg, $bulk_publish_action) !== false)) { $warningCount = 0; $warning_messages = $variables['message_list']['warning']; foreach($warning_messages as $delta => $message) { $pattern = '/Unable to publish node \<em class=\"placeholder\"\>\d*\<\/em\>./'; if(preg_match_all($pattern, $message, $matches)) { $warningCount = $warningCount + 1; } } if ($warningCount > 0) { $int_str = preg_replace('/[^0-9]/', '', $msg); $totalCount = intval($int_str); $diffCount = $totalCount - $warningCount; $diff_str = strval($diffCount); if ($diffCount == 0 ) { unset($variables['message_list']['status']); } else if ($diffCount == 1) { $new_msg = t(str_replace($int_str . ' items', $diff_str . ' item', $msg)); $variables['message_list']['status'][0] = Markup::create($new_msg); } else { $diff_str = strval($diffCount); $new_msg = t(str_replace($int_str ,$diff_str, $msg)); $variables['message_list']['status'][0] = Markup::create($new_msg); } } } }
src/AdminHelper.php +140 −4 Original line number Diff line number Diff line Loading @@ -11,6 +11,7 @@ use Drupal\file\Entity\File; use Drupal\Core\Link; use Drupal\Core\Url; use Drupal\Core\Database\Connection; use Drupal\Component\Utility\UrlHelper; class AdminHelper { Loading Loading @@ -375,6 +376,7 @@ EOT; * Safe delete validation and message prep. */ static public function checkNodeReferencesMessage($nid = null, &$bundle = 'page', &$show_button = TRUE, &$markup = '', $type = 'delete', $limit = 20) { $root_nid = $nid; $action_result = 'deleted'; if ($type == 'archived') { $action_result = $type; Loading Loading @@ -434,8 +436,11 @@ EOT; if (count($result) > 0) { $show_button = FALSE; $msg_type = t('Safe Delete verification check status'); $temp = 'This node is being used in entities and cannot be %action. Please edit the following content first and remove the link:'; $markup = t($temp, ['%action' => $action_result]) . '<ul>'; $temp = 'This node %nid is being used in entities and cannot be %action. Please edit the following content first and remove the link:'; $markup = t($temp, [ '%nid' => $root_nid, '%action' => $action_result, ]) . '<ul>'; $list_count = 0; foreach ($result as $entity_type => $results) { Loading Loading @@ -697,6 +702,19 @@ EOT; public static function getUrlForNode($nid, $lang = 'en') { $host_path = \Drupal::request()->getSchemeAndHttpHost(); $base_path = \Drupal::request()->getBasePath(); $is_multilingual = (int) \Drupal::languageManager()->isMultilingual(); $has_langprefix = FALSE; if ($is_multilingual) { static::addToLog($is_multilingual . ' = multilingual', FALSE); $config = \Drupal::configFactory()->get('language.negotiation'); $prefix_config = $config->get('url.prefixes'); if (isset($prefix_config[$lang])) { if ($prefix_config[$lang] == $lang) { $has_langprefix = TRUE; } } } if (strlen($base_path) > 0) { if (stripos(strval($base_path), '/') < 0) { $host_path = $host_path . '/' . $base_path; Loading @@ -707,12 +725,130 @@ EOT; } $relative = \Drupal::service('path_alias.manager')->getAliasByPath('/node/' . $nid, $lang); if (isset($relative['alias'])) { if ($has_langprefix && isset($relative['alias'])) { $url = $host_path . '/' . $lang . $relative['alias']; } else { else if ($has_langprefix) { $url = $host_path . '/' . $lang . '/node/' . $nid; } else { $url = $host_path . '/node/' . $nid; } return $url; } /** * Verify if the current access unpublished hashtoken is found. */ public static function checkUrlToken($bodyval, &$foundtokenhref = FALSE, &$markup = '') { $regex_token = '/\?auHash=/'; preg_match_all($regex_token, $bodyval, $matches, PREG_SET_ORDER); foreach ($matches as $match_token) { $tokenhref = reset($match_token); if (isset($tokenhref) && strlen($tokenhref) == 8) { if (!$foundtokenhref) { $foundtokenhref = TRUE; $msg = t('Please remove any links to Preview/Draft pages (pages whose URL contains "auHash").'); $markup = '<p style="color:#e0ac00; font-size:16px;"><strong>' . $msg .'</strong></p>'; break; } } } } /** * Verify if the page is published or not. */ public static function checkPagePublished($formbody, &$foundunpublishingnode = FALSE, &$markup ='') { $temparry = []; $regex_mediaobj = "/data-entity-type=\"node\" data-entity-uuid=\"([a-z]|[0-9]){8}-(([0-9]|[a-z]){4}-){3}([0-9]|[a-z]){12}\"/"; $regex_uuid = "/([a-z]|[0-9]){8}-(([0-9]|[a-z]){4}-){3}([0-9]|[a-z]){12}/"; preg_match_all($regex_mediaobj, $formbody, $matches, PREG_SET_ORDER); foreach ($matches as $match) { $tmpmsg = ''; $node = reset($match); preg_match_all($regex_uuid, $node, $id_match, PREG_SET_ORDER); $nodeuuid = $id_match[0][0]; $nentity = \Drupal::service('entity.repository')->loadEntityByUuid('node', $nodeuuid); if (is_null($nentity)) { continue; } $nodeid = $nentity->id(); $nmstate = $nentity->get('moderation_state')->getValue(); $nmstatetmp = array_pop($nmstate); $nmstateStr = array_pop($nmstatetmp); if ($nmstateStr != 'published') { if (!$foundunpublishingnode) { $foundunpublishingnode = TRUE; $msg = t('Please publish the identified pages first and then publish the current one.'); $markup = '<p style="color:#e0ac00; font-size:16px;"><strong>' . $msg .'</strong></p>'; $markup = $markup . '<ul>'; } $tmpmsg = '<li>' . $node . " /node/" . $nodeid . '</li>'; if (!in_array($tmpmsg, $temparry, true)) { $markup = $markup . $tmpmsg; array_push($temparry, $tmpmsg); } } } if ($foundunpublishingnode) { $markup =$markup . '</ul>'; } } /** * Verify if the url in the form body is absolute or not. * * Returns (bool) * True or false. */ public static function checkAbsoluteUrl($formbody, &$found_absolute_url = FALSE, &$markup ='') { $regex_tokenabspath = '/href=\".*\"/m'; preg_match_all($regex_tokenabspath, $formbody, $pathmatches, PREG_SET_ORDER); $temparry = []; foreach ($pathmatches as $match) { $tmpmsg = ''; $hrefpath = reset($match); // Ignore the token case. $tokenhead = "?auHash="; if (strpos($hrefpath, $tokenhead) == FALSE) { $path = str_replace("href=\"", "", $hrefpath); $path = str_replace("\"", "", $path); if (UrlHelper::isExternal($path) && UrlHelper::isValid($path, TRUE)) { if (UrlHelper::externalIsLocal($path, \Drupal::request()->getSchemeAndHttpHost())) { $absolutepathstring = $path; $base_path = \Drupal::request()->getBasePath(); $host = parse_url($path, PHP_URL_HOST); $host_end = strpos($path, $host) + strlen($host) + strlen($base_path); $path = substr($path, $host_end); $path = urldecode(trim($path, '/')); $path_args = explode('/', $path); $prefix = array_shift($path_args); $path = '/' . implode('/', $path_args); $nodpath = \Drupal::service('path_alias.manager')->getPathByAlias($path, $prefix); if (preg_match('/node\/(\d+)/', $nodpath, $matches)) { $hrefnode = Node::load($matches[1]); if (isset($hrefnode)) { if (!$found_absolute_url) { $found_absolute_url = TRUE; $msg = t('Please use Linkit to complete the reference.'); $markup = '<p style="color:#e0ac00; font-size:16px;"><strong>' . $msg .'</strong></p>'; $markup = $markup . '<ul>'; } $tmpmsg = '<li>' . $absolutepathstring . '</li>'; if (!in_array($tmpmsg, $temparry, true)) { $markup = $markup . $tmpmsg; array_push($temparry, $tmpmsg); } } } } } } } if ($found_absolute_url) { $markup = $markup . '</ul>'; } } }