asset_ver( 'assets/css/flipset-admin-ui.css' )
);
wp_enqueue_style(
'flipset-admin',
FLIPSET_URL . 'assets/css/flipset-admin.css',
array( 'flipset-admin-ui' ),
$this->asset_ver( 'assets/css/flipset-admin.css' )
);
if ( $is_dashboard ) {
wp_enqueue_style(
'flipset-dashboard',
FLIPSET_URL . 'assets/css/flipset-dashboard.css',
array( 'flipset-admin-ui' ),
$this->asset_ver( 'assets/css/flipset-dashboard.css' )
);
}
if ( $is_settings ) {
wp_enqueue_style(
'flipset-settings',
FLIPSET_URL . 'assets/css/flipset-settings.css',
array( 'flipset-admin-ui' ),
$this->asset_ver( 'assets/css/flipset-settings.css' )
);
}
if ( $is_docs ) {
wp_enqueue_style(
'flipset-docs',
FLIPSET_URL . 'assets/css/flipset-docs.css',
array( 'flipset-admin-ui' ),
$this->asset_ver( 'assets/css/flipset-docs.css' )
);
}
if ( $is_settings || $is_builder ) {
wp_enqueue_style(
'flipset-viewer',
FLIPSET_URL . 'assets/css/flipset-viewer.css',
array(),
$this->asset_ver( 'assets/css/flipset-viewer.css' )
);
Flipset_Viewer::register_pdfjs();
wp_enqueue_script( 'flipset-pdfjs' );
wp_enqueue_script(
'flipset-viewer',
FLIPSET_URL . 'assets/js/flipset-viewer.js',
array( 'flipset-pdfjs' ),
$this->asset_ver( 'assets/js/flipset-viewer.js' ),
true
);
wp_localize_script(
'flipset-viewer',
'FlipsetConfig',
array(
'workerSrc' => FLIPSET_URL . 'assets/vendor/pdfjs/pdf.worker.min.js',
'isPro' => Flipset_Loader::is_pro_active(),
'i18n' => Flipset_Viewer::viewer_i18n(),
)
);
}
if ( $is_docs && current_user_can( 'upload_files' ) ) {
Flipset_Viewer::register_pdfjs();
wp_enqueue_script( 'flipset-pdfjs' );
wp_enqueue_script(
'flipset-thumbnail-gen',
FLIPSET_URL . 'assets/js/flipset-thumbnail-gen.js',
array( 'flipset-pdfjs' ),
$this->asset_ver( 'assets/js/flipset-thumbnail-gen.js' ),
true
);
wp_localize_script(
'flipset-thumbnail-gen',
'FlipsetThumbGen',
array(
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'flipset_admin' ),
'workerSrc' => FLIPSET_URL . 'assets/vendor/pdfjs/pdf.worker.min.js',
'maxPages' => 40,
'maxChars' => 80000,
'i18n' => array(
'indexed' => __( 'Indexed', 'flipset' ),
'noText' => __( 'No text', 'flipset' ),
'tagged' => __( 'Tagged', 'flipset' ),
'untagged' => __( 'Untagged', 'flipset' ),
'untaggedHint' => __( 'This PDF has no accessibility tags. Export it from Word, InDesign, or Acrobat as a tagged PDF so screen readers can follow headings and reading order.', 'flipset' ),
'taggedHint' => __( 'Tagged PDF. Screen readers can follow its structure.', 'flipset' ),
'indexedHint' => __( 'Text extracted. WordPress search can find words in this PDF.', 'flipset' ),
'noTextHint' => __( 'No selectable text was found (a scanned image PDF), so it cannot be searched.', 'flipset' ),
),
)
);
}
if ( $is_dashboard || $is_docs || $is_builder ) {
wp_enqueue_media();
}
wp_enqueue_script(
'flipset-admin',
FLIPSET_URL . 'assets/js/flipset-admin.js',
array(),
$this->asset_ver( 'assets/js/flipset-admin.js' ),
true
);
wp_localize_script(
'flipset-admin',
'FlipsetAdmin',
array(
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'flipset_admin' ),
'i18n' => array(
'selectPdf' => __( 'Select PDF', 'flipset' ),
'usePdf' => __( 'Use this PDF', 'flipset' ),
'copied' => __( 'Copied!', 'flipset' ),
'copiedToast' => __( 'Shortcode copied', 'flipset' ),
'codeCopied' => __( 'Code snippet copied to clipboard', 'flipset' ),
'dismissed' => __( 'Dismissed', 'flipset' ),
/* translators: 1: completed steps, 2: total steps. */
'stepsComplete' => __( '%1$s of %2$s steps complete', 'flipset' ),
'setupDone' => __( 'You are set. New documents go in Documents or the Media Library.', 'flipset' ),
'dropHint' => __( 'Drop a PDF to embed it', 'flipset' ),
'uploading' => __( 'Uploading…', 'flipset' ),
'creating' => __( 'Creating page…', 'flipset' ),
'testPageReady' => __( 'Test page ready', 'flipset' ),
'createFail' => __( 'Could not create the test page.', 'flipset' ),
'copyShortcode' => __( 'Copy shortcode', 'flipset' ),
'notPdf' => __( 'Please drop a PDF file.', 'flipset' ),
'uploadFail' => __( 'Upload failed. Try the Media Library.', 'flipset' ),
'presetApplied' => __( 'Preset applied', 'flipset' ),
'selectPdfFirst' => __( 'Please select a PDF file.', 'flipset' ),
'previewEmpty' => __( 'Select or upload a PDF above to see live interactive preview.', 'flipset' ),
'saving' => __( 'Saving…', 'flipset' ),
'updateEmbed' => __( 'Update Embed & Copy', 'flipset' ),
'savedCopiedBtn' => __( '✓ Saved! Copied', 'flipset' ),
/* translators: %s: shortcode that was copied. */
'toastSaved' => __( 'Saved! %s copied to clipboard.', 'flipset' ),
'saveFailed' => __( 'Could not save embed.', 'flipset' ),
'testPageFailed' => __( 'Could not create test page.', 'flipset' ),
/* translators: %s: text that was copied. */
'copiedItem' => __( 'Copied: %s', 'flipset' ),
'embedDuplicated' => __( 'Embed duplicated successfully.', 'flipset' ),
'duplicateFailed' => __( 'Could not duplicate embed.', 'flipset' ),
/* translators: %s: embed ID. */
'confirmDelete' => __( 'Are you sure you want to delete this embed? Any shortcode using id="%s" will no longer work.', 'flipset' ),
'embedDeleted' => __( 'Embed deleted.', 'flipset' ),
'deleteFailed' => __( 'Could not delete embed.', 'flipset' ),
'selectCover' => __( 'Choose a cover image', 'flipset' ),
'useCover' => __( 'Use as cover', 'flipset' ),
'coverSaved' => __( 'Cover updated', 'flipset' ),
'coverReset' => __( 'Cover reset to the first page', 'flipset' ),
'coverFailed' => __( 'Could not change the cover.', 'flipset' ),
),
'uploadNonce' => wp_create_nonce( 'media-form' ),
'canUpload' => current_user_can( 'upload_files' ),
)
);
if ( $is_settings ) {
wp_enqueue_script(
'flipset-settings',
FLIPSET_URL . 'assets/js/flipset-settings.js',
array( 'flipset-viewer' ),
$this->asset_ver( 'assets/js/flipset-settings.js' ),
true
);
wp_localize_script(
'flipset-settings',
'FlipsetSettings',
array(
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'flipset_admin' ),
'i18n' => array(
/* translators: 1: number of switches turned on, 2: number of switches in the section. */
'countOn' => __( '%1$s/%2$s on', 'flipset' ),
'saved' => __( 'Settings saved', 'flipset' ),
'confirmReset' => __( 'Reset this section to the default values? Nothing is saved until you click Save changes.', 'flipset' ),
'previewFail' => __( 'Could not load the preview.', 'flipset' ),
/* translators: %s: configured viewer height, for example 600px. */
'heightChip' => __( 'Height %s · scaled to fit', 'flipset' ),
'loading' => __( 'Loading preview…', 'flipset' ),
),
)
);
}
}
/**
* Asset version query. With WP_DEBUG on, the file's modification time is
* added so edited admin assets are never served from the browser cache.
*
* @param string $relative Path inside the plugin, e.g. assets/css/flipset-docs.css.
* @return string
*/
private function asset_ver( $relative ) {
$file = FLIPSET_DIR . ltrim( $relative, '/' );
if ( defined( 'WP_DEBUG' ) && WP_DEBUG && file_exists( $file ) ) {
return FLIPSET_VERSION . '.' . filemtime( $file );
}
return FLIPSET_VERSION;
}
public function register_settings() {
register_setting( self::OPTION_GROUP, self::OPTION_NAME, array( $this, 'sanitize_settings' ) );
add_settings_section(
'flipset_general',
__( 'Viewer Defaults', 'flipset' ),
'__return_false',
self::PAGE_SLUG
);
add_settings_field(
'default_width',
__( 'Default width', 'flipset' ),
array( $this, 'field_default_width' ),
self::PAGE_SLUG,
'flipset_general'
);
add_settings_field(
'default_height',
__( 'Default height', 'flipset' ),
array( $this, 'field_default_height' ),
self::PAGE_SLUG,
'flipset_general'
);
add_settings_field(
'allow_download',
__( 'Download button', 'flipset' ),
array( $this, 'field_allow_download' ),
self::PAGE_SLUG,
'flipset_general'
);
add_settings_field(
'allow_print',
__( 'Print button', 'flipset' ),
array( $this, 'field_allow_print' ),
self::PAGE_SLUG,
'flipset_general'
);
add_settings_field(
'default_theme',
__( 'Default theme', 'flipset' ),
array( $this, 'field_default_theme' ),
self::PAGE_SLUG,
'flipset_general'
);
add_settings_field(
'lazy_loading',
__( 'Lazy loading', 'flipset' ),
array( $this, 'field_lazy_loading' ),
self::PAGE_SLUG,
'flipset_general'
);
add_settings_field(
'show_branding',
__( 'Credit link', 'flipset' ),
array( $this, 'field_show_branding' ),
self::PAGE_SLUG,
'flipset_general'
);
add_settings_section(
'flipset_toolbar',
__( 'Toolbar Controls', 'flipset' ),
array( $this, 'section_toolbar' ),
self::PAGE_SLUG
);
add_settings_field(
'allow_search',
__( 'Search in document', 'flipset' ),
array( $this, 'field_allow_search' ),
self::PAGE_SLUG,
'flipset_toolbar'
);
add_settings_field(
'allow_theme',
__( 'Theme switcher', 'flipset' ),
array( $this, 'field_allow_theme' ),
self::PAGE_SLUG,
'flipset_toolbar'
);
add_settings_field(
'allow_share',
__( 'Share button', 'flipset' ),
array( $this, 'field_allow_share' ),
self::PAGE_SLUG,
'flipset_toolbar'
);
add_settings_field(
'allow_shortcuts',
__( 'Keyboard shortcuts guide', 'flipset' ),
array( $this, 'field_allow_shortcuts' ),
self::PAGE_SLUG,
'flipset_toolbar'
);
add_settings_field(
'allow_fullscreen',
__( 'Full screen button', 'flipset' ),
array( $this, 'field_allow_fullscreen' ),
self::PAGE_SLUG,
'flipset_toolbar'
);
add_settings_field(
'allow_presentation',
__( 'Presentation button', 'flipset' ),
array( $this, 'field_allow_presentation' ),
self::PAGE_SLUG,
'flipset_toolbar'
);
add_settings_section(
'flipset_discover',
__( 'Search and sharing', 'flipset' ),
array( $this, 'section_discover' ),
self::PAGE_SLUG
);
add_settings_field(
'index_pdf_text',
__( 'Site search', 'flipset' ),
array( $this, 'field_index_pdf_text' ),
self::PAGE_SLUG,
'flipset_discover'
);
add_settings_field(
'social_preview',
__( 'Social preview', 'flipset' ),
array( $this, 'field_social_preview' ),
self::PAGE_SLUG,
'flipset_discover'
);
add_settings_field(
'check_pdf_a11y',
__( 'Accessibility tags', 'flipset' ),
array( $this, 'field_check_pdf_a11y' ),
self::PAGE_SLUG,
'flipset_discover'
);
add_settings_field(
'autoembed_url',
__( 'Direct PDF auto-embed', 'flipset' ),
array( $this, 'field_autoembed_url' ),
self::PAGE_SLUG,
'flipset_discover'
);
add_settings_field(
'embed_attachment_pages',
__( 'Attachment pages', 'flipset' ),
array( $this, 'field_embed_attachment_pages' ),
self::PAGE_SLUG,
'flipset_discover'
);
}
/**
* Bound only to OPTION_NAME (`flipset_settings`). Returning a fresh
* array of free-plugin keys means extra POST keys cannot persist
* here, and a separately registered option such as
* `flipset_pro_settings` is never read or written.
*
* @param mixed $input Raw submitted option value.
* @return array Sanitized settings.
*/
public function sanitize_settings( $input ) {
$existing = get_option( self::OPTION_NAME, array() );
if ( ! is_array( $existing ) ) {
$existing = array();
}
if ( ! is_array( $input ) ) {
$input = array();
}
// Only the Settings screen posts the marker. Every other write
// (activation, AJAX, add-ons, the add_option() re-run of this
// callback) is programmatic: keys it omits keep their current value,
// or their default when never stored, instead of turning off.
$is_form = ! empty( $input[ self::FORM_MARKER ] );
$flag_defaults = $this->flag_defaults();
$valid_themes = array( 'light', 'dark', 'sepia' );
$theme = isset( $input['default_theme'] ) && in_array( $input['default_theme'], $valid_themes, true )
? $input['default_theme']
: ( $existing['default_theme'] ?? 'light' );
$clean = array(
'default_width' => isset( $input['default_width'] ) ? $this->sanitize_css_dimension( $input['default_width'], $existing['default_width'] ?? '100%' ) : ( $existing['default_width'] ?? '100%' ),
'default_height' => isset( $input['default_height'] ) ? $this->sanitize_css_dimension( $input['default_height'], $existing['default_height'] ?? '600px' ) : ( $existing['default_height'] ?? '600px' ),
'default_theme' => $theme,
);
foreach ( $flag_defaults as $key => $default ) {
// 'setup_copied_shortcode' is never a form field.
if ( $is_form && 'setup_copied_shortcode' !== $key ) {
$clean[ $key ] = ! empty( $input[ $key ] );
} elseif ( array_key_exists( $key, $input ) ) {
$clean[ $key ] = ! empty( $input[ $key ] );
} elseif ( array_key_exists( $key, $existing ) ) {
$clean[ $key ] = ! empty( $existing[ $key ] );
} else {
$clean[ $key ] = $default;
}
}
return $clean;
}
/**
* Validate a user-supplied CSS dimension value. Accepts common units:
* px, %, em, rem, vh, vw, svh, dvh, lvh. Returns $default on failure.
*
* @param string $value Raw value from the settings form.
* @param string $default Fallback if the value is not a valid CSS dimension.
* @return string
*/
private function sanitize_css_dimension( $value, $default ) {
$value = trim( sanitize_text_field( (string) $value ) );
// Allow a positive number followed by a recognised CSS length unit.
if ( preg_match( '/^\d+(\.\d+)?(px|%|em|rem|vh|vw|svh|dvh|lvh)$/', $value ) ) {
return $value;
}
// Also allow bare "0" (no unit required for zero).
if ( '0' === $value ) {
return $value;
}
return $default;
}
public function section_toolbar() {
echo '
' . esc_html__( 'Enable or disable specific buttons across all embedded PDF viewers. Individual embeds can also customize controls via the "hide" attribute.', 'flipset' ) . '
';
}
public function field_default_theme() {
$this->render_control( 'default_theme' );
}
public function field_show_branding() {
$this->render_control( 'show_branding' );
}
public function field_lazy_loading() {
$this->render_control( 'lazy_loading' );
}
public function field_allow_search() {
$this->render_control( 'allow_search' );
}
public function field_allow_theme() {
$this->render_control( 'allow_theme' );
}
public function field_allow_share() {
$this->render_control( 'allow_share' );
}
public function field_allow_shortcuts() {
$this->render_control( 'allow_shortcuts' );
}
public function field_allow_fullscreen() {
$this->render_control( 'allow_fullscreen' );
}
public function field_allow_presentation() {
$this->render_control( 'allow_presentation' );
}
public function field_default_width() {
$this->render_control( 'default_width' );
}
public function field_default_height() {
$this->render_control( 'default_height' );
}
public function field_allow_download() {
$this->render_control( 'allow_download' );
}
public function field_allow_print() {
$this->render_control( 'allow_print' );
}
public function field_index_pdf_text() {
$this->render_control( 'index_pdf_text' );
}
public function field_social_preview() {
$this->render_control( 'social_preview' );
}
public function field_check_pdf_a11y() {
$this->render_control( 'check_pdf_a11y' );
}
public function field_autoembed_url() {
$this->render_control( 'autoembed_url' );
}
public function field_embed_attachment_pages() {
$this->render_control( 'embed_attachment_pages' );
}
public function section_discover() {
echo '
' . esc_html__( 'Make embedded PDFs findable in WordPress search and nicer when a page is shared.', 'flipset' ) . '