'Dashboard', 'akram-templates' => 'Templates', 'akram-theme' => 'Theme Builder', 'akram-popups' => 'Popups', 'akram-submissions' => 'Submissions', 'akram-design' => 'Design System', 'akram-settings' => 'Settings', 'akram-tools' => 'Tools', ); public static function init() { add_action( 'admin_menu', array( __CLASS__, 'menu' ) ); add_action( 'admin_enqueue_scripts', array( __CLASS__, 'assets' ) ); add_action( 'admin_init', array( __CLASS__, 'welcome_redirect' ) ); add_action( 'admin_action_akram_new', array( __CLASS__, 'new_post' ) ); add_filter( 'post_row_actions', array( __CLASS__, 'row_action' ), 10, 2 ); add_filter( 'page_row_actions', array( __CLASS__, 'row_action' ), 10, 2 ); add_filter( 'display_post_states', array( __CLASS__, 'post_state' ), 10, 2 ); add_action( 'edit_form_after_title', array( __CLASS__, 'classic_button' ) ); add_action( 'enqueue_block_editor_assets', array( __CLASS__, 'block_editor' ) ); add_action( 'wp_nav_menu_item_custom_fields', array( __CLASS__, 'menu_field' ), 10, 2 ); add_action( 'wp_update_nav_menu_item', array( __CLASS__, 'menu_save' ), 10, 2 ); add_filter( 'plugin_action_links_' . plugin_basename( AKRAM_FILE ), array( __CLASS__, 'plugin_links' ) ); add_action( 'admin_notices', array( __CLASS__, 'notices' ) ); add_action( 'admin_enqueue_scripts', array( __CLASS__, 'list_button' ) ); } public static function menu() { $icon = I18n::brand_logo() ?: Brand::data_uri( '#a7aaad' ); $cap = 'edit_pages'; $brand = trim( (string) Options::get( 'brand_name', '' ) ) ?: 'Akram'; add_menu_page( $brand, $brand, $cap, 'akram', array( __CLASS__, 'page' ), $icon, 58.5 ); $labels = array( 'akram' => __( 'Dashboard', 'akram' ), 'akram-templates' => __( 'Templates', 'akram' ), 'akram-theme' => __( 'Theme Builder', 'akram' ), 'akram-popups' => __( 'Popups', 'akram' ), 'akram-submissions' => __( 'Submissions', 'akram' ), 'akram-design' => __( 'Design System', 'akram' ), 'akram-settings' => __( 'Settings', 'akram' ), 'akram-tools' => __( 'Tools', 'akram' ), ); foreach ( $labels as $slug => $label ) { $sub_cap = in_array( $slug, array( 'akram-settings', 'akram-tools' ), true ) ? 'manage_options' : ( in_array( $slug, array( 'akram-theme', 'akram-design' ), true ) ? 'edit_theme_options' : $cap ); if ( 'akram-submissions' === $slug ) { $unread = (int) ( wp_count_posts( 'akram_submission' )->private ?? 0 ); if ( $unread ) { $label .= ' ' . $unread . ''; } } add_submenu_page( 'akram', $label, $label, $sub_cap, $slug, array( __CLASS__, 'page' ) ); } } public static function page() { echo '
'; } public static function assets( $hook ) { $page = isset( $_GET['page'] ) ? sanitize_key( $_GET['page'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only request value; it changes nothing and is sanitised. if ( ! isset( self::PAGES[ $page ] ) ) { return; } $file = AKRAM_DIR . 'assets/build/' . Plugin::build_file( 'admin.js' ); $ver = file_exists( $file ) ? AKRAM_VERSION . '.' . filemtime( $file ) : AKRAM_VERSION; wp_enqueue_media(); wp_enqueue_style( 'akram-admin', Plugin::build_url( 'admin.css' ), array(), $ver ); wp_add_inline_style( 'akram-admin', Brand::font_css() ); wp_enqueue_script( 'akram-admin', Plugin::build_url( 'admin.js' ), array(), $ver, true ); $menus = array(); foreach ( wp_get_nav_menus() as $m ) { $menus[] = array( 'id' => $m->term_id, 'name' => $m->name, ); } wp_add_inline_script( 'akram-admin', 'window.AKRAM_ADMIN=' . wp_json_encode( array( 'page' => $page, 'rest' => esc_url_raw( rest_url( Rest::NS . '/' ) ), 'nonce' => wp_create_nonce( 'wp_rest' ), 'admin' => admin_url(), 'assets' => AKRAM_URL . 'assets/', 'version' => AKRAM_VERSION, 'bps' => Options::breakpoints(), 'globals' => Options::globals(), 'tplTypes' => Post_Types::type_labels(), 'newPage' => self::new_url( 'page' ), 'newPost' => self::new_url( 'post' ), 'canManage' => Plugin::can_manage(), 'canDesign' => current_user_can( 'edit_theme_options' ), 'i18n' => I18n::js_map(), 'rtl' => I18n::is_rtl(), 'brand' => I18n::brand(), 'brandLogo' => I18n::brand_logo(), 'locale' => get_user_locale(), 'unfiltered' => current_user_can( 'unfiltered_html' ), 'user' => wp_get_current_user()->display_name, 'site' => array( 'name' => get_bloginfo( 'name' ), 'url' => home_url( '/' ), ), 'postTypes' => array_values( array_map( static function ( $pt ) { return array( $pt->name, $pt->labels->name ); }, get_post_types( array( 'public' => true ), 'objects' ) ) ), 'taxonomies' => array_values( array_map( static function ( $t ) { return array( $t->name, $t->labels->name ); }, get_taxonomies( array( 'public' => true ), 'objects' ) ) ), 'roles' => array_map( static function ( $r ) { return translate_user_role( $r['name'] ); }, wp_roles()->roles ), 'menus' => $menus, 'welcome' => (bool) get_user_meta( get_current_user_id(), 'akram_onboarded', true ) ? false : true, ) ) . ';', 'before' ); if ( isset( $_GET['welcome'], $_GET['_wpnonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'akram_welcome' ) ) { update_user_meta( get_current_user_id(), 'akram_onboarded', 1 ); } } public static function new_url( $type ) { return add_query_arg( array( 'action' => 'akram_new', 'post_type' => $type, '_wpnonce' => wp_create_nonce( 'akram_new' ), ), admin_url( 'admin.php' ) ); } public static function welcome_redirect() { $key = 'akram_welcome_' . get_current_user_id(); if ( get_transient( $key ) && ! wp_doing_ajax() && ! isset( $_GET['activate-multi'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only request value; it changes nothing and is sanitised. delete_transient( $key ); wp_safe_redirect( add_query_arg( '_wpnonce', wp_create_nonce( 'akram_welcome' ), admin_url( 'admin.php?page=akram&welcome=1' ) ) ); exit; } } /** Create a new draft and open it in the builder. */ public static function new_post() { check_admin_referer( 'akram_new' ); $type = isset( $_GET['post_type'] ) ? sanitize_key( $_GET['post_type'] ) : 'page'; if ( ! post_type_exists( $type ) || ! current_user_can( get_post_type_object( $type )->cap->edit_posts ) || ! Plugin::can_build() ) { wp_die( esc_html__( 'Not allowed.', 'akram' ), 403 ); } $id = wp_insert_post( array( 'post_type' => $type, 'post_status' => 'draft', 'post_title' => __( 'Untitled', 'akram' ), ) ); update_post_meta( $id, '_akram_mode', 'builder' ); if ( 'page' === $type ) { update_post_meta( $id, '_wp_page_template', Options::get( 'default_template', 'akram-full' ) ); } wp_safe_redirect( Editor::edit_url( $id ) ); exit; } public static function row_action( $actions, $post ) { if ( in_array( $post->post_type, Options::enabled_post_types(), true ) && Plugin::can_build( $post->ID ) && 'trash' !== $post->post_status ) { $actions = array( 'akram' => '' . esc_html__( 'Edit with Akram', 'akram' ) . '' ) + $actions; } return $actions; } public static function post_state( $states, $post ) { if ( Document::is_built( $post->ID ) && 'akram_template' !== $post->post_type ) { $states['akram'] = trim( (string) Options::get( 'brand_name', '' ) ) ?: 'Akram'; } return $states; } /** "Add New with Akram" next to "Add New" on post list screens. */ public static function list_button( $hook ) { $screen = get_current_screen(); if ( 'edit.php' !== $hook || ! $screen || ! in_array( $screen->post_type, Options::enabled_post_types(), true ) || ! Plugin::can_build() || 'akram_template' === $screen->post_type ) { return; } $data = array( 'url' => self::new_url( $screen->post_type ), 'label' => __( 'Add New with Akram', 'akram' ), ); wp_enqueue_script( 'akram-list', AKRAM_URL . 'assets/js/list-button.js', array(), AKRAM_VERSION, true ); wp_add_inline_script( 'akram-list', 'window.akramList=' . wp_json_encode( $data ) . ';', 'before' ); } public static function classic_button( $post ) { if ( ! in_array( $post->post_type, Options::enabled_post_types(), true ) || ! Plugin::can_build( $post->ID ) ) { return; } echo '

' . esc_html__( 'Edit with Akram', 'akram' ) . '

'; } public static function block_editor() { global $post; if ( ! $post || ! in_array( $post->post_type, Options::enabled_post_types(), true ) || ! Plugin::can_build( $post->ID ) ) { return; } $data = array( 'url' => Editor::edit_url( $post->ID ), 'built' => Document::is_built( $post->ID ), 'label' => __( 'Edit with Akram', 'akram' ), 'note' => __( 'This page is designed with Akram. Edits made here to the content will be replaced the next time it is saved in Akram.', 'akram' ), ); wp_enqueue_script( 'akram-gutenberg', AKRAM_URL . 'assets/js/block-editor.js', array( 'wp-data', 'wp-dom-ready', 'wp-notices' ), AKRAM_VERSION, true ); wp_add_inline_script( 'akram-gutenberg', 'window.akramBlockEditor=' . wp_json_encode( $data ) . ';', 'before' ); } public static function menu_field( $item_id, $item ) { $val = (int) get_post_meta( $item_id, '_akram_mega', true ); $tpls = get_posts( array( 'post_type' => 'akram_template', 'posts_per_page' => 100, 'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- bounded query on a builder meta key. array( 'key' => '_akram_type', 'value' => array( 'mega', 'section', 'component' ), 'compare' => 'IN', ), ), ) ); wp_nonce_field( 'akram_mega', 'akram_mega_nonce' ); echo '

