'lgbf_link', 'plural' => 'lgbf_links', 'ajax' => false, ) ); } /** * @return array Columns. */ public function get_columns() { return array( 'cb' => '', 'url' => __( 'URL', '404zen-broken-link-fixer' ), 'status' => __( 'Status', '404zen-broken-link-fixer' ), 'anchor_text' => __( 'Anchor text', '404zen-broken-link-fixer' ), 'source' => __( 'Found in', '404zen-broken-link-fixer' ), 'last_checked' => __( 'Last checked', '404zen-broken-link-fixer' ), ); } /** * @return array Sortable columns. */ protected function get_sortable_columns() { return array( 'url' => array( 'url', false ), 'status' => array( 'http_status', false ), 'source' => array( 'post_id', false ), 'last_checked' => array( 'last_checked', true ), ); } /** * @return array Bulk actions. */ protected function get_bulk_actions() { return array( 'recheck' => __( 'Recheck', '404zen-broken-link-fixer' ), 'dismiss' => __( 'Dismiss', '404zen-broken-link-fixer' ), 'unlink' => __( 'Unlink (keep text)', '404zen-broken-link-fixer' ), ); } /** * Status filter views above the table. * * @return array */ protected function get_views() { $counts = LGBF_Links_Store::get_counts(); $current = $this->status_filter; $base = admin_url( 'admin.php?page=lgbf-404zen' ); $views_def = array( 'problems' => array( __( 'All problems', '404zen-broken-link-fixer' ), $counts['problems'] ), 'broken' => array( __( 'Broken', '404zen-broken-link-fixer' ), $counts['broken'] ), 'timeout' => array( __( 'Timeouts', '404zen-broken-link-fixer' ), $counts['timeout'] ), 'redirect' => array( __( 'Redirect chains', '404zen-broken-link-fixer' ), $counts['redirect'] ), 'dismissed' => array( __( 'Dismissed', '404zen-broken-link-fixer' ), $counts['dismissed'] ), 'ok' => array( __( 'OK', '404zen-broken-link-fixer' ), $counts['ok'] ), ); $views = array(); foreach ( $views_def as $key => $def ) { $class = ( $key === $current ) ? ' class="current"' : ''; $url = esc_url( add_query_arg( 'link_status', $key, $base ) ); $views[ $key ] = sprintf( '%s (%d)', $url, $class, esc_html( $def[0] ), (int) $def[1] ); } return $views; } /** * Loads items for the current page. */ public function prepare_items() { global $wpdb; $table = LGBF_Links_Store::table(); $this->status_filter = isset( $_GET['link_status'] ) ? sanitize_key( wp_unslash( $_GET['link_status'] ) ) : 'problems'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( ! in_array( $this->status_filter, array( 'problems', 'broken', 'timeout', 'redirect', 'dismissed', 'ok' ), true ) ) { $this->status_filter = 'problems'; } if ( 'problems' === $this->status_filter ) { $where = "status IN ('broken', 'timeout', 'redirect')"; } else { $where = $wpdb->prepare( 'status = %s', $this->status_filter ); } $type_filter = isset( $_GET['link_type'] ) ? sanitize_key( wp_unslash( $_GET['link_type'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( in_array( $type_filter, array( 'link', 'image' ), true ) ) { $where .= $wpdb->prepare( ' AND link_type = %s', $type_filter ); } $search = isset( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( '' !== $search ) { $like = '%' . $wpdb->esc_like( $search ) . '%'; $where .= $wpdb->prepare( ' AND (url LIKE %s OR anchor_text LIKE %s)', $like, $like ); } $orderby_input = isset( $_GET['orderby'] ) ? sanitize_key( wp_unslash( $_GET['orderby'] ) ) : 'last_checked'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended $allowed = array( 'url', 'http_status', 'post_id', 'last_checked' ); $orderby = in_array( $orderby_input, $allowed, true ) ? $orderby_input : 'last_checked'; $order_input = isset( $_GET['order'] ) ? strtolower( sanitize_key( wp_unslash( $_GET['order'] ) ) ) : 'desc'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended $order = ( 'asc' === $order_input ) ? 'ASC' : 'DESC'; $per_page = $this->get_items_per_page( 'lgbf_links_per_page', 25 ); $current_page = $this->get_pagenum(); $offset = ( $current_page - 1 ) * $per_page; // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name and whitelisted clauses. $total_items = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$table} WHERE {$where}" ); $this->items = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$table} WHERE {$where} ORDER BY {$orderby} {$order}, id DESC LIMIT %d OFFSET %d", $per_page, $offset ), ARRAY_A ); // phpcs:enable $this->_column_headers = array( $this->get_columns(), array(), $this->get_sortable_columns() ); $this->set_pagination_args( array( 'total_items' => $total_items, 'per_page' => $per_page, 'total_pages' => (int) ceil( $total_items / $per_page ), ) ); } /** * Checkbox column. * * @param array $item Row. * @return string */ public function column_cb( $item ) { return sprintf( '', (int) $item['id'] ); } /** * URL column with row actions. * * @param array $item Row. * @return string */ public function column_url( $item ) { $url = esc_url( $item['url'] ); $html = sprintf( '%2$s', $url, esc_html( $item['url'] ) ); if ( 'image' === $item['link_type'] ) { $html .= ' ' . esc_html__( 'image', '404zen-broken-link-fixer' ) . ''; } $actions = array( 'edit' => sprintf( '%s', (int) $item['id'], esc_html__( 'Edit URL', '404zen-broken-link-fixer' ) ), 'unlink' => sprintf( '%s', (int) $item['id'], esc_html__( 'Unlink', '404zen-broken-link-fixer' ) ), 'dismiss' => sprintf( '%s', (int) $item['id'], esc_html__( 'Dismiss', '404zen-broken-link-fixer' ) ), 'recheck' => sprintf( '%s', (int) $item['id'], esc_html__( 'Recheck', '404zen-broken-link-fixer' ) ), ); if ( 'image' === $item['link_type'] ) { unset( $actions['unlink'] ); } // Third (and last) Pro touchpoint: offer auto-redirects on rows that need them. if ( in_array( $item['status'], array( 'broken', 'timeout' ), true ) ) { if ( lgbf_can_use_premium() && class_exists( 'LGBF_Redirects' ) ) { $redirect_url = add_query_arg( array( 'page' => 'lgbf-404zen-redirects', 'lgbf_source' => rawurlencode( $item['url'] ) ), admin_url( 'admin.php' ) ); $actions['autoredirect'] = sprintf( '%s', esc_url( $redirect_url ), esc_html__( 'Auto-redirect', '404zen-broken-link-fixer' ) ); } else { $actions['autoredirect'] = sprintf( '%s', esc_url( LGBF_Admin::upgrade_url() ), esc_html__( 'Auto-redirect (Pro)', '404zen-broken-link-fixer' ) ); } } $html .= '
'; return $html . $this->row_actions( $actions ); } /** * Status column. * * @param array $item Row. * @return string */ public function column_status( $item ) { $status = $item['status']; $code = (int) $item['http_status']; switch ( $status ) { case 'broken': $label = $code ? sprintf( 'HTTP %d', $code ) : __( 'Connection failed', '404zen-broken-link-fixer' ); $class = 'lgbf-badge-broken'; break; case 'timeout': $label = __( 'Timeout', '404zen-broken-link-fixer' ); $class = 'lgbf-badge-broken'; break; case 'redirect': /* translators: %d: number of redirect hops. */ $label = sprintf( __( 'Redirect chain (%d hops)', '404zen-broken-link-fixer' ), (int) $item['redirect_count'] ); $class = 'lgbf-badge-redirect'; break; case 'dismissed': $label = __( 'Dismissed', '404zen-broken-link-fixer' ); $class = 'lgbf-badge-muted'; break; case 'pending': $label = __( 'Queued', '404zen-broken-link-fixer' ); $class = 'lgbf-badge-muted'; break; default: $label = $code ? sprintf( 'HTTP %d', $code ) : __( 'OK', '404zen-broken-link-fixer' ); $class = 'lgbf-badge-ok'; } $html = '' . esc_html( $label ) . ''; if ( 'redirect' === $status && ! empty( $item['final_url'] ) ) { $html .= '