core = $core; // Initialize layout and column definitions, $this->setup_columns(); $this->setup_layouts(); // Figure out what the "safe" URL to acccess the current page would be. // This is used by the bulk action form. $special_args = array( '_wpnonce', '_wp_http_referer', 'action', 'selected_links' ); $this->neutral_current_url = remove_query_arg( $special_args ); } /** * Print the entire link table and associated navigation elements. * * @param array $current_filter * @param string $layout * @param array $visible_columns * @param bool $compact * @return void */ function print_table( $current_filter, $layout = 'flexible', $visible_columns = null, $compact = false ) { $this->current_filter = $current_filter; $this->page = $current_filter['page']; $this->per_page = $current_filter['per_page']; $current_layout = $this->layouts[ $layout ]; if ( empty( $visible_columns ) ) { $visible_columns = $current_layout; } // Only allow columns actually present in this layout. $visible_columns = array_intersect( $visible_columns, $current_layout ); echo '
'; wp_nonce_field( 'bulk-action' ); // Top navigation. $this->prepare_nav_html(); $this->navigation( $compact ); // Table header. $table_classes = array( 'widefat' ); if ( $compact ) { $table_classes[] = 'compact'; }; if ( $this->core->conf->options['table_color_code_status'] ) { $table_classes[] = 'color-code-link-status'; }; $table_classes[] = 'base-filter-' . esc_html( $current_filter['base_filter'] ); printf( '', implode( ' ', $table_classes ) ); // The select-all checkbox. echo ''; // Column headers. foreach ( $current_layout as $column_id ) { $column = $this->columns[ $column_id ]; $column_classes = array( 'column-' . $column_id ); if ( isset( $column['class'] ) ) { $column_classes[] = $column['class']; } if ( ! in_array( $column_id, $visible_columns ) ) { $column_classes[] = 'hidden'; } $heading = $column['heading']; if ( isset( $column['sortable'] ) && $column['sortable'] ) { $orderby = $column['orderby']; $current_orderby = isset( $_GET['orderby'] ) ? $_GET['orderby'] : ''; $current_order = isset( $_GET['order'] ) ? $_GET['order'] : 'asc'; if ( ! in_array( $current_order, array( 'asc', 'desc' ) ) ) { $current_order = 'asc'; } if ( $orderby == $current_orderby ) { $column_classes[] = 'sorted'; $column_classes[] = $current_order; $order = ( 'asc' == $current_order ) ? 'desc' : 'asc'; //Reverse the sort direction } else { $order = 'asc'; $column_classes[] = 'desc'; $column_classes[] = 'sortable'; } $heading = sprintf( '%s', esc_attr( add_query_arg( array( 'orderby' => $orderby, 'order' => $order, ) ) ), $heading ); } printf( '', implode( ' ', $column_classes ), isset( $column['id'] ) ? ' id="' . $column['id'] . '"' : '', $heading ); } echo ''; // Table body. echo ''; $this->bulk_edit_form( $visible_columns ); $rownum = 0; foreach ( $this->current_filter['links'] as $link ) { $rownum++; $this->link_row( $link, $current_layout, $visible_columns, $rownum ); $this->link_details_row( $link, $visible_columns, $rownum ); } echo ''; // Bottom navigation. $this->navigation( $compact, '2' ); echo '
'; // Inline editor (hidden by default, JS will move it to the right place). $this->inline_editor( $visible_columns ); } /** * Print the "Bulk Actions" dropdown and navigation links * * @param bool $table_compact Whether to use the full or compact view. * @param string $suffix Optional. Appended to ID and name attributes of the bulk action dropdown. * @return void */ function navigation( $table_compact = false, $suffix = '' ) { // Display the "Bulk Actions" dropdown. echo '
'; // Display pagination links. if ( ! empty( $this->pagination_html ) ) { echo $this->pagination_html; } // Display the view switch (only in the top nav. area). if ( empty( $suffix ) ) { ?>
'; } /** * Initialize the internal list of available table columns. * * @return void */ function setup_columns() { $this->columns = array( 'status' => array( 'heading' => __( 'Status', 'broken-link-checker' ), 'content' => array( $this, 'column_status' ), ), 'new-url' => array( 'heading' => __( 'URL', 'broken-link-checker' ), 'content' => array( $this, 'column_new_url' ), 'sortable' => true, 'orderby' => 'url', ), 'used-in' => array( 'heading' => __( 'Source', 'broken-link-checker' ), 'class' => 'column-title', 'content' => array( $this, 'column_used_in' ), ), 'new-link-text' => array( 'heading' => __( 'Link Text', 'broken-link-checker' ), 'content' => array( $this, 'column_new_link_text' ), 'sortable' => true, 'orderby' => 'link_text', ), 'redirect-url' => array( 'heading' => __( 'Redirect URL', 'broken-link-checker' ), 'content' => array( $this, 'column_redirect_url' ), 'sortable' => true, 'orderby' => 'redirect_url', ), ); } /** * Initialize the list of available layouts * * @return void */ function setup_layouts() { $this->layouts = array( 'classic' => array( 'used-in', 'new-link-text', 'new-url' ), 'flexible' => array( 'new-url', 'status', 'new-link-text', 'redirect-url', 'used-in' ), ); } /** * Get a list of columns available in a specific table layout. * * @param string $layout Layout ID. * @return array Associative array of column data indexed by column ID. */ function get_layout_columns( $layout ) { if ( isset( $this->layouts[ $layout ] ) ) { $result = array(); foreach ( $this->layouts[ $layout ] as $column_id ) { if ( isset( $this->columns[ $column_id ] ) ) { $result[ $column_id ] = $this->columns[ $column_id ]; } } return $result; } else { return null; } } /** * Pre-generate some HTML fragments used for both the top and bottom navigation/bulk action boxes. * * @return void */ function prepare_nav_html() { //Generate an ', $value, $name ); } $this->bulk_actions_html = $bulk_actions_html; // Pagination links can also be pre-generated. // WP has a built-in function for pagination :). $page_links = paginate_links( array( 'base' => add_query_arg( 'paged', '%#%' ), 'format' => '', 'prev_text' => __( '«' ), 'next_text' => __( '»' ), 'total' => $this->current_filter['max_pages'], 'current' => $this->page, ) ); if ( $page_links ) { $this->pagination_html = '
'; $this->pagination_html .= sprintf( '' . __( 'Displaying %1$s–%2$s of %3$s', 'broken-link-checker' ) . '%4$s', number_format_i18n( ( $this->page - 1 ) * $this->per_page + 1 ), number_format_i18n( min( $this->page * $this->per_page, $this->current_filter['count'] ) ), number_format_i18n( $this->current_filter['count'] ), $page_links ); $this->pagination_html .= '
'; } else { $this->pagination_html = ''; } } /** * Print the bulk edit form. * * @param array $visible_columns List of visible columns. * @return void */ function bulk_edit_form( $visible_columns ) { ?>

 

