PluginProbe
StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More / 2.2.0
StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More v2.2.0
2.3.0 2.2.0 2.1.1 2.1.0 2.0.0 1.10.0 1.9.1 1.9.0 1.2.1 1.2.2 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 All 59 releases
storeengine / includes / installer.php

installer.php in StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More 2.2.0, at includes/installer.php

274 lines 8.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace StoreEngine;
4
5 if ( ! defined( 'ABSPATH' ) ) {
6 exit; // Exit if accessed directly.
7 }
8
9 use StoreEngine\Admin\Notices;
10 use StoreEngine\Admin\Settings;
11 use StoreEngine\Classes\PayoutsRepository;
12 use StoreEngine\Classes\Role;
13 use StoreEngine\Utils\Helper;
14
15 class Installer {
16 protected $storeengine_version;
17
18 public function __construct() {
19 $this->storeengine_version = get_option( 'storeengine_version' );
20 }
21
22 public static function init() {
23 add_action( 'init', [ __CLASS__, 'check_version' ], 5 );
24 }
25
26 /**
27 * Check version & run installer if needed.
28 *
29 * @since 1.6.4
30 * @return void
31 */
32 public static function check_version() {
33 $current_version = get_option( 'storeengine_version' );
34 $current_db_version = get_option( 'storeengine_db_version' );
35 $requires_update = version_compare( $current_version, STOREENGINE_VERSION, '<' )
36 || version_compare( $current_db_version, STOREENGINE_DB_VERSION, '<' );
37
38 if ( ! defined( 'IFRAME_REQUEST' ) && $requires_update ) {
39 define( 'STOREENGINE_UPDATING', true );
40 // Run the installer.
41 self::install();
42
43 /**
44 * Run after StoreEngine has been updated.
45 *
46 * @since 1.6.4
47 */
48 do_action( 'storeengine/updated' );
49 }
50 }
51
52 public static function install() {
53 if ( ! is_blog_installed() ) {
54 return;
55 }
56
57 // Check if we are not already running this routine.
58 if ( 'yes' === get_transient( 'storeengine_installing' ) ) {
59 return;
60 }
61
62 // If we made it till here nothing is running yet, lets set the transient now.
63 set_transient( 'storeengine_installing', 'yes', MINUTE_IN_SECONDS * 10 );
64
65 define( 'STOREENGINE_INSTALLING', true );
66
67 global $wpdb;
68 $wpdb->hide_errors();
69
70 try {
71 Database::init()->wpdb_table_fix();
72 Notices::remove_all_notices();
73 Database::create_initial_custom_table();
74 Settings::save_settings();
75 self::add_role();
76 // Copy any rows from legacy affiliate/vendor payout tables into the unified ledger.
77 PayoutsRepository::migrate_legacy_tables();
78 // Create files.
79 self::create_base_secure_directory();
80 // Need for flush rewrite rules.
81 PermalinkRewrite::init();
82 Database::init()->register_product_post_type();
83
84 if ( ! self::is_new_install() && 'yes' !== get_option( 'storeengine_has_redirect_to_setup_wizard', 'no' ) ) {
85 // Don't redirect old users to setup-wizard upon update.
86 update_option( 'storeengine_has_redirect_to_setup_wizard', 'yes', false );
87 }
88
89 update_option( 'storeengine_version', STOREENGINE_VERSION );
90 } catch ( \Exception $e ) {
91 // NoOp.
92 Helper::log_error( $e );
93 } finally {
94 delete_transient( 'storeengine_installing' );
95 }
96
97 if ( ! get_option( 'storeengine_first_install_time' ) ) {
98 add_option( 'storeengine_first_install_time', Helper::get_time() );
99 }
100
101 // Force a flush of rewrite rules even if the corresponding hook isn't initialized yet.
102 if ( ! has_action( 'storeengine/flush_rewrite_rules' ) ) {
103 flush_rewrite_rules();
104 }
105
106 /**
107 * Flush the rewrite rules after install or update.
108 */
109 do_action( 'storeengine/flush_rewrite_rules' );
110
111 /**
112 * Run after StoreEngine has been installed or updated.
113 *
114 * @since 1.6.4
115 */
116 do_action( 'storeengine/installed' );
117 }
118
119 /**
120 * Is this a brand-new StoreEngine installation?
121 *
122 * A brand-new installation has no version yet. Also treat empty installations as 'new'.
123 *
124 * @return boolean
125 */
126 public static function is_new_install(): bool {
127 return
128 is_null( get_option( 'storeengine_version', null ) ) ||
129 is_null( get_option( 'storeengine_first_install_time', null ) ) ||
130 (
131 ! Helper::get_settings( 'shop_page' ) &&
132 0 === array_sum( (array) wp_count_posts( Helper::PRODUCT_POST_TYPE ) )
133 );
134 }
135
136 public static function add_role() {
137 // customer role
138 Role::add_customer_role();
139 // shop manager role
140 Role::add_shop_manager_role();
141 // One-shot back-fill for caps added in v2 (manage_storeengine_settings, edit_storeengine_orders).
142 Role::migrate_caps_v2();
143 }
144
145 /**
146 * Create files/directories.
147 */
148 public static function create_base_secure_directory() {
149 // Install files and folders for uploading files and prevent hotlinking.
150 // Directory is restricted and downloads must be proxied via script.
151 $htaccess = '<IfModule mod_authz_core.c>' . PHP_EOL;
152 $htaccess .= "\tRequire all denied" . PHP_EOL;
153 $htaccess .= '</IfModule>' . PHP_EOL;
154 $htaccess .= '<IfModule !mod_authz_core.c>' . PHP_EOL;
155 $htaccess .= "\tDeny from all" . PHP_EOL;
156 $htaccess .= '</IfModule>';
157
158 self::create_uploads_upload_directory_files( [
159 [
160 'base' => STOREENGINE_SECURE_UPLOADS_DIR,
161 'file' => 'index.html', // Empty index, so server does not output auto-generated directory index.
162 'content' => '',
163 ],
164 [
165 'base' => STOREENGINE_SECURE_UPLOADS_DIR,
166 'file' => '.htaccess',
167 'content' => $htaccess,
168 ],
169 [
170 'base' => STOREENGINE_LOGS_DIR,
171 'file' => 'index.html',
172 'content' => '',
173 ],
174 [
175 'base' => STOREENGINE_LOGS_DIR,
176 'file' => '.htaccess',
177 'content' => $htaccess,
178 ],
179 ] );
180
181 // @TODO (In a separate function:private) Create a placeholder image in the media library (STOREENGINE_ASSETS_URI . images/thumbnail-placeholder.png).
182 // Add settings for user to change placeholder image.
183 // @TODO Add image sizes for gallery, preview & thumbnails.
184 // Maybe add settings for user to change image sizes
185 // If image sizes changes or theme switched regenerate thumbnails.
186 }
187
188 public static function create_uploads_upload_directory_files( array $config ) {
189 $upload_dir = wp_get_upload_dir();
190
191 foreach ( $config as $file ) {
192 $file = wp_parse_args( $file, [
193 'base' => '',
194 'file' => '',
195 'content' => '',
196 ] );
197
198 if ( empty( $file['base'] ) ) {
199 continue;
200 }
201
202 if ( ! str_starts_with( $file['base'], $upload_dir['basedir'] ) ) {
203 // Maybe out-of wp-content/uploads.
204 continue;
205 }
206
207 $file['file'] = ltrim( rtrim( $file['file'], '/\\' ), '/\\' );
208
209 // Check if file not exists first. If the file exists, the directory is already created.
210 if ( ! file_exists( trailingslashit( $file['base'] ) . $file['file'] ) && wp_mkdir_p( $file['base'] ) && $file['file'] ) {
211 $file_handle = @fopen( trailingslashit( $file['base'] ) . $file['file'], 'wb' ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_system_read_fopen, WordPress.WP.AlternativeFunctions.file_system_operations_fopen
212
213 if ( $file_handle ) {
214 fwrite( $file_handle, $file['content'] ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fwrite, WordPress.WP.AlternativeFunctions.file_system_operations_fwrite
215 fclose( $file_handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fclose, WordPress.WP.AlternativeFunctions.file_system_operations_fclose
216 }
217 }
218 }
219 }
220
221 public static function uninstall() {
222 as_unschedule_all_actions( 'storeengine/geolocation/maxmind/db-update' );
223 Role::remove_shop_manager_role();
224 Role::remove_customer_role();
225 flush_rewrite_rules();
226 }
227
228 /**
229 * Every Action Scheduler hook owned by StoreEngine core + bundled addons.
230 * Kept in one place so the "Reset Action Scheduler" admin tool and the
231 * uninstall hook stay in sync.
232 *
233 * @return string[]
234 */
235 public static function get_known_scheduled_action_hooks(): array {
236 return [
237 // Core.
238 'storeengine/geolocation/maxmind/db-update',
239 'storeengine/logs/auto_cleanup',
240 'storeengine/store_product_lookup',
241 'storeengine/remove_product_lookup_data',
242
243 // Webhooks addon.
244 'storeengine/webhooks/async_delivery',
245
246 // CSV addon.
247 'storeengine/csv/clean_tmp_csv',
248
249 // Invoice addon.
250 'storeengine/mail/clean_tmp_invoices',
251
252 // Multi-currency addon.
253 'storeengine/multi_currency/refresh_rates',
254
255 // Subscription addon.
256 'storeengine/subscription/start',
257 'storeengine/subscription/renewal',
258 'storeengine/subscription/scheduled_payment',
259 'storeengine/subscription/schedule_payment_retry',
260 'storeengine/subscription/schedule_trial_end',
261 'storeengine/subscription/schedule_expiration',
262 'storeengine/subscription/schedule_end_of_prepaid_term',
263
264 // Deprecated subscription hooks (kept for cleanup of legacy installs).
265 'storeengine_subscription_renewal',
266 'storeengine_create_renewal_order_schedule',
267
268 // StoreEngine Pro — license management addon.
269 'storeengine_pro/license/update_top_product_cache',
270 'storeengine_pro/license/update_event_cc',
271 ];
272 }
273 }
274