… display conditions * server-rendered widgets * embedded / global templates * {{nx:BASE64}} dynamic tags */ final class Runtime { public static function process( $html, array $out, $post_id ) { if ( false === strpos( $html, '/i', static function ( $m ) use ( $dyn, $post_id ) { $d = $dyn[ $m[1] ] ?? null; if ( ! $d || empty( $d['t'] ) ) { return ''; } return Widgets::render( (string) $d['t'], (array) ( $d['s'] ?? array() ), $m[1], $post_id ); }, $html ); $html = preg_replace_callback( '//', static function ( $m ) { return self::template( (int) $m[1] ); }, $html ); return $html; } private static function conditions( $html, array $cond ) { if ( false === strpos( $html, '([\s\S]*?)/i', static function ( $m ) use ( $cond ) { $rules = $cond[ $m[1] ] ?? null; if ( $rules && ! Conditions::check( (array) $rules ) ) { return ''; } return self::conditions( $m[2], $cond ); }, $html ); } // ------------------------------------------------------------------ // Protected content: contentlock box // ------------------------------------------------------------------ /** @var string element id whose last unlock attempt failed */ public static $lock_error = ''; public static function pass_hash( $pass ) { return 'nxh$' . hash_hmac( 'sha256', (string) $pass, wp_salt( 'auth' ) ); } public static function lock_token( $id, $hash ) { return hash_hmac( 'sha256', $id . '|' . $hash, wp_salt( 'logged_in' ) ); } public static function lock_open( $id, array $cfg ) { if ( Plugin::can_build() ) { return true; } $mode = (string) ( $cfg['mode'] ?? 'password' ); if ( 'login' === $mode ) { return is_user_logged_in(); } if ( 'role' === $mode ) { $roles = array_filter( array_map( 'trim', explode( ',', (string) ( $cfg['roles'] ?? '' ) ) ) ); return is_user_logged_in() && (bool) array_intersect( $roles, (array) wp_get_current_user()->roles ); } $hash = (string) ( $cfg['pass'] ?? '' ); if ( '' === $hash ) { return true; } $cookie = isset( $_COOKIE[ 'nx_unlock_' . $id ] ) ? sanitize_text_field( wp_unslash( $_COOKIE[ 'nx_unlock_' . $id ] ) ) : ''; return '' !== $cookie && hash_equals( self::lock_token( $id, $hash ), $cookie ); } private static function locks( $html, array $locks ) { if ( false === strpos( $html, '([\s\S]*?)([\s\S]*?)/i', static function ( $m ) use ( $locks ) { $cfg = (array) ( $locks[ $m[1] ] ?? array() ); if ( self::lock_open( $m[1], $cfg ) ) { return $m[2]; } $box = $m[3]; if ( self::$lock_error === $m[1] ) { $box = str_replace( '', '', $box ); } if ( 'login' === ( $cfg['mode'] ?? '' ) ) { $box = str_replace( 'href="/wp-login.php"', 'href="' . esc_url( wp_login_url( get_permalink() ?: home_url( '/' ) ) ) . '"', $box ); } return $box; }, $html ); } /** Handle a password unlock POST before output (sets a cookie, then redirects back). */ public static function maybe_unlock( array $docs ) { if ( empty( $_POST['nx_unlock'] ) || ! isset( $_POST['nx_pass'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- public password form (like WordPress's own post-password form); the password itself is the check. return; } $id = sanitize_key( wp_unslash( $_POST['nx_unlock'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- see above. $pass = (string) wp_unslash( $_POST['nx_pass'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- a password is compared as typed, never output or stored. foreach ( $docs as $out ) { $cfg = $out['lock'][ $id ] ?? null; if ( ! $cfg || 'password' !== ( $cfg['mode'] ?? '' ) || '' === (string) ( $cfg['pass'] ?? '' ) ) { continue; } if ( hash_equals( (string) $cfg['pass'], self::pass_hash( $pass ) ) ) { setcookie( 'nx_unlock_' . $id, self::lock_token( $id, (string) $cfg['pass'] ), array( 'expires' => time() + 30 * DAY_IN_SECONDS, 'path' => COOKIEPATH ?: '/', 'secure' => is_ssl(), 'httponly' => true, 'samesite' => 'Lax', ) ); $back = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : home_url( '/' ); wp_safe_redirect( $back, 303 ); exit; } self::$lock_error = $id; usleep( 300000 ); // slow down guessing return; } } /** Output of a published template (used by "Saved Template", global components, mega menus, loop items). */ public static function template( $id ) { $post = get_post( $id ); if ( ! $post || 'akram_template' !== $post->post_type || 'publish' !== $post->post_status && ! Plugin::can_build() ) { return ''; } return Frontend::render_post( $id ); } }