<?php /** * Suppress "error - 0 - No summary was found for this file" on phpdoc generation * * @package WPDataProjects\Parent_Child */ namespace WPDataProjects\Parent_Child { use WPDataAccess\Connection\WPDADB; use WPDataAccess\WPDA; use WPDataProjects\List_Table\WPDP_List_Table_Lookup; /** * Class WPDP_Child_List_Table extends WPDP_List_Table_Lookup * * @see WPDP_List_Table_Lookup * * @author Peter Schulz * @since 2.0.0 */ class WPDP_Child_List_Table extends WPDP_List_Table_Lookup { /** * Possible values: edit and view * * @var string */ protected $mode; /** * Parent key values * * @var array */ protected $parent; /** * Child relationships (for actual parent) * * @var array */ protected $child; /** * Tab value of actual child * * @var string */ protected $child_tab = ''; /** * Flexible usage of IN and NOT IN :-o * * @var string */ protected $where_in = 'in'; // Just to save code! (opposite using in WPDP_Child_List_Table_Selection: not in) /** * Indicates that this is a list table for a child * * Parent and stand-alone list tables do not have this property. * * @var bool */ public $is_child = true; /** * Overwrites WPDP_Child_List_Table constructor * * @param array $args */ public function __construct( $args = array() ) { if ( isset( $args['mode'] ) ) { $this->mode = $args['mode']; } else { wp_die( __( 'ERROR: Wrong arguments [missing mode]', 'wp-data-access' ) ); } if ( isset( $args['parent'] ) ) { $this->parent = $args['parent']; } else { wp_die( __( 'ERROR: Wrong arguments [missing parent]', 'wp-data-access' ) ); } if ( isset( $args['child'] ) ) { $this->child = $args['child']; } else { wp_die( __( 'ERROR: Wrong arguments [missing child]', 'wp-data-access' ) ); } $args['child_request'] = true; $args['allow_insert'] = 'off'; if ( 'view' === $this->mode ) { $args['allow_delete'] = 'off'; } else { $args['allow_delete'] = 'on'; } if ( 'edit' === $this->mode ) { $args['bulk_actions_enabled'] = true; } else { $args['bulk_actions_enabled'] = false; } if ( isset( $_REQUEST['child_tab'] ) ) { $this->child_tab = sanitize_text_field( wp_unslash( $_REQUEST['child_tab'] ) ); // input var okay. } $this->page_number_item_name = 'child_page_number'; parent::__construct( $args ); } /** * Overwrite method get_search_value to add parent arguments * * @param null $param_cookie_name * * @return string */ protected function get_search_value( $param_cookie_name = null ) { $parent_key_values = ''; foreach ( $this->parent['parent_key_value'] as $parent_key_value ) { $parent_key_values .= esc_attr( $parent_key_value ); } return parent::get_search_value( $this->page . '_search_child_' . $parent_key_values . str_replace( '.', '_', $this->table_name ) ); } /** * Overwrites method search_box to add parent arguments * * @param string $text * @param string $input_id */ public function search_box( $text, $input_id ) { parent::search_box( $text, $input_id ); $this->add_parent_args(); } /** * Add parent arguments as hidden items */ protected function add_parent_args() { foreach ( $this->parent['parent_key'] as $parent_key ) { ?> <input type='hidden' name='WPDA_PARENT_KEY*<?php echo( esc_attr( $parent_key ) ); ?>' value='<?php echo( esc_attr( $this->parent['parent_key_value'][ $parent_key ] ) ); ?>' /> <?php } ?> <input type="hidden" name="mode" value="<?php echo esc_attr( $this->mode ); ?>"> <input type='hidden' name='child_request' value='TRUE'/> <input type='hidden' name='child_tab' value='<?php echo esc_attr( $this->child_tab ); ?>'/> <?php if ( ! $this->has_items() ) { ?> <input type='hidden' name='action' value='-1'/> <input type='hidden' name='action2' value='-1'/> <input type='hidden' name='paged' value='1'/> <?php } } /** * Add parent arguments as string */ protected function add_parent_args_as_string( $item ) { $parent_args = ''; foreach ( $this->parent['parent_key'] as $parent_key ) { $p_key = esc_attr( $parent_key ); $p_val = esc_attr( $this->parent['parent_key_value'][ $parent_key ] ); $parent_args .= "<input type='hidden' name='WPDA_PARENT_KEY*$p_key' value='$p_val'/>"; } $mode = esc_attr( $this->mode ); $child_tab = esc_attr( $this->child_tab ); $parent_args .= "<input type='hidden' name='mode' value='$mode'>"; $parent_args .= "<input type='hidden' name='child_request' value='TRUE'/>"; $parent_args .= "<input type='hidden' name='child_tab' value='$child_tab'/>"; if ( ! $this->has_items() ) { $parent_args .= "<input type='hidden' name='action' value='-1'/>"; $parent_args .= "<input type='hidden' name='action2' value='-1'/>"; $parent_args .= "<input type='hidden' name='paged' value='1'/>"; } return $parent_args; } /** * Constructs the where clause depending on the relationship type * * Adds a subquery for n:m relationships. Adds parent key value comparison for 1:n relationships. */ protected function construct_where_clause() { $wpdadb = WPDADB::get_db_connection( $this->schema_name ); if ( null === $wpdadb ) { if ( is_admin() ) { wp_die( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $this->schema_name ) ) ); } else { die( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $this->schema_name ) ) ); } } if ( isset( $this->child['default_where'] ) && null !== $this->child['default_where'] && '' !== trim( $this->child['default_where'] ) ) { $default_where = " and ( {$this->child['default_where']} ) "; $default_where = WPDA::substitute_environment_vars( $default_where ); } else { $default_where = ''; } if ( isset( $this->child['relation_nm'] ) ) { $child_table = $this->child['relation_nm']['child_table']; $parent_key = $this->child['relation_nm']['parent_key']; $child_table_select = $this->child['relation_nm']['child_table_select']; $child_table_where = $this->child['relation_nm']['child_table_where']; $data_type = $this->child['relation_nm']['data_type']; $parent_key_column_names = ''; $select_column_names = ''; $index = 0; foreach ( $parent_key as $key ) { if ( $key === reset( $parent_key ) ) {//phpcs:ignore - 8.1 proof $parent_key_column_names .= "(`$key`"; $select_column_names .= '`' . $child_table_select[ $index ] . '`'; } else { $parent_key_column_names .= ",`$key`"; $select_column_names .= ', `' . $child_table_select[ $index ] . '`'; } $index ++; } $parent_key_column_names .= ')'; $index = 0; $where = ''; foreach ( $child_table_where as $child_where ) { if ( $child_where === reset( $child_table_where ) ) {//phpcs:ignore - 8.1 proof $and = ''; } else { $and = ' and '; } if ( 'number' === strtolower( $data_type[ $index ] ) ) { $where .= $wpdadb->prepare( " $and `$child_where` = %f ", $this->parent['parent_key_value'][ $this->parent['parent_key'][ $index ] ] ); // phpcs:ignore Standard.Category.SniffName.ErrorCode } else { $where .= $wpdadb->prepare( " $and `$child_where` = %s ", $this->parent['parent_key_value'][ $this->parent['parent_key'][ $index ] ] ); // phpcs:ignore Standard.Category.SniffName.ErrorCode } $index ++; } if ( '' === $this->schema_name ) { $schema_table_name = "`$child_table`"; } else { $schema_table_name = "`{$wpdadb->dbname}`.`$child_table`"; } $this->where = " where ($parent_key_column_names {$this->where_in} " . " (select $select_column_names from $schema_table_name where $where)) "; // phpcs:ignore Standard.Category.SniffName.ErrorCode } elseif ( isset( $this->child['relation_1n'] ) ) { $child_key = $this->child['relation_1n']['child_key']; $data_type = $this->child['relation_1n']['data_type']; if ( isset( $this->child['relation_1n']['parent_key'] ) && is_array( $this->child['relation_1n']['parent_key'] ) ) { $parent_key = $this->child['relation_1n']['parent_key']; } else { $parent_key = null; } $index = 0; $where = ''; foreach ( $child_key as $key ) { if ( $key === reset( $child_key ) ) {//phpcs:ignore - 8.1 proof $and = ''; } else { $and = ' and '; } if ( null === $parent_key ) { $parent_key_value = $this->parent['parent_key_value'][ $this->parent['parent_key'][ $index ] ]; } else { if ( $parent_key == $this->parent['parent_key'] ) { $parent_key_value = $this->parent['parent_key_value'][ $this->parent['parent_key'][ $index ] ]; } else { if ( isset( $_REQUEST[ 'WPDA_PARENT_KEY*' . $parent_key[ $index ] ] ) ) { $parent_key_value = sanitize_text_field( wp_unslash( $_REQUEST[ 'WPDA_PARENT_KEY*' . $parent_key[ $index ] ] ) ); // input var okay. } elseif ( isset( $_REQUEST[ $parent_key[ $index ] ] ) ) { $parent_key_value = sanitize_text_field( wp_unslash( $_REQUEST[ $parent_key[ $index ] ] ) ); // input var okay. } else { wp_die( '<p style="clear: both; padding: 10px;">' . __( 'ERROR: No value for parent key found', 'wp-data-access' ) . '</p>' ); } } } if ( isset( $data_type[ $index ] ) && 'number' === strtolower( $data_type[ $index ] ) ) { $where .= $wpdadb->prepare( " $and `$key` = %f ", $parent_key_value ); // phpcs:ignore Standard.Category.SniffName.ErrorCode } else { $where .= $wpdadb->prepare( " $and `$key` = %s ", $parent_key_value ); // phpcs:ignore Standard.Category.SniffName.ErrorCode } $index++; } $this->where = " where ($where) "; // phpcs:ignore Standard.Category.SniffName.ErrorCode } // Add default where $this->where .= $default_where; parent::construct_where_clause(); } protected function get_order_by() { $orderby = parent::get_order_by(); if ( '' !== $orderby ) { return $orderby; } if ( isset( $this->child['default_orderby'] ) && null !== $this->child['default_orderby'] && '' !== trim( $this->child['default_orderby'] ) ) { $default_orderby = " order by {$this->child['default_orderby']} "; } else { $default_orderby = ''; } // Add default order by return $default_orderby; // phpcs:ignore Standard.Category.SniffName.ErrorCode } /** * Overwrites method column_default_add_action * * Adds parent arguments * * @param array $item * @param string $column_name * @param array $actions */ protected function column_default_add_action( $item, $column_name, &$actions ) { $form_id = '_' . ( self::$list_number - 1 ); if ( isset( $this->child['relation_nm'] ) ) { $link_label = __( 'Delete Relationship', 'wp-data-access' ); $link_title = __( 'Delete child row (this only affects the relationship table)', 'wp-data-access' ); } else { $link_label = __( 'Delete', 'wp-data-access' ); $link_title = __( 'Delete child row', 'wp-data-access' ); } $warning = __( "You are about to permanently delete these items from your site.\\nThis action cannot be undone.\\n\\'Cancel\\' to stop, \\'OK\\' to delete.", 'wp-data-access' ); $actions['delete'] = sprintf( '<a href="javascript:void(0)" class="delete wpda_tooltip" title="%s" onclick="if (confirm(\'%s\')) jQuery(\'#%s\').submit()"> <span style="white-space:nowrap"> <i class="fas fa-trash wpda_icon_on_button"></i> %s </span> </a> ', $link_title, $warning, "delete_form$form_id", $link_label ); } /** * Overwrites method get_bulk_actions * * Adds 'Delete Relationship' action for n:m relationships and 'Delete' action for 1:n relationships to bulk * listbox. * * @return array|string */ public function get_bulk_actions() { if ( ! $this->bulk_actions_enabled ) { // Bulk actions disabled. return ''; } if ( empty( $this->wpda_list_columns->get_table_primary_key() ) ) { // Tables has no primary key: no bulk actions allowed! // Primary key is necessary to ensure uniqueness. return ''; } $actions = parent::get_bulk_actions(); if ( isset( $this->child['relation_nm'] ) ) { $actions['bulk-delete'] = __( 'Delete Relationship', 'wp-data-access' ); } else { $actions['bulk-delete'] = __( 'Delete', 'wp-data-access' ); } return $actions; } /** * Overwrite method delete_row * * Adds parent arguments * * @param string $where where clause * @param string $engine connect or empty * * @return mixed */ public function delete_row( $where, $engine = '' ) { // Expand named array with parent key for delete operation. $next_row_to_be_deleted = array(); $i = 0; foreach ( $where as $key => $value ) { if ( isset( $this->child['relation_nm'] ) ) { $next_row_to_be_deleted[ $this->child['relation_nm']['child_table_select'][ $i ] ] = $value; $next_row_to_be_deleted[ $this->child['relation_nm']['child_table_where'][ $i ] ] = $this->parent['parent_key_value'][ $this->parent['parent_key'][ $i ] ]; } else { $next_row_to_be_deleted[ $key ] = $value; } $i ++; } if ( isset( $this->child['relation_nm'] ) ) { $table_name = $this->child['relation_nm']['child_table']; } else { $table_name = $this->table_name; } $wpdadb = WPDADB::get_db_connection( $this->schema_name ); if ( null === $wpdadb ) { if ( is_admin() ) { wp_die( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $this->schema_name ) ) ); } else { die( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $this->schema_name ) ) ); } } $row_deleted = $wpdadb->delete( $table_name, $next_row_to_be_deleted ); // db call ok; no-cache ok. if ( 'connect' === strtolower( $engine ) ) { // Connect engine does not return number of rows deleted // Presuming delete was successful when no error message was returned return '' === $wpdadb->last_error; } return $row_deleted; } } }