core->is_excluded( $link->url ); if ( $excluded ) { $rowclass .= ' blc-excluded-link'; } if ( $link->redirect_count > 0 ) { $rowclass .= ' blc-redirect'; } $days_broken = 0; if ( $link->broken ) { // Add a highlight to broken links that appear to be permanently broken. $days_broken = intval( ( time() - $link->first_failure ) / ( 3600 * 24 ) ); if ( $days_broken >= $this->core->conf->options['failure_duration_threshold'] ) { $rowclass .= ' blc-permanently-broken'; if ( $this->core->conf->options['highlight_permanent_failures'] ) { $rowclass .= ' blc-permanently-broken-hl'; } } } $status = $link->analyse_status(); $rowclass .= ' link-status-' . $status['code']; // Retrieve link instances to display in the table. $instances = $link->get_instances(); if ( ! empty( $instances ) ) { // Put instances that match the selected link type at the top. Makes search results look better. if ( ! empty( $this->current_filter['search_params']['s_link_type'] ) ) { $s_link_type = $this->current_filter['search_params']['s_link_type']; } else { $s_link_type = ''; } $instances = $this->sort_instances_for_display( $instances, $s_link_type ); } // For inline editing, we'll need to know if any instances have editable link text, and what it is. $can_edit_text = false; $can_edit_url = false; $editable_link_texts = array(); $non_editable_link_texts = array(); foreach ( $instances as $instance ) { if ( $instance->is_link_text_editable() ) { $can_edit_text = true; $editable_link_texts[ $instance->link_text ] = true; } else { $non_editable_link_texts[ $instance->link_text ] = true; } if ( $instance->is_url_editable() ) { $can_edit_url = true; } } $link_texts = $can_edit_text ? $editable_link_texts : $non_editable_link_texts; $data_link_text = ''; if ( 1 === count( $link_texts ) ) { // All instances have the same text - use it. $link_text = key( $link_texts ); $data_link_text = ' data-link-text="' . esc_attr( $link_text ) . '"'; } printf( '', $link->link_id, $rowclass, $days_broken, $can_edit_url ? 1 : 0, $can_edit_text ? 1 : 0, $data_link_text ); // The checkbox used to select links is automatically printed in all layouts // and can't be disabled. Without it, bulk actions wouldn't work. $this->column_checkbox( $link ); foreach ( $layout as $column_id ) { $column = $this->columns[ $column_id ]; printf( '', $column_id, in_array( $column_id, $visible_columns ) ? '' : ' hidden' ); if ( isset( $column['content'] ) ) { if ( is_callable( $column['content'] ) ) { call_user_func( $column['content'], $link, $instances ); } else { echo $column['content']; } } else { echo '[', $column_id, ']'; } echo ''; } echo ''; } /** * Print the details row for a specific link. * * @uses blcTablePrinter::details_row_contents() * * @param blcLink $link The link to display. * @param array $visible_columns List of visible columns. * @param integer $rownum Table row number. * @return void */ function link_details_row( $link, $visible_columns, $rownum = 0 ) { printf( '', $link->link_id, count( $visible_columns ) + 1 ); $this->details_row_contents( $link ); echo ''; } /** * Print the contents of the details row for a specific link. * * @param blcLink $link * @return void */ public static function details_row_contents( $link ) { ?>
    post_date ) ) { ?>
  1. :
  2. : last_check; if ( $last_check < strtotime( '-10 years' ) ) { _e( 'Never', 'broken-link-checker' ); } else { printf( '', esc_attr( date( 'c', $last_check ) ), //phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date date_i18n( get_option( 'date_format' ), $last_check ) ); } ?>
  3. : http_code; ?>
  4. : request_duration ); ?>
  5. : final_url ); ?>
  6. : redirect_count; ?>
  7. : get_instances() ); ?>
  8. broken || $link->warning ) && ( intval( $link->check_count ) > 0 ) ) { ?>

  9. check_count, 'broken-link-checker' ), $link->check_count ); echo '
    '; $delta = time() - $link->first_failure; printf( __( 'This link has been broken for %s.', 'broken-link-checker' ), blcUtility::fuzzy_delta( $delta ) ); ?>
  1. : log ); ?>
