<?php
if ( ! defined( 'ABSPATH' ) ) {
die( 'You are not allowed to call this page directly.' );
}
/**
* @since 5.3
*/
class FrmProApplicationsController {
/**
* @return void
*/
public static function load_assets_for_applications_index() {
$plugin_url = FrmProAppHelper::plugin_url();
$version = FrmProDb::$plug_version;
$js_dependencies = array( 'formidable_applications', 'popper', 'bootstrap_tooltip' );
wp_register_script( 'formidable_pro_applications', $plugin_url . '/js/admin/applications/applications.js', $js_dependencies, $version, true );
$can_add_views = FrmProApplicationsHelper::views_is_active_and_supports_applications();
$expected_views_folder = WP_PLUGIN_DIR . '/formidable-views/';
$views_exists = file_exists( $expected_views_folder . 'formidable-views.php' );
$views_is_up_to_date = $views_exists && file_exists( $expected_views_folder . 'classes/controllers/FrmViewsApplicationsController.php' );
$js_vars = array(
'allApplicationsUrl' => admin_url( 'edit-tags.php?taxonomy=frm_application' ),
'canAddViews' => $can_add_views,
'canAddApplications' => FrmProApplicationsHelper::current_user_can_edit_applications(),
'viewsIsUpdated' => $views_is_up_to_date,
'viewsExists' => $views_exists,
);
if ( ! $can_add_views ) {
$views_install_url = self::get_views_install_url();
if ( is_string( $views_install_url ) ) {
$js_vars['viewsInstallUrl'] = $views_install_url;
}
}
wp_localize_script( 'formidable_pro_applications', 'frmProApplicationsVars', $js_vars );
wp_enqueue_script( 'formidable_pro_applications' );
self::load_new_application_modal_assets();
wp_register_style( 'formidable_pro_applications', $plugin_url . '/css/admin/applications/applications.css', array(), $version );
wp_enqueue_style( 'formidable_pro_applications' );
}
/**
* @return string|false
*/
private static function get_views_install_url() {
$api = new FrmFormApi();
$addons = $api->get_api_info();
$visual_views_id = 28058856;
if ( ! is_array( $addons ) || ! array_key_exists( $visual_views_id, $addons ) || empty( $addons[ $visual_views_id ]['url'] ) ) {
return false;
}
return $addons[ $visual_views_id ]['url'];
}
/**
* @return void
*/
public static function load_new_application_modal_assets() {
self::register_common_js();
$plugin_url = FrmProAppHelper::plugin_url();
$version = FrmProDb::$plug_version;
$js_dependencies = array( 'formidable_dom', 'frm_applications_common' );
wp_register_script( 'formidable_pro_new_application_modal', $plugin_url . '/js/admin/applications/new_application_modal.js', $js_dependencies, $version, true );
$js_vars = array(
'canAddViews' => FrmProApplicationsHelper::views_is_active_and_supports_applications(),
'viewsUpgradeUrl' => FrmAppHelper::admin_upgrade_link(
array(
'medium' => 'application-views',
'content' => 'applications',
)
),
'importUrl' => admin_url( 'admin.php?page=formidable-import' ),
);
wp_localize_script( 'formidable_pro_new_application_modal', 'frmNewApplicationModalVars', $js_vars );
wp_enqueue_script( 'formidable_pro_new_application_modal' );
wp_register_style( 'formidable_pro_new_application_modal', $plugin_url . '/css/admin/applications/new_application_modal.css', array( 'frm_applications_common' ), $version );
wp_enqueue_style( 'formidable_pro_new_application_modal' );
}
/**
* @return void
*/
public static function register_common_js() {
$plugin_url = FrmProAppHelper::plugin_url();
$version = FrmProDb::$plug_version;
$js_dependencies = array( 'formidable_dom' );
wp_register_script( 'frm_applications_common', $plugin_url . '/js/admin/applications/common.js', $js_dependencies, $version, true );
$js_vars = array(
'proImagesUrl' => FrmProAppHelper::plugin_url() . '/images/',
'canEditForms' => current_user_can( 'frm_edit_forms' ),
'canEditViews' => current_user_can( 'frm_edit_displays' ),
'canEditPages' => current_user_can( 'edit_pages' ),
);
wp_localize_script( 'frm_applications_common', 'frmCommonApplicationVars', $js_vars );
}
/**
* @return void
*/
public static function register_common_css() {
$plugin_url = FrmProAppHelper::plugin_url();
$version = FrmProDb::$plug_version;
wp_register_style( 'frm_applications_common', $plugin_url . '/css/admin/applications/common.css', array(), $version );
}
/**
* Extend application data so icon (application thumbnail) and url (link to xml) are included in dashboard JavaScript data.
*
* @param array $keys
* @return array
*/
public static function application_data_keys( $keys ) {
array_push( $keys, 'icon', 'url' );
return $keys;
}
/**
* Add application meta on XML import / Template install.
* Also adds summary of import to the response to show in modal.
*
* @param array $response
* @param array $args {
* @type array $form
* @type array $imported
* }
* @return array
*/
public static function xml_response( $response, $args ) {
if ( empty( $args['imported'] ) || ! empty( $response['message'] ) ) {
return $response;
}
FrmProAppController::create_taxonomies();
$application_name = FrmAppHelper::get_post_param( 'application_name', '', 'sanitize_text_field' );
if ( $application_name ) {
// Create a new application with a posted application name.
$term = FrmProApplication::create( $application_name );
if ( ! is_array( $term ) ) {
return $response;
}
$term_id = $term['term_id'];
} else {
// Use an existing posted application id.
$application_id = FrmAppHelper::get_post_param( 'application_id', 0, 'absint' );
if ( ! $application_id ) {
return $response;
}
$term = get_term( $application_id, 'frm_application' );
if ( ! ( $term instanceof WP_Term ) ) {
return $response;
}
$term_id = $term->term_id;
}
$term_id = (int) $term_id;
$imported = $args['imported'];
$response['applicationSummary'] = array(
'applicationId' => $term_id,
'form' => array(),
'view' => array(),
'page' => array(),
);
if ( ! empty( $imported['forms'] ) ) {
$response['applicationSummary']['form'] = self::add_forms_to_application( $term_id, $imported['forms'] );
}
if ( ! empty( $imported['posts'] ) ) {
$response['applicationSummary'] = array_merge(
$response['applicationSummary'],
self::add_posts_to_application( $term_id, $imported['posts'] )
);
}
$response['redirect'] = FrmProApplicationsHelper::get_edit_url( $term_id );
return $response;
}
/**
* Add forms to application and return a summary of imported form names.
*
* @param int $term_id
* @param array<int> $form_ids
* @return array<array> Imported form details.
*/
private static function add_forms_to_application( $term_id, $form_ids ) {
$where = array(
'id' => $form_ids,
);
$form_results = FrmDb::get_results( 'frm_forms', $where, 'id, name' );
$form_names_by_id = wp_list_pluck( $form_results, 'name', 'id' );
$form_details = array();
foreach ( $form_ids as $form_id ) {
if ( ! array_key_exists( $form_id, $form_names_by_id ) ) {
continue;
}
FrmProApplication::add_form_to_application( $term_id, $form_id );
$form_details[] = array(
'id' => $form_id,
'name' => $form_names_by_id[ $form_id ],
);
}
return $form_details;
}
/**
* Add posts (views and pages) to application and return a summary of imported post names.
*
* @param int $term_id
* @param array<int> $post_ids
* @return array<array> Imported post names as two arrays (views and pages).
*/
private static function add_posts_to_application( $term_id, $post_ids ) {
$post_results = FrmDb::get_results(
'posts',
array(
'ID' => $post_ids,
'post_status !' => 'trash',
'post_type' => array( 'frm_display', 'page' ),
),
'ID, post_title, post_type'
);
$view_names_by_id = array();
$page_names_by_id = array();
foreach ( $post_results as $result ) {
if ( 'frm_display' === $result->post_type ) {
$view_names_by_id[ $result->ID ] = $result->post_title;
} elseif ( 'page' === $result->post_type ) {
$page_names_by_id[ $result->ID ] = $result->post_title;
}
}
$view_details = array();
$page_details = array();
foreach ( $post_ids as $post_id ) {
if ( isset( $view_names_by_id[ $post_id ] ) ) {
$view_details[] = array(
'id' => $post_id,
'name' => $view_names_by_id[ $post_id ],
);
} elseif ( isset( $page_names_by_id[ $post_id ] ) ) {
$page_details[] = array(
'id' => $post_id,
'name' => $page_names_by_id[ $post_id ],
);
} else {
// Continue to avoid adding a post id that doesn't match results.
// Styles and form actions get imported but should not get adding to taxonomy.
continue;
}
FrmProApplication::add_post_to_application( $term_id, $post_id );
}
return array(
'view' => $view_details,
'page' => $page_details,
);
}
/**
* Flag application pages as white pages (defines white background, some standard style rules).
*
* @param bool $is_white_page
* @return bool
*/
public static function is_white_page( $is_white_page ) {
if ( $is_white_page ) {
return true;
}
global $pagenow;
switch ( $pagenow ) {
case 'term.php':
case 'edit-tags.php':
return 'frm_application' === FrmAppHelper::simple_get( 'taxonomy' );
default:
return false;
}
}
/**
* Render the New Application and Import buttons after header title.
*
* @param string $context possible values include 'index' and 'edit'.
* @return void
*/
public static function header_after_title( $context ) {
if ( in_array( $context, array( 'index', 'list' ), true ) ) {
FrmAppHelper::include_svg();
if ( FrmProApplicationsHelper::current_user_can_edit_applications() ) {
FrmAppHelper::add_new_item_link(
array(
'class' => 'frm-new-application-button',
)
);
}
if ( 'list' === $context ) {
self::render_button( 'full-close' );
}
// Import button
} elseif ( 'edit' === $context ) {
self::render_button( 'full-close' );
self::render_button( 'settings' );
}
}
/**
* Maybe add an add button in page header title after span element.
*
* @param string $context possible values include 'index' and 'edit'.
* @return void
*/
public static function header_inside_title_after_span( $context ) {
if ( 'edit' === $context ) {
FrmAppHelper::add_new_item_link(
array(
'class' => 'frm-applications-add-item-button',
'button_text' => __( 'Add Item', 'formidable-pro' ),
)
);
}
}
/**
* Render a button view file in the application buttons directory.
*
* @param string $filename
* @return void
*/
public static function render_button( $filename ) {
require FrmProAppHelper::plugin_path() . '/classes/views/applications/buttons/' . $filename . '.php';
}
/**
* Add hooks before installing a form so the Application ID can be added to the new form.
*
* @return void
*/
public static function before_install_form() {
$application_id = FrmAppHelper::get_post_param( 'application_id', 0, 'absint' );
if ( ! $application_id ) {
// No application id set. Do not add hooks.
return;
}
add_action(
'frm_build_new_form',
function( $form_id ) use ( $application_id ) {
FrmProApplication::add_form_to_application( $application_id, $form_id );
}
);
}
/**
* @return void
*/
public static function get_application_item_options() {
FrmProApplicationsHelper::custom_application_permission_check();
$type = FrmAppHelper::simple_get( 'type' );
if ( ! $type ) {
wp_die();
}
$options = self::get_item_options_for_type( $type );
$data = compact( 'options' );
wp_send_json_success( $data );
}
/**
* @param string $type supports 'form', 'page', 'view'.
* @return array<string>
*/
private static function get_item_options_for_type( $type ) {
switch ( $type ) {
case 'form':
$table = 'frm_forms';
$where = array(
'status' => 'published',
'is_template' => 0,
);
$columns = array( 'id', 'name' );
break;
case 'page':
case 'view':
$post_type = 'view' === $type ? 'frm_display' : $type;
$table = 'posts';
$where = array(
'post_type' => $post_type,
'post_status' => array( 'private', 'publish' ),
);
$columns = array( 'ID', 'post_title' );
break;
default:
return array();
}
list( $id_column, $title_column ) = $columns;
$args = array( 'order_by' => $title_column . ' ASC' );
$results = FrmDb::get_results( $table, $where, implode( ',', $columns ), $args );
$output = array();
foreach ( $results as $result ) {
$output[] = array(
'value' => (int) $result->$id_column,
'label' => $result->$title_column,
);
}
return $output;
}
/**
* Get meta about an Application template via AJAX action.
*
* @return void
*/
public static function get_application_template_meta() {
FrmProApplicationsHelper::templates_permission_check();
check_ajax_referer( 'frm_ajax', 'nonce' );
$url = FrmAppHelper::get_param( 'xml', '', 'post', 'esc_url_raw' );
if ( ! $url ) {
die( 0 );
}
$response = wp_remote_get( $url ); // Note: If Query Monitor is active, I see 502 errors when trying to call wp_remote_get in Docker.
$body = wp_remote_retrieve_body( $response );
$xml = simplexml_load_string( $body );
if ( ! $xml ) {
wp_send_json_error( __( 'There was an error reading the form template', 'formidable' ) );
die();
}
if ( $xml instanceof SimpleXMLElement && isset( $xml->Code ) && in_array( (string) $xml->Code, array( 'NoSuchKey', 'AccessDenied' ), true ) ) { // phpcs:ignore WordPress.NamingConventions
wp_send_json_error( (string) $xml->Message ); // phpcs:ignore WordPress.NamingConventions
die();
}
$found = array(
'form' => array(),
'view' => array(),
'page' => array(),
);
if ( isset( $xml->form ) ) {
foreach ( $xml->form as $form ) {
if ( ! empty( $form->parent_form_id ) ) {
// Do not show repeater forms in the list of forms.
continue;
}
$found['form'][] = (string) $form->name;
}
}
if ( isset( $xml->view ) ) {
foreach ( $xml->view as $view ) {
if ( isset( $view->status ) && 'trash' === (string) $view->status ) {
// If template view happens to be trash, don't mention it.
continue;
}
if ( ! isset( $view->post_type ) || ! in_array( (string) $view->post_type, array( 'frm_display', 'page' ), true ) ) {
// Only include views, skip any false positives.
continue;
}
if ( 'frm_display' === (string) $view->post_type ) {
$found['view'][] = (string) $view->title;
} else {
$found['page'][] = (string) $view->title;
}
}
}
$data = compact( 'found' );
wp_send_json_success( $data );
die();
}
}