0,
'id' => 0,
'status' => '',
'per_page' => 0,
'page' => '',
),
$raw,
'entrystage'
);
$fixed = Query::shortcode_filters( $raw );
$atts['user_filter'] = $fixed['user'];
$atts['fixed'] = $fixed['fields'];
$form_id = absint( $atts['form'] ? $atts['form'] : $atts['id'] );
$form = $form_id ? \GFAPI::get_form( $form_id ) : false;
if ( ! $form || empty( $form['is_active'] ) || ! empty( $form['is_trash'] ) ) {
return '';
}
$settings = Settings::get( $form );
if ( '' !== trim( (string) $atts['page'] ) ) {
// page="…": form page of this list (page id, path or address of this site).
list( $page_id, $page_url ) = Settings::resolve_page( $atts['page'] );
if ( $page_id || '' !== $page_url ) {
$settings['page_id'] = $page_id;
$settings['page_url'] = $page_url;
}
}
if ( ! Access::can_see_list( $settings ) ) {
return is_user_logged_in() || empty( $settings['enabled'] ) ? '' : '
' . esc_html__( 'Please log in to see your entries.', 'entrystage' ) . '
';
}
Addon::get_instance()->enqueue_front_assets();
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only view.
$view_id = isset( $_GET['estg_view'], $_GET['estg_f'] ) && absint( $_GET['estg_f'] ) === $form_id ? absint( $_GET['estg_view'] ) : 0;
if ( $view_id ) {
$entry = \GFAPI::get_entry( $view_id );
if ( ! is_wp_error( $entry ) && (int) $entry['form_id'] === $form_id && Access::can( 'view', $entry, $settings ) ) {
return self::entry_view( $form, $entry, $settings );
}
}
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only view.
if ( ! empty( $settings['print'] ) && isset( $_GET['estg_print'] ) && absint( $_GET['estg_print'] ) === $form_id ) {
return self::print_html( $form, $settings, $atts );
}
return self::list_html( $form, $settings, $atts );
}
/**
* Personal space: several forms, one tab per form with its number of entries; the tab shows the
* list of that form as set in its settings. [entrystage forms="12,15" user="current"]
*
* @param array $raw Shortcode attributes.
* @return string
*/
private static function space( array $raw ) {
$ids = array_values( array_unique( array_filter( array_map( 'absint', preg_split( '/[\s,]+/', (string) $raw['forms'] ) ) ) ) );
unset( $raw['forms'], $raw['form'], $raw['id'], $raw['page'] );
$fixed = Query::shortcode_filters( $raw );
$tabs = array();
foreach ( array_slice( $ids, 0, 20 ) as $id ) {
$form = \GFAPI::get_form( $id );
if ( ! $form || empty( $form['is_active'] ) || ! empty( $form['is_trash'] ) ) {
continue;
}
$settings = Settings::get( $form );
if ( empty( $settings['enabled'] ) || ! Access::can_see_list( $settings ) ) {
continue;
}
// number of entries as the list opens (without the search of the visitor).
$state = Query::state(
$form,
$settings,
array(),
array(
'defaults' => true,
'user_filter' => $fixed['user'],
'fixed' => $fixed['fields'],
)
);
$count = Query::run( $form, $settings, array_merge( $state, array( 'page' => 1, 'per_page' => 1 ) ) );
$tabs[ $id ] = array(
'title' => (string) $form['title'],
'count' => (int) $count['total'],
);
}
if ( ! $tabs ) {
return is_user_logged_in() ? '' : '' . esc_html__( 'Please log in to see your entries.', 'entrystage' ) . '
';
}
// phpcs:disable WordPress.Security.NonceVerification.Recommended -- read-only view.
$active = isset( $_GET['estg_tab'] ) ? absint( $_GET['estg_tab'] ) : 0;
if ( ! isset( $tabs[ $active ] ) && isset( $_GET['estg_f'] ) ) {
$active = absint( $_GET['estg_f'] ); // back from an action or a page of a list.
}
// phpcs:enable WordPress.Security.NonceVerification.Recommended
if ( ! isset( $tabs[ $active ] ) ) {
$active = (int) array_key_first( $tabs );
}
/**
* Filters the tabs of a personal space (form id => title and count).
*
* @param array $tabs Tabs.
* @param int $active Form id of the tab shown.
*/
$tabs = (array) apply_filters( 'estg_space_tabs', $tabs, $active );
$h = '';
}
/** Most entries on one printed list. */
const PRINT_LIMIT = 500;
/**
* Printable list: the entries matching the filters, every page, without buttons. front.js opens the
* print dialog once the page is loaded.
*
* @param array $form Form object.
* @param array $settings Form settings.
* @param array $atts Shortcode attributes.
* @return string
*/
private static function print_html( $form, array $settings, array $atts ) {
$form_id = (int) $form['id'];
$layout = Settings::layout( $form, $settings );
$state = Query::state( $form, $settings, array(), $atts );
$back = Urls::list_url( $form_id, $state );
$state = array_merge(
$state,
array(
'page' => 1,
'per_page' => self::PRINT_LIMIT,
)
);
$result = Query::run( $form, $settings, $state );
$title = get_the_title();
$title = '' !== trim( wp_strip_all_tags( (string) $title ) ) ? $title : $form['title'];
$h = '';
}
$h .= '';
foreach ( $layout as $item ) {
$h .= '' . esc_html( $item['label'] ) . ' ';
}
$h .= ' ';
foreach ( $result['entries'] as $entry ) {
$h .= '';
foreach ( $layout as $item ) {
$class = 'estg-col-' . sanitize_html_class( $item['key'] );
if ( 'field' === $item['kind'] ) {
$h .= '' . Fields::render( $form, $entry, $item['column']['field'], $item['mode'], $settings ) . ' ';
} else {
$h .= '' . esc_html( \GFCommon::format_date( $entry['date_created'], false, '', false ) ) . ' ';
}
}
$h .= ' ';
}
return $h . '
';
}
/**
* The list.
*
* @param array $form Form object.
* @param array $settings Form settings.
* @param array $atts Shortcode attributes.
* @return string
*/
private static function list_html( $form, array $settings, array $atts ) {
$form_id = (int) $form['id'];
$layout = Settings::layout( $form, $settings );
$state = Query::state( $form, $settings, array(), $atts );
// the board (EntryStage Pro) fetches its entries column by column.
$result = empty( $state['board'] ) ? Query::run( $form, $settings, $state ) : array(
'entries' => array(),
'total' => 0,
);
$here = Urls::list_url( $form_id, $state );
$with_bulk = false;
$more = false;
ob_start();
echo '';
return (string) ob_get_clean();
}
/**
* One row.
*
* @param array $form Form object.
* @param array $entry Entry.
* @param array $settings Form settings.
* @param array $layout Columns in display order.
* @param bool $with_bulk Show the selection box.
* @param string $here Current list address.
* @param int $span Number of cells of a row when "More details" is used, 0 otherwise.
* @return string
*/
private static function row( $form, $entry, array $settings, array $layout, $with_bulk, $here, $span = 0 ) {
$id = (int) $entry['id'];
$classes = (array) apply_filters( 'estg_row_classes', array( 'estg-row' ), $entry, $settings );
$style = '';
$h = '';
foreach ( $layout as $item ) {
$class = 'estg-col-' . sanitize_html_class( $item['key'] );
if ( 'field' === $item['kind'] ) {
$h .= '' . Fields::render( $form, $entry, $item['column']['field'], $item['mode'], $settings ) . ' ';
} else {
$h .= '' . esc_html( \GFCommon::format_date( $entry['date_created'], false, '', false ) ) . ' ';
}
}
$acts = self::row_actions( $entry, $settings, $here );
$top = '';
$h .= '' . $top . '' . implode( ' ', $acts ) . '
';
return $h . ' ';
}
/**
* Action links of a row.
*
* @param array $entry Entry.
* @param array $settings Form settings.
* @param string $here Current list address.
* @return string[] Safe HTML.
*/
private static function row_actions( $entry, array $settings, $here ) {
$id = (int) $entry['id'];
$form_id = (int) $entry['form_id'];
$out = array();
if ( Access::can( 'view', $entry, $settings ) ) {
$url = add_query_arg(
array(
'estg_f' => $form_id,
'estg_view' => $id,
),
$here
);
$out[] = '' . self::act_inner( $settings, 'view' ) . ' ';
}
foreach ( array( 'edit', 'duplicate' ) as $action ) {
if ( Access::can( $action, $entry, $settings ) ) {
$url = Urls::form_url( $settings, $action, $id, $here );
if ( '' !== $url ) {
$out[] = '' . self::act_inner( $settings, $action ) . ' ';
}
}
}
if ( Access::can( 'archive', $entry, $settings ) ) {
$out[] = '' . self::act_inner( $settings, 'archive' ) . ' ';
}
/**
* Filters the action links of a row (each item is safe HTML).
*
* @param string[] $out Links.
* @param array $entry Entry.
* @param array $settings Form settings.
*/
return (array) apply_filters( 'estg_row_actions', $out, $entry, $settings );
}
/**
* Share of a column whose width is a percentage, for its data-estg-share attribute (read by front.js).
*
* @param string $width Clean width (Settings::clean_width()).
* @return string The share (for example "0.15"), or '' when the width is not a percentage.
*/
private static function share( $width ) {
return '%' === substr( $width, -1 ) ? (string) round( (float) $width / 100, 4 ) : '';
}
/**
* Width kept for the actions column when the table layout is fixed.
*
* @param array $settings Form settings.
* @return string CSS width.
*/
private static function actions_width( array $settings ) {
$count = 0;
$actions = array( 'view', 'edit', 'duplicate', 'archive' );
foreach ( $actions as $action ) {
$count += ! empty( $settings[ $action ] ) ? 1 : 0;
}
$count = max( 1, $count );
$per = array(
'icons' => 2.6,
'both' => 7.5,
'text' => 6,
);
$em = $count * ( isset( $per[ $settings['action_display'] ] ) ? $per[ $settings['action_display'] ] : 2.6 ) + 2; // + cell padding (1.4em).
return max( 6, $em ) . 'em';
}
/**
* Tooltip and accessible name of an action button (icons only).
*
* @param array $settings Form settings.
* @param string $action Action.
* @return string
*/
private static function act_attrs( array $settings, $action ) {
return 'icons' === $settings['action_display'] ? ' title="' . esc_attr( $settings[ $action . '_label' ] ) . '"' : '';
}
/**
* Content of an action button: icon, text or both, as set for the form.
*
* @param array $settings Form settings.
* @param string $action Action.
* @return string
*/
private static function act_inner( array $settings, $action ) {
$display = $settings['action_display'];
$icon = 'text' !== $display ? self::action_icon( $settings, $action ) : '';
$class = 'icons' === $display ? 'screen-reader-text' : 'estg-act-text';
return $icon . '' . esc_html( $settings[ $action . '_label' ] ) . ' ';
}
/**
* Icon of an action button: the picture chosen in the settings, or the built-in icon.
*
* @param array $settings Form settings.
* @param string $action view | edit | duplicate | archive | pdf | next.
* @return string
*/
private static function action_icon( array $settings, $action ) {
return self::icon( $action );
}
/**
* Inline SVG icon (drawn with the text colour).
*
* @param string $name view | edit | duplicate | archive | pdf | next | export | download | file | chevron.
* @return string
*/
public static function icon( $name ) {
$paths = array(
'view' => ' ',
'edit' => ' ',
'duplicate' => ' ',
'archive' => ' ',
'pdf' => ' ',
'next' => ' ',
'export' => ' ',
'download' => ' ',
'file' => ' ',
'chevron' => ' ',
'search' => ' ',
'print' => ' ',
'list' => ' ',
'board' => ' ',
);
/**
* Filters the SVG content of an icon (the inner elements of a 24x24 ).
*
* @param string $svg SVG elements.
* @param string $name Icon name.
*/
$inner = (string) apply_filters( 'estg_icon', isset( $paths[ $name ] ) ? $paths[ $name ] : '', $name );
if ( '' === $inner ) {
return '';
}
return '' . $inner . ' ';
}
/**
* Column header with sort link.
*
* @param int $form_id Form id.
* @param array $state List state.
* @param string $key Sort key.
* @param string $label Label.
* @return string
*/
private static function sort_link( $form_id, array $state, $key, $label ) {
$active = (string) $state['orderby'] === (string) $key;
$dir = $active && 'ASC' === $state['dir'] ? 'DESC' : 'ASC';
$url = Urls::list_url(
$form_id,
$state,
array(
'estg_o' => $key,
'estg_d' => $dir,
'estg_p' => null,
)
);
$arrow = $active ? ( 'ASC' === $state['dir'] ? ' ↑' : ' ↓' ) : '';
return '' . esc_html( $label ) . $arrow . ' ';
}
/**
* Search, filters, page size and export.
*
* @param array $form Form object.
* @param array $settings Form settings.
* @param array $state List state.
* @param array $layout Columns (Settings::layout()).
* @return string
*/
private static function toolbar( $form, array $settings, array $state, array $layout ) {
$form_id = (int) $form['id'];
$has_tools = ! empty( $settings['search'] );
$export = false;
$print = ! empty( $settings['print'] );
$switch = false;
if ( ! $has_tools && ! $export && ! $print && ! $switch ) {
return '';
}
$uid = 'estg-' . $form_id;
$active = '' !== (string) $state['search'];
$h = '';
}
/**
* Rows per page, next to the pagination. Changing it reloads the list (front.js); the
* button is there for browsers without JavaScript.
*
* @param int $form_id Form id.
* @param array $state List state.
* @param array $settings Form settings.
* @return string
*/
private static function per_page( $form_id, array $state, array $settings ) {
if ( count( $settings['per_page_choices'] ) < 2 ) {
return '';
}
$args = array();
wp_parse_str( (string) wp_parse_url( Urls::list_url( $form_id, array_merge( $state, array( 'page' => 1 ) ), array( 'estg_pp' => null ) ), PHP_URL_QUERY ), $args );
$h = '';
return $h;
}
/**
* Pagination.
*
* @param int $form_id Form id.
* @param array $state List state.
* @param int $total Total entries.
* @param array $settings Form settings.
* @return string
*/
private static function pagination( $form_id, array $state, $total, array $settings ) {
$pages = (int) ceil( $total / max( 1, $state['per_page'] ) );
$h = '';
}
/**
* Message after an action (from the redirect).
*
* @param int $form_id Form id.
* @return string
*/
private static function notice( $form_id ) {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- display only.
if ( ! isset( $_GET['estg_msg'], $_GET['estg_f'] ) || absint( $_GET['estg_f'] ) !== (int) $form_id ) {
return '';
}
$code = sanitize_key( wp_unslash( $_GET['estg_msg'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$n = isset( $_GET['estg_n'] ) ? absint( $_GET['estg_n'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$messages = array(
/* translators: %d: number of entries */
'archived' => _n( '%d entry archived.', '%d entries archived.', $n, 'entrystage' ),
/* translators: %d: number of entries */
'duplicated' => _n( '%d entry duplicated.', '%d entries duplicated.', $n, 'entrystage' ),
/* translators: %d: number of entries */
'updated' => __( 'Entry updated.', 'entrystage' ),
'denied' => __( 'This action is not allowed.', 'entrystage' ),
'none' => __( 'No entry selected.', 'entrystage' ),
);
if ( ! isset( $messages[ $code ] ) ) {
return '';
}
$error = in_array( $code, array( 'denied', 'none' ), true );
return '' . esc_html( sprintf( $messages[ $code ], $n ) ) . '
';
}
/**
* Read-only view of an entry.
*
* @param array $form Form object.
* @param array $entry Entry.
* @param array $settings Form settings.
* @return string
*/
private static function entry_view( $form, $entry, array $settings ) {
$back = remove_query_arg( array( 'estg_view' ), Urls::list_url( (int) $form['id'], Query::state( $form, $settings ) ) );
$skip = array( 'html', 'section', 'page', 'captcha', 'password' );
$h = '← ' . esc_html__( 'Back to the list', 'entrystage' ) . '
';
$h .= '
' . esc_html( sprintf( /* translators: %d: entry id */ __( 'Entry #%d', 'entrystage' ), (int) $entry['id'] ) ) . ' ';
$page = array();
if ( $page ) {
// the fields and section titles chosen in the settings, in their order; empty values are left out,
// and so is a section title followed by no value.
$open = false;
$pending = '';
foreach ( $page as $item ) {
if ( 'section' === $item['type'] ) {
$pending = '
' . esc_html( $item['label'] ) . ' ';
continue;
}
$html = Fields::render( $form, $entry, $item['field'], $item['mode'], $settings );
if ( '' === trim( wp_strip_all_tags( (string) $html, true ) ) && false === strpos( (string) $html, '
' : '' ) . $pending;
$open = false;
$pending = '';
}
if ( ! $open ) {
$h .= '
';
$open = true;
}
$h .= '' . esc_html( $item['label'] ) . ' ' . wp_kses_post( $html ) . ' ';
}
$h .= $open ? ' ' : '';
} else {
$h .= '
';
foreach ( (array) $form['fields'] as $field ) {
if ( in_array( $field->type, $skip, true ) || ! empty( rgobj( $field, 'adminOnly' ) ) || in_array( rgobj( $field, 'visibility' ), array( 'hidden', 'administrative' ), true ) ) {
continue;
}
$value = \GFFormsModel::get_lead_field_value( $entry, $field );
$html = Fields::display( $field, $value, $entry, 'html' );
if ( '' === trim( wp_strip_all_tags( (string) $html ) ) ) {
continue;
}
$h .= '' . esc_html( wp_strip_all_tags( \GFCommon::get_label( $field ) ) ) . ' ' . wp_kses_post( $html ) . ' ';
}
$h .= ' ';
}
$h .= '
';
return (string) apply_filters( 'estg_entry_view_html', $h, $entry, $form, $settings );
}
}