'; } public static function menu_save( $menu_id, $item_id ) { if ( ! isset( $_POST['akram_mega_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['akram_mega_nonce'] ) ), 'akram_mega' ) ) { return; } $v = isset( $_POST['akram_mega'][ $item_id ] ) ? absint( $_POST['akram_mega'][ $item_id ] ) : 0; $v ? update_post_meta( $item_id, '_akram_mega', $v ) : delete_post_meta( $item_id, '_akram_mega' ); } public static function plugin_links( $links ) { array_unshift( $links, '' . esc_html__( 'Dashboard', 'akram' ) . '', '' . esc_html__( 'Settings', 'akram' ) . '' ); return $links; } public static function notices() { if ( get_option( 'akram_needs_rebuild' ) && Plugin::can_manage() && ! isset( self::PAGES[ sanitize_key( $_GET['page'] ?? '' ) ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only request value; it changes nothing and is sanitised. echo '

Akram: ' . esc_html__( 'Pages can be rebuilt with the latest rendering engine and breakpoints.', 'akram' ) . ' ' . esc_html__( 'Rebuild now', 'akram' ) . '

'; } if ( ! Document::ensure_upload_dir() && Plugin::can_manage() ) { echo '

Akram: ' . esc_html__( 'The uploads folder is not writable, so CSS is printed inline. Pages still work, but caching is less efficient.', 'akram' ) . '

'; } } }