', esc_attr( __( 'Show more info about this link', 'broken-link-checker' ) ) ); $status = $link->analyse_status(); printf( ' %s %s ', $status['code'], empty( $link->http_code ) ? '' : $link->http_code, $status['text'] ); // Last checked... if ( 0 != $link->last_check ) { $last_check = _x( 'Checked', 'checked how long ago', 'broken-link-checker' ) . ' '; $last_check .= blcUtility::fuzzy_delta( time() - $link->last_check, 'ago' ); printf( '%s', $last_check ); } // Broken for... if ( $link->broken ) { $delta = time() - $link->first_failure; $broken_for = blcUtility::fuzzy_delta( $delta ); printf( '%s %s', __( 'Broken for', 'broken-link-checker' ), $broken_for ); } echo ''; // "Details" link. echo '
'; printf( '%s', esc_attr( __( 'Show more info about this link', 'broken-link-checker' ) ), _x( 'Details', 'link in the "Status" column', 'broken-link-checker' ) ); echo '
'; } /** * @param blcLink $link */ function column_new_url( $link ) { ?> url ); ?> " . __( 'Edit URL', 'broken-link-checker' ) . ''; $actions['delete'] = "" . __( 'Unlink', 'broken-link-checker' ) . ''; if ( $link->broken || $link->warning ) { $actions['blc-discard-action'] = sprintf( '%s', esc_attr( __( 'Remove this link from the list of broken links and mark it as valid', 'broken-link-checker' ) ), __( 'Not broken', 'broken-link-checker' ) ); } if ( ! $link->dismissed && ( $link->broken || $link->warning || ( $link->redirect_count > 0 ) ) ) { $actions['blc-dismiss-action'] = sprintf( '%s', esc_attr( __( 'Hide this link and do not report it again unless its status changes', 'broken-link-checker' ) ), __( 'Dismiss', 'broken-link-checker' ) ); } elseif ( $link->dismissed ) { $actions['blc-undismiss-action'] = sprintf( '%s', esc_attr( __( 'Undismiss this link', 'broken-link-checker' ) ), __( 'Undismiss', 'broken-link-checker' ) ); } $actions['blc-recheck-action'] = sprintf( '%s', __( 'Recheck', 'broken-link-checker' ) ); if ( $link->redirect_count > 0 && ! empty( $link->final_url ) && ( $link->url != $link->final_url ) ) { //TODO: Check if at least one instance has an editable URL. Otherwise this won't work. $actions['blc-deredirect-action'] = sprintf( '%s', __( 'Replace this redirect with a direct link', 'broken-link-checker' ), _x( 'Fix redirect', 'link action; replace one redirect with a direct link', 'broken-link-checker' ) ); } // Only show the enabled actions. $conf = blc_get_configuration(); foreach ( $conf->get( 'show_link_actions', $actions ) as $name => $enabled ) { if ( ! $enabled ) { unset( $actions[ $name ] ); } } // Wrap actions with and separate them with | characters. // Basically, this emulates the HTML structure that WP uses for post actions under Posts -> All Posts. $spans = array(); $is_first_action = true; foreach ( $actions as $name => $html ) { $spans[] = sprintf( '%s%s', esc_attr( $name ), $is_first_action ? '' : ' | ', $html ); $is_first_action = false; } echo '
'; echo implode( '', $spans ); echo '
'; ?>
', $link->link_id, ''; if ( ! empty( $instances ) ) { /** @var $instance blcLinkInstance */ $instance = reset( $instances ); echo $instance->ui_get_source(); $actions = $instance->ui_get_action_links(); echo '
'; echo implode( ' | ', $actions ); echo '
'; } else { _e( '[An orphaned link! This is a bug.]', 'broken-link-checker' ); } } /** * @param blcLink $link * @param blcLinkInstance[] $instances */ function column_new_link_text( $link, $instances ) { if ( empty( $instances ) ) { echo 'N/A'; } else { $instance = reset( $instances ); /** @var blcLinkInstance $instance */ echo $instance->ui_get_link_text(); } } function column_redirect_url( $link, $instances ) { if ( $link->redirect_count > 0 ) { printf( '%2$s', esc_attr( $link->final_url ), esc_html( $link->final_url ) ); } } /** * Sort a list of link instances to be displayed in the "Broken Links" page. * * Groups instances by container type and, if $search_link_type is specified, * puts instances that have a matching container type or parser type at the * beginning. * * @param array $instances An array of blcLinkInstance objects. * @param string $searched_link_type Optional. The required container/parser type. * @return array Sorted array. */ function sort_instances_for_display( $instances, $searched_link_type = '' ) { $this->searched_link_type = $searched_link_type; usort( $instances, array( $this, 'compare_link_instances' ) ); return $instances; } /** * Callback function for sorting link instances. * * @see blcTablePrinter::sort_instances_for_display() * * @param blcLinkInstance $a * @param blcLinkInstance $b * @return int */ function compare_link_instances( $a, $b ) { if ( ! empty( $this->searched_link_type ) ) { if ( ( $a->container_type == $this->searched_link_type ) || ( $a->parser_type == $this->searched_link_type ) ) { if ( ( $b->container_type == $this->searched_link_type ) || ( $b->parser_type == $this->searched_link_type ) ) { return 0; } else { return -1; } } else { if ( ( $b->container_type == $this->searched_link_type ) || ( $b->parser_type == $this->searched_link_type ) ) { return 1; } } } return strcmp( $a->container_type, $b->container_type ); } protected function inline_editor( $visible_columns ) { ?>