PluginProbe
Extendify / 3.1.5
Extendify v3.1.5
3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 0.7.0 All 126 releases
extendify / app / Toolbar / Frontend.php

Frontend.php in Extendify 3.1.5, at app/Toolbar/Frontend.php

266 lines 11.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Front-end loader for the simple Extendify toolbar.
5 *
6 * Renders a minimal toolbar at the top of the page (AI Agent /
7 * Quick Edit / Edit page / WP Admin) to replace the WordPress core
8 * admin bar for editors who prefer it. The core admin bar stays in
9 * the DOM so the Agent's mounted button still exists for the toolbar's
10 * "AI Agent" link to drive — we just hide it via CSS.
11 *
12 * Render decision: front end + logged in + Extendify's required
13 * capability (default manage_options) + the user's core "Show Toolbar
14 * when viewing site" preference is on + the user's Toolbar Style
15 * setting resolves to "simple". The default style is Launch-aware:
16 * 'simple' when the site has finished Launch, 'full' before it.
17 */
18
19 namespace Extendify\Toolbar;
20
21 defined('ABSPATH') || die('No direct access.');
22
23 use Extendify\Config;
24 use Extendify\PartnerData;
25
26 class Frontend
27 {
28 // phpcs:ignore PSR12.Properties.ConstantVisibility.NotFound -- 7.0 floor: no const visibility
29 const STYLE_META = 'extendify_toolbar_style';
30
31 public function __construct()
32 {
33 \add_action('wp_enqueue_scripts', [$this, 'loadScriptsAndStyles']);
34 \add_action('wp_body_open', [$this, 'render'], 1);
35 \add_action('wp_head', [$this, 'hideCoreAdminBar'], 1);
36 // This class is loaded conditionally, so callers often see the false default.
37 \add_filter('extendify_simple_toolbar_active', [$this, 'reportActive']);
38 }
39
40 /**
41 * @param bool $active
42 * @return bool
43 */
44 public function reportActive($active)
45 {
46 return $active || self::shouldRender();
47 }
48
49 /**
50 * Resolved style for the current user — 'simple' or 'full'.
51 *
52 * Honors the per-user setting; falls back to a Launch-aware
53 * default when the user hasn't picked one.
54 *
55 * @param int|null $userId
56 * @return string
57 */
58 public static function style($userId = null)
59 {
60 $userId = $userId ? (int) $userId : \get_current_user_id();
61 if (!$userId) {
62 return self::defaultStyle();
63 }
64 $val = \get_user_meta($userId, self::STYLE_META, true);
65 if ($val === 'simple' || $val === 'full') {
66 return $val;
67 }
68 return self::defaultStyle();
69 }
70
71 /**
72 * The toolbar style the user lands on when they haven't picked
73 * one explicitly. Simple after Launch, full before.
74 *
75 * @return string
76 */
77 public static function defaultStyle()
78 {
79 return Config::$launchCompleted ? 'simple' : 'full';
80 }
81
82 /**
83 * True iff the user has the core "Show Toolbar when viewing
84 * site" pref on. We respect it — turning the toolbar off in the
85 * core profile setting hides ours too.
86 *
87 * @param int|null $userId
88 * @return bool
89 */
90 protected static function corePrefOn($userId = null)
91 {
92 $userId = $userId ? (int) $userId : \get_current_user_id();
93 if (!$userId) {
94 return false;
95 }
96 return 'true' === \get_user_option('show_admin_bar_front', $userId);
97 }
98
99 /**
100 * Whether to render the simple toolbar on this request.
101 *
102 * @return bool
103 */
104 public static function shouldRender()
105 {
106 if (\is_admin() || !\is_user_logged_in() || !\current_user_can(Config::$requiredCapability)) {
107 return false;
108 }
109 // The Customizer preview iframe is a front-end render — the toolbar
110 // belongs only on the live, top-level page.
111 if (\is_customize_preview()) {
112 return false;
113 }
114 if (!self::corePrefOn()) {
115 return false;
116 }
117 return self::style() === 'simple';
118 }
119
120 /**
121 * @return void
122 */
123 public function loadScriptsAndStyles()
124 {
125 if (!self::shouldRender()) {
126 return;
127 }
128
129 $version = constant('EXTENDIFY_DEVMODE') ? uniqid() : Config::$version;
130 $manifest = Config::$assetManifest;
131 $assetKey = 'extendify-toolbar.php';
132 $jsKey = 'extendify-toolbar.js';
133 $cssKey = 'extendify-toolbar.css';
134
135 if (empty($manifest[$assetKey]) || empty($manifest[$jsKey])) {
136 return;
137 }
138
139 $scriptAssetPath = EXTENDIFY_PATH . 'public/build/' . $manifest[$assetKey];
140 $fallback = ['dependencies' => [], 'version' => $version];
141 $scriptAsset = file_exists($scriptAssetPath) ? require $scriptAssetPath : $fallback;
142
143 \wp_enqueue_script(
144 Config::$slug . '-toolbar-scripts',
145 EXTENDIFY_BASE_URL . 'public/build/' . $manifest[$jsKey],
146 $scriptAsset['dependencies'],
147 $scriptAsset['version'],
148 true
149 );
150
151 \wp_set_script_translations(
152 Config::$slug . '-toolbar-scripts',
153 'extendify-local',
154 EXTENDIFY_PATH . 'languages/js'
155 );
156
157 if (!empty($manifest[$cssKey])) {
158 \wp_enqueue_style(
159 Config::$slug . '-toolbar-styles',
160 EXTENDIFY_BASE_URL . 'public/build/' . $manifest[$cssKey],
161 [],
162 $version,
163 'all'
164 );
165 }
166 }
167
168 /**
169 * Render the toolbar at the top of <body>.
170 *
171 * @return void
172 */
173 public function render()
174 {
175 if (!self::shouldRender()) {
176 return;
177 }
178
179 $editLink = '';
180 $queried = \get_queried_object();
181 if ($queried instanceof \WP_Post) {
182 $maybe = \get_edit_post_link($queried->ID);
183 if ($maybe) {
184 $editLink = $maybe;
185 }
186 }
187
188 // phpcs:disable Generic.Files.LineLength.TooLong -- inline SVG + HTML template markup
189 ?>
190 <div id="extendify-toolbar" role="navigation" aria-label="<?php \esc_attr_e('Site toolbar', 'extendify-local'); ?>">
191 <div class="ext-tb-section ext-tb-left">
192 <button type="button" class="ext-tb-btn ext-tb-ai-agent" id="ext-tb-ai-agent" disabled aria-busy="true" aria-label="<?php \esc_attr_e('Open the AI Agent', 'extendify-local'); ?>">
193 <svg class="ext-tb-magic" width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
194 <path d="M17.0909 9.81818L18 7.81818L20 6.90909L18 6L17.0909 4L16.1818 6L14.1818 6.90909L16.1818 7.81818L17.0909 9.81818Z" fill="currentColor"/>
195 <path d="M17.0909 14.1818L16.1818 16.1818L14.1818 17.0909L16.1818 18L17.0909 20L18 18L20 17.0909L18 16.1818L17.0909 14.1818Z" fill="currentColor"/>
196 <path d="M11.6364 10.1818L9.81818 6.18182L8 10.1818L4 12L8 13.8182L9.81818 17.8182L11.6364 13.8182L15.6364 12L11.6364 10.1818ZM10.5382 12.72L9.81818 14.3055L9.09818 12.72L7.51273 12L9.09818 11.28L9.81818 9.69455L10.5382 11.28L12.1236 12L10.5382 12.72Z" fill="currentColor"/>
197 </svg>
198 <span class="ext-tb-ai-label"><?php \esc_html_e('AI Agent', 'extendify-local'); ?></span>
199 </button>
200 <?php if (PartnerData::setting('showQuickEdit') || Config::preview('quick-edit')) : ?>
201 <button type="button" class="ext-tb-btn ext-tb-quick-edit" id="ext-tb-quick-edit" role="switch" aria-checked="false">
202 <span class="ext-tb-toggle" aria-hidden="true"><span class="ext-tb-toggle-thumb"></span></span>
203 <span><?php \esc_html_e('Quick Edit', 'extendify-local'); ?></span>
204 </button>
205 <?php endif; ?>
206 </div>
207 <div class="ext-tb-section ext-tb-right">
208 <?php if ($editLink) : ?>
209 <a class="ext-tb-btn ext-tb-edit" href="<?php echo \esc_url($editLink); ?>" target="_blank" rel="noopener noreferrer">
210 <svg class="ext-tb-pencil" width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
211 <path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04a1 1 0 0 0 0-1.41l-2.34-2.34a1 1 0 0 0-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z" fill="currentColor"/>
212 </svg>
213 <span><?php /* translators: button label linking to the WordPress block editor. */ \esc_html_e('Block Editor', 'extendify-local'); ?></span>
214 <svg class="ext-tb-external" width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
215 <path d="M7 1.5h3.5V5" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
216 <path d="M10.5 1.5L5.5 6.5" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
217 <path d="M9.5 7v3a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1H5" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
218 </svg>
219 <span class="screen-reader-text"><?php \esc_html_e('(opens in a new tab)', 'extendify-local'); ?></span>
220 </a>
221 <?php endif; ?>
222 <a class="ext-tb-btn ext-tb-admin-link" href="<?php echo \esc_url(\admin_url()); ?>" target="_blank" rel="noopener noreferrer">
223 <span><?php \esc_html_e('WP Admin', 'extendify-local'); ?></span>
224 <svg class="ext-tb-external" width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
225 <path d="M7 1.5h3.5V5" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
226 <path d="M10.5 1.5L5.5 6.5" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
227 <path d="M9.5 7v3a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1H5" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
228 </svg>
229 <span class="screen-reader-text"><?php \esc_html_e('(opens in a new tab)', 'extendify-local'); ?></span>
230 </a>
231 <?php \do_action('extendify_toolbar_right'); ?>
232 </div>
233 </div>
234 <?php
235 // phpcs:enable Generic.Files.LineLength.TooLong
236 }
237
238 /**
239 * Hide the core admin bar visually and keep the top-margin reservation
240 * so theme layouts that account for it still flow correctly. We can't
241 * unconditionally drop the bar from the DOM — the Agent script mounts
242 * its own button into `#wp-admin-bar-extendify-agent-btn`, and the
243 * toolbar's "AI Agent" link drives that mounted button at click time.
244 *
245 * @return void
246 */
247 public function hideCoreAdminBar()
248 {
249 if (!self::shouldRender()) {
250 return;
251 }
252
253 ?>
254 <style id="extendify-toolbar-reset">
255 #wpadminbar { display: none !important; }
256 html { margin-top: 32px !important; }
257 * html body { margin-top: 32px !important; }
258 @media screen and (max-width: 782px) {
259 html { margin-top: 46px !important; }
260 * html body { margin-top: 46px !important; }
261 }
262 </style>
263 <?php
264 }
265 }
266