'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 '';
}
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' )
. '
';
}
}
}