PluginProbe
Leadpages / trunk
Leadpages vtrunk
1.3.0 1.2.1 1.2.2 1.2.3 trunk 1.0.0 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.2.0
leadpages / includes / Core.php

Core.php in Leadpages trunk, at includes/Core.php

249 lines 7.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Leadpages;
4
5 use Leadpages\providers\Utils;
6 use Leadpages\rest\Service;
7 use Leadpages\models\Page;
8 use Leadpages\providers\config\Config;
9 use Leadpages\models\Options;
10
11 defined('ABSPATH') || die('No script kiddies please!'); // Avoid direct file request
12
13 /**
14 * This is the core class of the plugin responsible for initializing all other dependencies
15 * and running the plugin. All of the hooks the plugin depends on are added in this class.
16 */
17 class Core {
18
19 use Utils;
20
21 /** @var Core $me the singleton instantiation of the plugin */
22 private static $me;
23
24 /** @var Activator $activator */
25 private $activator;
26
27 /** @var Assets $assets */
28 private $assets;
29
30 /** @var Service $service */
31 private $service;
32
33 /** @var Proxy $proxy */
34 private $proxy;
35
36 /** @var Popups $popups */
37 private $popups;
38
39 /** @var Config */
40 private $config;
41
42 /**
43 * Instantiate all of the class dependencies.
44 *
45 * The constructor is protected because a factory method should only create
46 * a Core object.
47 */
48 protected function __construct() {
49 $this->config = Config::get_instance();
50
51 $this->activator = new Activator();
52 $this->assets = new Assets();
53 $this->service = new Service();
54 $this->proxy = new Proxy();
55 $this->popups = new Popups();
56 }
57
58 /**
59 * Get singleton core class.
60 *
61 * @return Core
62 */
63 public static function get_instance() {
64 return ! isset(self::$me) ? ( self::$me = new Core() ) : self::$me;
65 }
66
67 /**
68 * Register all of the hooks for the plugin.
69 */
70 public function run() {
71 add_action('init', [ $this->get_proxy(), 'serve_landing_page' ], 1);
72 add_action('init', [ $this, 'init' ]);
73 add_action('rest_api_init', [ $this->get_service(), 'rest_api_init' ]);
74
75 add_action('plugins_loaded', [ $this->get_activator(), 'update_db_check' ]);
76 add_action('plugins_loaded', [ $this, 'detect_plugin_conflicts' ]);
77 register_deactivation_hook(LEADPAGES_FILE, [ $this->get_activator(), 'deactivate' ]);
78
79 add_filter('wp_insert_post_data', [ $this, 'check_and_modify_post_slug' ], 1, 1);
80 add_action('wp_enqueue_scripts', [ $this->get_popups(), 'inject_embed' ]);
81 add_filter('script_loader_tag', [ $this->get_popups(), 'add_async_attribute' ], 10, 2);
82 $this->setup_admin_notices();
83 }
84
85 /**
86 * This method is fired on the WordPress init hook. It sets up the admin
87 * menu items and enqueues the scripts we need for our admin pages to work.
88 */
89 public function init() {
90 add_action('admin_menu', [ $this->get_assets(), 'render_admin_menu_page' ]);
91 add_action('admin_menu', [ $this->get_assets(), 'render_oauth_complete_page' ]);
92 add_action('admin_enqueue_scripts', [ $this->get_assets(), 'enqueue_admin_scripts' ]);
93
94 if ($this->is_connected_to_plugin()) {
95 add_action('admin_menu', [ $this->get_assets(), 'render_settings_page' ]);
96 }
97 }
98
99 /**
100 * Alert the user if:
101 * - Permalinks are not enabled (i.e. "Plain")
102 */
103 private function setup_admin_notices() {
104 if ('' === Options::get(Options::$permalink_structure)) {
105 add_action('admin_notices', [ $this, 'turn_on_permalinks' ]);
106 }
107 }
108
109
110 /**
111 * Detect another active plugin that also serves Leadpages content (registers a
112 * serve_landing_page handler on init at priority 1). To ensure two plugins never both take over
113 * the request and exit(), stand our own proxy down and warn the administrator to keep only one
114 * Leadpages plugin active.
115 *
116 * @return void
117 */
118 public function detect_plugin_conflicts() {
119 if (! $this->has_conflicting_serve_hook()) {
120 return;
121 }
122
123 // Stand down so only the other plugin serves. When the admin deactivates the duplicate (per
124 // the notice below) the remaining plugin no longer sees a conflict and serves normally.
125 remove_action('init', [ $this->get_proxy(), 'serve_landing_page' ], 1);
126 add_action('admin_notices', [ $this, 'conflicting_plugin_notice' ]);
127 }
128
129 /**
130 * Whether an object other than our own Proxy has registered serve_landing_page on init:1.
131 *
132 * @return bool
133 */
134 private function has_conflicting_serve_hook() {
135 global $wp_filter;
136 if (empty($wp_filter['init']) || empty($wp_filter['init']->callbacks[1])) {
137 return false;
138 }
139
140 foreach ($wp_filter['init']->callbacks[1] as $callback) {
141 $function = $callback['function'];
142 if (
143 is_array($function)
144 && isset($function[1])
145 && 'serve_landing_page' === $function[1]
146 && $function[0] !== $this->get_proxy()
147 ) {
148 return true;
149 }
150 }
151
152 return false;
153 }
154
155 /**
156 * Show an admin notice warning that another Leadpages plugin is active.
157 */
158 public function conflicting_plugin_notice() {
159 echo wp_kses(
160 "<div class='notice notice-warning is-dismissible'>
161 <p> Another Leadpages plugin appears to be active. To avoid conflicts serving your pages,
162 this Leadpages plugin has paused serving pages. Please keep only one Leadpages plugin
163 active.
164 </p></div>",
165 [
166 'div' => [
167 'class' => [],
168 ],
169 'p' => [],
170 ]
171 );
172 }
173
174 /**
175 * Show an admin notice informing the user that they need to enable permalinks
176 */
177 public function turn_on_permalinks() {
178 echo wp_kses(
179 "<div class='notice notice-error is-dismissible'>
180 <p> Leadpages plugin needs
181 <a href='options-permalink.php'>permalinks</a> enabled!
182 Permalink structure can not be 'Plain'.
183 </p></div>",
184 [
185 'div' => [
186 'class' => [],
187 ],
188 'p' => [],
189 'a' => [
190 'href' => [],
191 ],
192 ]
193 );
194 }
195
196 /**
197 * Check if Leadpages landing page is already connected with the same slug as the
198 * name of the WordPress post. This would cause permalink conflicts when serving pages if not prevented.
199 * Update the post name to a unique value across the posts and Leadpages tables.
200 *
201 * @param array $data an array of slashed, sanitized, and processed wp post data
202 * @return array
203 */
204 public function check_and_modify_post_slug( $data ) {
205 $slug = $data['post_name'];
206 $conflicts = Page::get_by_slug($slug);
207
208 // if a conflict is found, modify the slug by adding a numbered suffix and repeat until there is no conflict
209 if ($conflicts) {
210 $suffix = 2;
211 do {
212 $new_slug = $slug . '-' . $suffix;
213 ++$suffix;
214 $this->debug("Conflict with $slug, changing post name to $new_slug");
215 $conflicts = Page::get_by_slug($new_slug);
216 } while ($conflicts);
217
218 $data['post_name'] = $new_slug;
219 }
220
221 return $data;
222 }
223
224 /** @return Activator */
225 public function get_activator() {
226 return $this->activator;
227 }
228
229 /** @return Assets */
230 public function get_assets() {
231 return $this->assets;
232 }
233
234 /** @return Service */
235 public function get_service() {
236 return $this->service;
237 }
238
239 /** @return Proxy */
240 public function get_proxy() {
241 return $this->proxy;
242 }
243
244 /** @return Popups */
245 public function get_popups() {
246 return $this->popups;
247 }
248 }
249