diff --git a/assets/css/admin-tables.css b/assets/css/admin-tables.css
new file mode 100644
index 00000000000..bd30ea785c2
--- /dev/null
+++ b/assets/css/admin-tables.css
@@ -0,0 +1,52 @@
+.column-error_status .dashicons-editor-help {
+ color: #767676;
+}
+.column-sources_with_invalid_output .dashicons {
+ margin-right: 5px;
+}
+.column-sources_with_invalid_output .dashicons-admin-plugins {
+ color: #64a2e9;
+}
+.column-sources_with_invalid_output .dashicons-admin-appearance {
+ color: #ebb04f;
+}
+.column-sources_with_invalid_output .dashicons-wordpress-alt {
+ color: #92b371;
+}
+.amp-logo-icon {
+ background-image: url( '../images/amp-logo-icon.svg' );
+ background-color: transparent;
+ background-size: 20px 20px;
+ height: 20px;
+ width: 20px;
+ display: inline-block;
+}
+.column-error_status .error-status {
+ line-height: 20px;
+ display: inline-block;
+ position: relative;
+ vertical-align: top;
+ margin-left: 10px;
+}
+td.column-found_elements_and_attributes {
+ color: #c06e60;
+}
+.column-error_status .dashicons-flag.new {
+ color: #d98501;
+}
+.column-error_status .dashicons-yes.new {
+ color: #ff0000;
+}
+.column-error_status .dashicons-warning.rejected {
+ color: #68c6ff;
+}
+.column-sources_with_invalid_output .source {
+ margin-bottom: 10px;
+}
+.column-sources_with_invalid_output .source {
+ margin-bottom: 10px;
+ display: block;
+}
+.wrap .wp-heading-inline + .page-title-action {
+ margin-left: 1rem;
+}
diff --git a/assets/css/amp-validation-error-taxonomy.css b/assets/css/amp-validation-error-taxonomy.css
index 91192865397..74a3937049b 100644
--- a/assets/css/amp-validation-error-taxonomy.css
+++ b/assets/css/amp-validation-error-taxonomy.css
@@ -74,17 +74,27 @@ details[open] .details-attributes__summary::after {
color: #00a0d2;
}
+.column-sources_with_invalid_output details[open] .details-attributes__summary {
+ margin-bottom: 5px;
+}
+.column-sources_with_invalid_output details > div {
+ padding-left: 25px;
+}
+
/* Error details toggle button */
-.manage-column.column-details {
+.manage-column.column-details, .manage-column.column-sources_with_invalid_output {
display: flex;
justify-content: space-between;
align-items: center;
}
+.manage-column.column-sources_with_invalid_output .error-details-toggle {
+ margin: 0;
+}
.error-details-toggle {
display: flex;
flex-direction: column;
- height: 12px;
+ height: 14px;
margin-right: 10px;
padding: 0;
background: none;
diff --git a/assets/images/amp-logo-icon.svg b/assets/images/amp-logo-icon.svg
new file mode 100644
index 00000000000..f6f70c9ba55
--- /dev/null
+++ b/assets/images/amp-logo-icon.svg
@@ -0,0 +1,10 @@
+
+
+
+
diff --git a/assets/images/baseline-error-blue.svg b/assets/images/baseline-error-blue.svg
new file mode 100644
index 00000000000..fa6d7953888
--- /dev/null
+++ b/assets/images/baseline-error-blue.svg
@@ -0,0 +1,12 @@
+
+
+
diff --git a/assets/images/baseline-error.svg b/assets/images/baseline-error.svg
new file mode 100644
index 00000000000..e6d4620d34d
--- /dev/null
+++ b/assets/images/baseline-error.svg
@@ -0,0 +1,12 @@
+
+
+
diff --git a/assets/images/editor-help.svg b/assets/images/editor-help.svg
new file mode 100755
index 00000000000..e5cdf524755
--- /dev/null
+++ b/assets/images/editor-help.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/src/amp-validation-error-detail-toggle.js b/assets/src/amp-validation-error-detail-toggle.js
index 85275c20b06..38fb0282d25 100644
--- a/assets/src/amp-validation-error-detail-toggle.js
+++ b/assets/src/amp-validation-error-detail-toggle.js
@@ -6,7 +6,7 @@ import domReady from '@wordpress/dom-ready';
/**
* Localized data
*/
-import { btnAriaLabel } from 'amp-validation-i18n';
+import { btnAriaLabel, errorIndexLink, errorIndexAnchor } from 'amp-validation-i18n';
const OPEN_CLASS = 'is-open';
@@ -16,12 +16,19 @@ const OPEN_CLASS = 'is-open';
* table column via backend code.
*/
function addToggleButtons() {
- [ ...document.querySelectorAll( 'th.column-details.manage-column' ) ].forEach( th => {
+ const addButtons = ( th ) => {
const button = document.createElement( 'button' );
button.setAttribute( 'aria-label', btnAriaLabel );
button.setAttribute( 'type', 'button' );
button.setAttribute( 'class', 'error-details-toggle' );
th.appendChild( button );
+ };
+
+ [ ...document.querySelectorAll( 'th.column-details.manage-column' ) ].forEach( th => {
+ addButtons( th );
+ } );
+ [ ...document.querySelectorAll( 'th.manage-column.column-sources_with_invalid_output' ) ].forEach( th => {
+ addButtons( th );
} );
}
@@ -31,7 +38,7 @@ function addToggleButtons() {
function addToggleListener() {
let open = false;
- const details = [ ...document.querySelectorAll( '.column-details details' ) ];
+ const details = [ ...document.querySelectorAll( '.column-details details, .column-sources_with_invalid_output details' ) ];
const toggleButtons = [ ...document.querySelectorAll( 'button.error-details-toggle' ) ];
const onButtonClick = () => {
open = ! open;
@@ -54,7 +61,21 @@ function addToggleListener() {
} );
}
+// @todo This should be harmonized with the approach in PHP via AMP_Validation_Error_Taxonomy::render_link_to_errors_by_url().
+function addViewErrorsByTypeLinkButton() {
+ if ( 'undefined' === typeof errorIndexAnchor || 'undefined' === typeof errorIndexLink ) {
+ return;
+ }
+ const heading = document.querySelector( '.wp-heading-inline' );
+ const link = document.createElement( 'a' );
+ link.innerText = errorIndexAnchor;
+ link.setAttribute( 'href', errorIndexLink );
+ link.setAttribute( 'class', 'page-title-action' );
+ heading.after( link );
+}
+
domReady( () => {
addToggleButtons();
addToggleListener();
+ addViewErrorsByTypeLinkButton();
} );
diff --git a/includes/validation/class-amp-invalid-url-post-type.php b/includes/validation/class-amp-invalid-url-post-type.php
index 43e68e0a576..b18d0b9a470 100644
--- a/includes/validation/class-amp-invalid-url-post-type.php
+++ b/includes/validation/class-amp-invalid-url-post-type.php
@@ -85,13 +85,13 @@ public static function register() {
self::POST_TYPE_SLUG,
array(
'labels' => array(
- 'name' => _x( 'Invalid AMP Pages (URLs)', 'post type general name', 'amp' ),
- 'menu_name' => __( 'Invalid Pages', 'amp' ),
- 'singular_name' => __( 'Invalid AMP Page (URL)', 'amp' ),
- 'not_found' => __( 'No invalid AMP pages found', 'amp' ),
- 'not_found_in_trash' => __( 'No forgotten invalid AMP pages', 'amp' ),
- 'search_items' => __( 'Search invalid AMP pages', 'amp' ),
- 'edit_item' => __( 'Invalid AMP Page (URL)', 'amp' ),
+ 'name' => _x( 'Invalid URLs', 'post type general name', 'amp' ),
+ 'menu_name' => __( 'Invalid URLs', 'amp' ),
+ 'singular_name' => __( 'Invalid URL', 'amp' ),
+ 'not_found' => __( 'No invalid URLs found', 'amp' ),
+ 'not_found_in_trash' => __( 'No forgotten invalid URLs', 'amp' ),
+ 'search_items' => __( 'Search invalid URLs', 'amp' ),
+ 'edit_item' => __( 'Invalid URL', 'amp' ),
),
'supports' => false,
'public' => false,
@@ -126,6 +126,8 @@ public static function should_show_in_menu() {
* Add admin hooks.
*/
public static function add_admin_hooks() {
+ add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_post_list_screen_scripts' ) );
+
add_filter( 'dashboard_glance_items', array( __CLASS__, 'filter_dashboard_glance_items' ) );
add_action( 'rightnow_end', array( __CLASS__, 'print_dashboard_glance_styles' ) );
@@ -139,7 +141,6 @@ public static function add_admin_hooks() {
add_action( 'restrict_manage_posts', array( __CLASS__, 'render_post_filters' ), 10, 2 );
add_filter( 'manage_' . self::POST_TYPE_SLUG . '_posts_columns', array( __CLASS__, 'add_post_columns' ) );
add_action( 'manage_posts_custom_column', array( __CLASS__, 'output_custom_column' ), 10, 2 );
- add_filter( 'post_row_actions', array( __CLASS__, 'filter_row_actions' ), 10, 2 );
add_filter( 'bulk_actions-edit-' . self::POST_TYPE_SLUG, array( __CLASS__, 'filter_bulk_actions' ), 10, 2 );
add_filter( 'handle_bulk_actions-edit-' . self::POST_TYPE_SLUG, array( __CLASS__, 'handle_bulk_action' ), 10, 3 );
add_action( 'admin_notices', array( __CLASS__, 'print_admin_notice' ) );
@@ -169,6 +170,46 @@ public static function add_admin_hooks() {
} );
}
+ /**
+ * Enqueue style.
+ */
+ public static function enqueue_post_list_screen_scripts() {
+ // Styles.
+ $screen = get_current_screen();
+ if ( 'edit-amp_invalid_url' !== $screen->id ) {
+ return;
+ }
+
+ wp_enqueue_style(
+ 'amp-admin-tables',
+ amp_get_asset_url( 'css/admin-tables.css' ),
+ false,
+ AMP__VERSION
+ );
+ wp_enqueue_style(
+ 'amp-validation-error-taxonomy',
+ amp_get_asset_url( 'css/amp-validation-error-taxonomy.css' ),
+ array( 'common' ),
+ AMP__VERSION
+ );
+ wp_enqueue_script(
+ 'amp-validation-error-detail-toggle',
+ amp_get_asset_url( 'js/amp-validation-error-detail-toggle-compiled.js' ),
+ array(),
+ AMP__VERSION,
+ true
+ );
+ wp_localize_script(
+ 'amp-validation-error-detail-toggle',
+ 'ampValidationI18n',
+ array(
+ 'btnAriaLabel' => esc_attr__( 'Toggle all sources', 'amp' ),
+ 'errorIndexLink' => get_admin_url( null, 'edit-tags.php?taxonomy=amp_validation_error&post_type=amp_invalid_url' ),
+ 'errorIndexAnchor' => esc_html__( 'View Error Index', 'amp' ),
+ )
+ );
+ }
+
/**
* Add count of how many validation error posts there are to the admin menu.
*/
@@ -286,25 +327,34 @@ public static function display_invalid_url_validation_error_counts_summary( $pos
$result = array();
if ( $counts['new'] ) {
- $result[] = esc_html( sprintf(
+ if ( AMP_Validation_Manager::is_sanitization_forcibly_accepted() ) {
+ $icon = 'flag';
+ } else {
+ $icon = 'yes';
+ }
+ $result[] = sprintf(
/* translators: %s is count */
- __( '❓ New: %s', 'amp' ),
+ '%2$s: %3$s',
+ esc_attr( $icon ),
+ esc_html__( 'New', 'amp' ),
number_format_i18n( $counts['new'] )
- ) );
+ );
}
if ( $counts['accepted'] ) {
- $result[] = esc_html( sprintf(
- /* translators: %s is count */
- __( '✅ Accepted: %s', 'amp' ),
+ $result[] = sprintf(
+ /* translators: 1. Title, 2. %s is count */
+ '%1$s: %2$s',
+ esc_html__( 'Accepted', 'amp' ),
number_format_i18n( $counts['accepted'] )
- ) );
+ );
}
if ( $counts['rejected'] ) {
- $result[] = esc_html( sprintf(
+ $result[] = sprintf(
/* translators: %s is count */
- __( '❌ Rejected: %s', 'amp' ),
+ '%1$s: %2$s',
+ esc_html__( 'Rejected', 'amp' ),
number_format_i18n( $counts['rejected'] )
- ) );
+ );
}
echo implode( '
', $result ); // WPCS: xss ok.
}
@@ -544,18 +594,20 @@ public static function add_post_columns( $columns ) {
$columns = array_merge(
$columns,
array(
- 'error_status' => esc_html__( 'Error Status', 'amp' ),
- AMP_Validation_Error_Taxonomy::REMOVED_ELEMENTS => esc_html__( 'Removed Elements', 'amp' ),
- AMP_Validation_Error_Taxonomy::REMOVED_ATTRIBUTES => esc_html__( 'Removed Attributes', 'amp' ),
- AMP_Validation_Error_Taxonomy::SOURCES_INVALID_OUTPUT => esc_html__( 'Incompatible Sources', 'amp' ),
+ AMP_Validation_Error_Taxonomy::ERROR_STATUS => sprintf( '%s', esc_html__( 'Status', 'amp' ) ), // @todo Create actual tooltip.
+ AMP_Validation_Error_Taxonomy::FOUND_ELEMENTS_AND_ATTRIBUTES => esc_html__( 'Invalid', 'amp' ),
+ AMP_Validation_Error_Taxonomy::SOURCES_INVALID_OUTPUT => esc_html__( 'Sources', 'amp' ),
)
);
+ if ( isset( $columns['title'] ) ) {
+ $columns['title'] = esc_html__( 'URL', 'amp' );
+ }
+
// Move date to end.
if ( isset( $columns['date'] ) ) {
- $date = $columns['date'];
unset( $columns['date'] );
- $columns['date'] = $date;
+ $columns['date'] = esc_html__( 'Last Checked', 'amp' );
}
return $columns;
@@ -585,9 +637,9 @@ public static function output_custom_column( $column_name, $post_id ) {
}
self::display_invalid_url_validation_error_counts_summary( $post_id );
break;
- case AMP_Validation_Error_Taxonomy::REMOVED_ELEMENTS:
+ case AMP_Validation_Error_Taxonomy::FOUND_ELEMENTS_AND_ATTRIBUTES:
+ $items = array();
if ( ! empty( $error_summary[ AMP_Validation_Error_Taxonomy::REMOVED_ELEMENTS ] ) ) {
- $items = array();
foreach ( $error_summary[ AMP_Validation_Error_Taxonomy::REMOVED_ELEMENTS ] as $name => $count ) {
if ( 1 === intval( $count ) ) {
$items[] = sprintf( '%s
', esc_html( $name ) );
@@ -595,82 +647,87 @@ public static function output_custom_column( $column_name, $post_id ) {
$items[] = sprintf( '%s
(%d)', esc_html( $name ), $count );
}
}
- echo implode( ', ', $items ); // WPCS: XSS OK.
- } else {
- esc_html_e( '--', 'amp' );
}
- break;
- case AMP_Validation_Error_Taxonomy::REMOVED_ATTRIBUTES:
if ( ! empty( $error_summary[ AMP_Validation_Error_Taxonomy::REMOVED_ATTRIBUTES ] ) ) {
- $items = array();
foreach ( $error_summary[ AMP_Validation_Error_Taxonomy::REMOVED_ATTRIBUTES ] as $name => $count ) {
if ( 1 === intval( $count ) ) {
- $items[] = sprintf( '%s
', esc_html( $name ) );
+ $items[] = sprintf( '[%s]
', esc_html( $name ) );
} else {
- $items[] = sprintf( '%s
(%d)', esc_html( $name ), $count );
+ $items[] = sprintf( '[%s]
(%d)', esc_html( $name ), $count );
}
}
- echo implode( ', ', $items ); // WPCS: XSS OK.
+ }
+ if ( ! empty( $items ) ) {
+ echo implode( ',
', $items ); // WPCS: XSS OK.
} else {
esc_html_e( '--', 'amp' );
}
break;
case AMP_Validation_Error_Taxonomy::SOURCES_INVALID_OUTPUT:
if ( isset( $error_summary[ AMP_Validation_Error_Taxonomy::SOURCES_INVALID_OUTPUT ] ) ) {
- $sources = array();
- foreach ( $error_summary[ AMP_Validation_Error_Taxonomy::SOURCES_INVALID_OUTPUT ] as $type => $names ) {
- foreach ( array_unique( $names ) as $name ) {
- $sources[] = sprintf( '%s: %s
', esc_html( $type ), esc_html( $name ) );
+ $sources = $error_summary[ AMP_Validation_Error_Taxonomy::SOURCES_INVALID_OUTPUT ];
+ $output = array();
+
+ if ( isset( $sources['plugin'] ) ) {
+ $output[] = '%s
', esc_html__( 'Plugin', 'amp' ) );
+ } else {
+ $output[] = sprintf( '%s (%d)
', esc_html__( 'Plugins', 'amp' ), $count );
+ }
+ $output[] = '
', array_unique( $plugin_names ) );
+ $output[] = '%s
', esc_html__( 'Other', 'amp' ) );
+ } else {
+ $output[] = sprintf( '%s (%d)
', esc_html__( 'Other', 'amp' ), $count );
+ }
+ $output[] = '
', array_unique( $sources['core'] ) );
+ $output[] = '
-
+
post_type ) {
+ return $actions;
+ }
+
+ // Inline edits are not relevant.
+ unset( $actions['inline hide-if-no-js'] );
+
+ if ( isset( $actions['edit'] ) ) {
+ $actions['edit'] = sprintf(
+ '%s',
+ esc_url( get_edit_post_link( $post ) ),
+ esc_html__( 'Details', 'amp' )
+ );
+ }
+
+ if ( 'trash' !== $post->post_status ) {
+ $url = self::get_url_from_post( $post );
+ if ( $url ) {
+ $actions['view'] = sprintf(
+ '%s',
+ esc_url( add_query_arg( AMP_Validation_Manager::VALIDATE_QUERY_VAR, '', $url ) ),
+ esc_html__( 'View', 'amp' )
+ );
+ }
+
+ $actions[ self::VALIDATE_ACTION ] = sprintf(
+ '%s',
+ esc_url( self::get_recheck_url( $post ) ),
+ esc_html__( 'Recheck', 'amp' )
+ );
+ if ( self::get_post_staleness( $post ) ) {
+ $actions[ self::VALIDATE_ACTION ] = sprintf( '%s', $actions[ self::VALIDATE_ACTION ] );
+ }
+ }
+
// Replace 'Trash' text with 'Forget'.
if ( isset( $actions['trash'] ) ) {
$actions['trash'] = sprintf(
@@ -1712,5 +1806,4 @@ public static function filter_bulk_post_updated_messages( $messages, $bulk_count
return $messages;
}
-
}
diff --git a/includes/validation/class-amp-validation-error-taxonomy.php b/includes/validation/class-amp-validation-error-taxonomy.php
index b97c4f4e8ee..8925eaaf6f0 100644
--- a/includes/validation/class-amp-validation-error-taxonomy.php
+++ b/includes/validation/class-amp-validation-error-taxonomy.php
@@ -157,6 +157,13 @@ class AMP_Validation_Error_Taxonomy {
*/
const REMOVED_ELEMENTS = 'removed_elements';
+ /**
+ * The key for found elements and attributes.
+ *
+ * @var string
+ */
+ const FOUND_ELEMENTS_AND_ATTRIBUTES = 'found_elements_and_attributes';
+
/**
* The key for removed attributes.
*
@@ -178,6 +185,13 @@ class AMP_Validation_Error_Taxonomy {
*/
const REMOVED_SOURCES = 'removed_sources';
+ /**
+ * The key for the error status.
+ *
+ * @var string
+ */
+ const ERROR_STATUS = 'error_status';
+
/**
* Whether the terms_clauses filter should apply to a term query for validation errors to limit to a given status.
*
@@ -664,7 +678,7 @@ public static function add_admin_hooks() {
wp_enqueue_script(
'amp-validation-error-detail-toggle',
amp_get_asset_url( 'js/amp-validation-error-detail-toggle-compiled.js' ),
- array( 'wp-dom-ready' ),
+ array(),
AMP__VERSION,
true
);
@@ -887,9 +901,6 @@ public static function render_taxonomy_filters( $taxonomy_name ) {
$( function() {
// Move the filter UI after the 'Bulk Actions'