Completed
Branch FET/extract-activation-detecti... (285969)
by
unknown
02:37 queued 19s
created
core/espresso_definitions.php 2 patches
Indentation   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -7,16 +7,16 @@  discard block
 block discarded – undo
7 7
 define('EE_SUPPORT_EMAIL', '[email protected]');
8 8
 // used to be DIRECTORY_SEPARATOR, but that caused issues on windows
9 9
 if (! defined('DS')) {
10
-    define('DS', '/');
10
+	define('DS', '/');
11 11
 }
12 12
 if (! defined('PS')) {
13
-    define('PS', PATH_SEPARATOR);
13
+	define('PS', PATH_SEPARATOR);
14 14
 }
15 15
 if (! defined('SP')) {
16
-    define('SP', ' ');
16
+	define('SP', ' ');
17 17
 }
18 18
 if (! defined('EENL')) {
19
-    define('EENL', "\n");
19
+	define('EENL', "\n");
20 20
 }
21 21
 // define the plugin directory and URL
22 22
 define('EE_PLUGIN_BASENAME', plugin_basename(EVENT_ESPRESSO_MAIN_FILE));
@@ -70,7 +70,7 @@  discard block
 block discarded – undo
70 70
 define('EE_LANGUAGES_SAFE_DIR', EVENT_ESPRESSO_UPLOAD_DIR . 'languages/');
71 71
 // check for DOMPDF fonts in uploads
72 72
 if (file_exists(EVENT_ESPRESSO_UPLOAD_DIR . 'fonts/')) {
73
-    define('DOMPDF_FONT_DIR', EVENT_ESPRESSO_UPLOAD_DIR . 'fonts/');
73
+	define('DOMPDF_FONT_DIR', EVENT_ESPRESSO_UPLOAD_DIR . 'fonts/');
74 74
 }
75 75
 // just a handy constant occasionally needed for finding values representing infinity in the DB
76 76
 // you're better to use this than its straight value (currently -1) in case you ever
@@ -78,9 +78,9 @@  discard block
 block discarded – undo
78 78
 define('EE_INF_IN_DB', -1);
79 79
 define('EE_INF', INF > (float) PHP_INT_MAX ? INF : PHP_INT_MAX);
80 80
 if (! defined('EE_DEBUG')) {
81
-    define('EE_DEBUG', false);
81
+	define('EE_DEBUG', false);
82 82
 }
83 83
 // for older WP versions
84 84
 if (! defined('MONTH_IN_SECONDS')) {
85
-    define('MONTH_IN_SECONDS', DAY_IN_SECONDS * 30);
85
+	define('MONTH_IN_SECONDS', DAY_IN_SECONDS * 30);
86 86
 }
Please login to merge, or discard this patch.
Spacing   +45 added lines, -45 removed lines patch added patch discarded remove patch
@@ -6,81 +6,81 @@
 block discarded – undo
6 6
 define('EE_MIN_PHP_VER_RECOMMENDED', '5.6.32');
7 7
 define('EE_SUPPORT_EMAIL', '[email protected]');
8 8
 // used to be DIRECTORY_SEPARATOR, but that caused issues on windows
9
-if (! defined('DS')) {
9
+if ( ! defined('DS')) {
10 10
     define('DS', '/');
11 11
 }
12
-if (! defined('PS')) {
12
+if ( ! defined('PS')) {
13 13
     define('PS', PATH_SEPARATOR);
14 14
 }
15
-if (! defined('SP')) {
15
+if ( ! defined('SP')) {
16 16
     define('SP', ' ');
17 17
 }
18
-if (! defined('EENL')) {
18
+if ( ! defined('EENL')) {
19 19
     define('EENL', "\n");
20 20
 }
21 21
 // define the plugin directory and URL
22 22
 define('EE_PLUGIN_BASENAME', plugin_basename(EVENT_ESPRESSO_MAIN_FILE));
23
-define('EE_PLUGIN_DIR_PATH', dirname(EVENT_ESPRESSO_MAIN_FILE) . '/');
23
+define('EE_PLUGIN_DIR_PATH', dirname(EVENT_ESPRESSO_MAIN_FILE).'/');
24 24
 define('EE_PLUGIN_DIR_URL', plugin_dir_url(EVENT_ESPRESSO_MAIN_FILE));
25 25
 // main root folder paths
26
-define('EE_ADMIN_PAGES', EE_PLUGIN_DIR_PATH . 'admin_pages/');
27
-define('EE_CORE', EE_PLUGIN_DIR_PATH . 'core/');
28
-define('EE_MODULES', EE_PLUGIN_DIR_PATH . 'modules/');
29
-define('EE_PUBLIC', EE_PLUGIN_DIR_PATH . 'public/');
30
-define('EE_SHORTCODES', EE_PLUGIN_DIR_PATH . 'shortcodes/');
31
-define('EE_WIDGETS', EE_PLUGIN_DIR_PATH . 'widgets/');
32
-define('EE_PAYMENT_METHODS', EE_PLUGIN_DIR_PATH . 'payment_methods/');
33
-define('EE_CAFF_PATH', EE_PLUGIN_DIR_PATH . 'caffeinated/');
26
+define('EE_ADMIN_PAGES', EE_PLUGIN_DIR_PATH.'admin_pages/');
27
+define('EE_CORE', EE_PLUGIN_DIR_PATH.'core/');
28
+define('EE_MODULES', EE_PLUGIN_DIR_PATH.'modules/');
29
+define('EE_PUBLIC', EE_PLUGIN_DIR_PATH.'public/');
30
+define('EE_SHORTCODES', EE_PLUGIN_DIR_PATH.'shortcodes/');
31
+define('EE_WIDGETS', EE_PLUGIN_DIR_PATH.'widgets/');
32
+define('EE_PAYMENT_METHODS', EE_PLUGIN_DIR_PATH.'payment_methods/');
33
+define('EE_CAFF_PATH', EE_PLUGIN_DIR_PATH.'caffeinated/');
34 34
 // core system paths
35
-define('EE_ADMIN', EE_CORE . 'admin/');
36
-define('EE_CPTS', EE_CORE . 'CPTs/');
37
-define('EE_CLASSES', EE_CORE . 'db_classes/');
38
-define('EE_INTERFACES', EE_CORE . 'interfaces/');
39
-define('EE_BUSINESS', EE_CORE . 'business/');
40
-define('EE_MODELS', EE_CORE . 'db_models/');
41
-define('EE_HELPERS', EE_CORE . 'helpers/');
42
-define('EE_LIBRARIES', EE_CORE . 'libraries/');
43
-define('EE_TEMPLATES', EE_CORE . 'templates/');
44
-define('EE_THIRD_PARTY', EE_CORE . 'third_party_libs/');
45
-define('EE_GLOBAL_ASSETS', EE_TEMPLATES . 'global_assets/');
46
-define('EE_FORM_SECTIONS', EE_LIBRARIES . 'form_sections/');
35
+define('EE_ADMIN', EE_CORE.'admin/');
36
+define('EE_CPTS', EE_CORE.'CPTs/');
37
+define('EE_CLASSES', EE_CORE.'db_classes/');
38
+define('EE_INTERFACES', EE_CORE.'interfaces/');
39
+define('EE_BUSINESS', EE_CORE.'business/');
40
+define('EE_MODELS', EE_CORE.'db_models/');
41
+define('EE_HELPERS', EE_CORE.'helpers/');
42
+define('EE_LIBRARIES', EE_CORE.'libraries/');
43
+define('EE_TEMPLATES', EE_CORE.'templates/');
44
+define('EE_THIRD_PARTY', EE_CORE.'third_party_libs/');
45
+define('EE_GLOBAL_ASSETS', EE_TEMPLATES.'global_assets/');
46
+define('EE_FORM_SECTIONS', EE_LIBRARIES.'form_sections/');
47 47
 // gateways
48
-define('EE_GATEWAYS', EE_MODULES . 'gateways/');
49
-define('EE_GATEWAYS_URL', EE_PLUGIN_DIR_URL . 'modules/gateways/');
48
+define('EE_GATEWAYS', EE_MODULES.'gateways/');
49
+define('EE_GATEWAYS_URL', EE_PLUGIN_DIR_URL.'modules/gateways/');
50 50
 // asset URL paths
51
-define('EE_TEMPLATES_URL', EE_PLUGIN_DIR_URL . 'core/templates/');
52
-define('EE_GLOBAL_ASSETS_URL', EE_TEMPLATES_URL . 'global_assets/');
53
-define('EE_IMAGES_URL', EE_GLOBAL_ASSETS_URL . 'images/');
54
-define('EE_THIRD_PARTY_URL', EE_PLUGIN_DIR_URL . 'core/third_party_libs/');
55
-define('EE_HELPERS_ASSETS', EE_PLUGIN_DIR_URL . 'core/helpers/assets/');
56
-define('EE_LIBRARIES_URL', EE_PLUGIN_DIR_URL . 'core/libraries/');
51
+define('EE_TEMPLATES_URL', EE_PLUGIN_DIR_URL.'core/templates/');
52
+define('EE_GLOBAL_ASSETS_URL', EE_TEMPLATES_URL.'global_assets/');
53
+define('EE_IMAGES_URL', EE_GLOBAL_ASSETS_URL.'images/');
54
+define('EE_THIRD_PARTY_URL', EE_PLUGIN_DIR_URL.'core/third_party_libs/');
55
+define('EE_HELPERS_ASSETS', EE_PLUGIN_DIR_URL.'core/helpers/assets/');
56
+define('EE_LIBRARIES_URL', EE_PLUGIN_DIR_URL.'core/libraries/');
57 57
 // define upload paths
58 58
 $uploads = wp_upload_dir();
59 59
 // define the uploads directory and URL
60
-define('EVENT_ESPRESSO_UPLOAD_DIR', $uploads['basedir'] . '/espresso/');
61
-define('EVENT_ESPRESSO_UPLOAD_URL', $uploads['baseurl'] . '/espresso/');
60
+define('EVENT_ESPRESSO_UPLOAD_DIR', $uploads['basedir'].'/espresso/');
61
+define('EVENT_ESPRESSO_UPLOAD_URL', $uploads['baseurl'].'/espresso/');
62 62
 // define the templates directory and URL
63
-define('EVENT_ESPRESSO_TEMPLATE_DIR', $uploads['basedir'] . '/espresso/templates/');
64
-define('EVENT_ESPRESSO_TEMPLATE_URL', $uploads['baseurl'] . '/espresso/templates/');
63
+define('EVENT_ESPRESSO_TEMPLATE_DIR', $uploads['basedir'].'/espresso/templates/');
64
+define('EVENT_ESPRESSO_TEMPLATE_URL', $uploads['baseurl'].'/espresso/templates/');
65 65
 // define the gateway directory and URL
66
-define('EVENT_ESPRESSO_GATEWAY_DIR', $uploads['basedir'] . '/espresso/gateways/');
67
-define('EVENT_ESPRESSO_GATEWAY_URL', $uploads['baseurl'] . '/espresso/gateways/');
66
+define('EVENT_ESPRESSO_GATEWAY_DIR', $uploads['basedir'].'/espresso/gateways/');
67
+define('EVENT_ESPRESSO_GATEWAY_URL', $uploads['baseurl'].'/espresso/gateways/');
68 68
 // languages folder/path
69
-define('EE_LANGUAGES_SAFE_LOC', '../' . 'uploads/' . 'espresso/languages/');
70
-define('EE_LANGUAGES_SAFE_DIR', EVENT_ESPRESSO_UPLOAD_DIR . 'languages/');
69
+define('EE_LANGUAGES_SAFE_LOC', '../'.'uploads/'.'espresso/languages/');
70
+define('EE_LANGUAGES_SAFE_DIR', EVENT_ESPRESSO_UPLOAD_DIR.'languages/');
71 71
 // check for DOMPDF fonts in uploads
72
-if (file_exists(EVENT_ESPRESSO_UPLOAD_DIR . 'fonts/')) {
73
-    define('DOMPDF_FONT_DIR', EVENT_ESPRESSO_UPLOAD_DIR . 'fonts/');
72
+if (file_exists(EVENT_ESPRESSO_UPLOAD_DIR.'fonts/')) {
73
+    define('DOMPDF_FONT_DIR', EVENT_ESPRESSO_UPLOAD_DIR.'fonts/');
74 74
 }
75 75
 // just a handy constant occasionally needed for finding values representing infinity in the DB
76 76
 // you're better to use this than its straight value (currently -1) in case you ever
77 77
 // want to change its default value! or find when -1 means infinity
78 78
 define('EE_INF_IN_DB', -1);
79 79
 define('EE_INF', INF > (float) PHP_INT_MAX ? INF : PHP_INT_MAX);
80
-if (! defined('EE_DEBUG')) {
80
+if ( ! defined('EE_DEBUG')) {
81 81
     define('EE_DEBUG', false);
82 82
 }
83 83
 // for older WP versions
84
-if (! defined('MONTH_IN_SECONDS')) {
84
+if ( ! defined('MONTH_IN_SECONDS')) {
85 85
     define('MONTH_IN_SECONDS', DAY_IN_SECONDS * 30);
86 86
 }
Please login to merge, or discard this patch.
core/domain/services/pue/Stats.php 2 patches
Spacing   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -74,9 +74,9 @@  discard block
 block discarded – undo
74 74
     public function statsCallback()
75 75
     {
76 76
         // returns a callback that can is used to retrieve the stats to send along to the pue server.
77
-        return function () {
77
+        return function() {
78 78
             // we only send stats one a week, so let's see if our stat timestamp has expired.
79
-            if (! $this->sendStats()) {
79
+            if ( ! $this->sendStats()) {
80 80
                 return array();
81 81
             }
82 82
             return $this->stats_gatherer->stats();
@@ -124,9 +124,9 @@  discard block
 block discarded – undo
124 124
      */
125 125
     public static function optinText($extra = true)
126 126
     {
127
-        if (! $extra) {
127
+        if ( ! $extra) {
128 128
             echo '<h2 class="ee-admin-settings-hdr" '
129
-                 . (! $extra ? 'id="UXIP_settings"' : '')
129
+                 . ( ! $extra ? 'id="UXIP_settings"' : '')
130 130
                  . '>'
131 131
                  . esc_html__('User eXperience Improvement Program (UXIP)', 'event_espresso')
132 132
                  . EEH_Template::get_help_tab_link('organization_logo_info')
@@ -157,7 +157,7 @@  discard block
 block discarded – undo
157 157
                 ),
158 158
                 '<a href="https://eventespresso.com/about/user-experience-improvement-program-uxip/" target="_blank">',
159 159
                 '</a>',
160
-                '<a href="' . $settings_url . '" target="_blank">',
160
+                '<a href="'.$settings_url.'" target="_blank">',
161 161
                 '</a>'
162 162
             );
163 163
         }
@@ -171,14 +171,14 @@  discard block
 block discarded – undo
171 171
     {
172 172
         wp_register_script(
173 173
             'ee-data-optin-js',
174
-            EE_GLOBAL_ASSETS_URL . 'scripts/ee-data-optin.js',
174
+            EE_GLOBAL_ASSETS_URL.'scripts/ee-data-optin.js',
175 175
             array('jquery'),
176 176
             EVENT_ESPRESSO_VERSION,
177 177
             true
178 178
         );
179 179
         wp_register_style(
180 180
             'ee-data-optin-css',
181
-            EE_GLOBAL_ASSETS_URL . 'css/ee-data-optin.css',
181
+            EE_GLOBAL_ASSETS_URL.'css/ee-data-optin.css',
182 182
             array(),
183 183
             EVENT_ESPRESSO_VERSION
184 184
         );
@@ -197,7 +197,7 @@  discard block
 block discarded – undo
197 197
         $request = LoaderFactory::getLoader()->getShared(RequestInterface::class);
198 198
         $nonce = $request->getRequestParam('nonce');
199 199
         // verify nonce
200
-        if (! $nonce || ! wp_verify_nonce($nonce, 'ee-data-optin')) {
200
+        if ( ! $nonce || ! wp_verify_nonce($nonce, 'ee-data-optin')) {
201 201
             exit();
202 202
         }
203 203
 
Please login to merge, or discard this patch.
Indentation   +204 added lines, -204 removed lines patch added patch discarded remove patch
@@ -21,86 +21,86 @@  discard block
 block discarded – undo
21 21
  */
22 22
 class Stats
23 23
 {
24
-    const OPTIONS_KEY_EXPIRY_TIMESTAMP_FOR_SENDING_STATS = 'ee_uxip_stats_expiry';
25
-
26
-    /**
27
-     * @var Config
28
-     */
29
-    private $config;
30
-
31
-
32
-    /**
33
-     * @var StatsGatherer
34
-     */
35
-    private $stats_gatherer;
36
-
37
-
38
-    /**
39
-     * @var EE_Maintenance_Mode
40
-     */
41
-    private $maintenance_mode;
42
-
43
-    public function __construct(
44
-        Config $config,
45
-        EE_Maintenance_Mode $maintenance_mode,
46
-        StatsGatherer $stats_gatherer
47
-    ) {
48
-        $this->config = $config;
49
-        $this->maintenance_mode = $maintenance_mode;
50
-        $this->stats_gatherer = $stats_gatherer;
51
-        $this->setUxipNotices();
52
-    }
53
-
54
-
55
-    /**
56
-     * Displays uxip opt-in notice if necessary.
57
-     */
58
-    private function setUxipNotices()
59
-    {
60
-        if ($this->canDisplayNotices()) {
61
-            add_action('admin_notices', array($this, 'optinNotice'));
62
-            add_action('admin_enqueue_scripts', array($this, 'enqueueScripts'));
63
-            add_action('wp_ajax_espresso_data_optin', array($this, 'ajaxHandler'));
64
-        }
65
-    }
66
-
67
-
68
-    /**
69
-     * This returns the callback that PluginUpdateEngineChecker will use for getting any extra stats to send.
70
-     *
71
-     * @return Closure
72
-     */
73
-    public function statsCallback()
74
-    {
75
-        // returns a callback that can is used to retrieve the stats to send along to the pue server.
76
-        return function () {
77
-            // we only send stats one a week, so let's see if our stat timestamp has expired.
78
-            if (! $this->sendStats()) {
79
-                return array();
80
-            }
81
-            return $this->stats_gatherer->stats();
82
-        };
83
-    }
84
-
85
-
86
-    /**
87
-     * Return whether notices can be displayed or not
88
-     *
89
-     * @return bool
90
-     */
91
-    private function canDisplayNotices()
92
-    {
93
-        return ! $this->config->hasNotifiedForUxip()
94
-               && $this->maintenance_mode->level() !== EE_Maintenance_Mode::level_2_complete_maintenance;
95
-    }
96
-
97
-
98
-    /**
99
-     * Callback for the admin_notices hook that outputs the UXIP optin-in notice.
100
-     */
101
-    public function optinNotice()
102
-    {
103
-        ?>
24
+	const OPTIONS_KEY_EXPIRY_TIMESTAMP_FOR_SENDING_STATS = 'ee_uxip_stats_expiry';
25
+
26
+	/**
27
+	 * @var Config
28
+	 */
29
+	private $config;
30
+
31
+
32
+	/**
33
+	 * @var StatsGatherer
34
+	 */
35
+	private $stats_gatherer;
36
+
37
+
38
+	/**
39
+	 * @var EE_Maintenance_Mode
40
+	 */
41
+	private $maintenance_mode;
42
+
43
+	public function __construct(
44
+		Config $config,
45
+		EE_Maintenance_Mode $maintenance_mode,
46
+		StatsGatherer $stats_gatherer
47
+	) {
48
+		$this->config = $config;
49
+		$this->maintenance_mode = $maintenance_mode;
50
+		$this->stats_gatherer = $stats_gatherer;
51
+		$this->setUxipNotices();
52
+	}
53
+
54
+
55
+	/**
56
+	 * Displays uxip opt-in notice if necessary.
57
+	 */
58
+	private function setUxipNotices()
59
+	{
60
+		if ($this->canDisplayNotices()) {
61
+			add_action('admin_notices', array($this, 'optinNotice'));
62
+			add_action('admin_enqueue_scripts', array($this, 'enqueueScripts'));
63
+			add_action('wp_ajax_espresso_data_optin', array($this, 'ajaxHandler'));
64
+		}
65
+	}
66
+
67
+
68
+	/**
69
+	 * This returns the callback that PluginUpdateEngineChecker will use for getting any extra stats to send.
70
+	 *
71
+	 * @return Closure
72
+	 */
73
+	public function statsCallback()
74
+	{
75
+		// returns a callback that can is used to retrieve the stats to send along to the pue server.
76
+		return function () {
77
+			// we only send stats one a week, so let's see if our stat timestamp has expired.
78
+			if (! $this->sendStats()) {
79
+				return array();
80
+			}
81
+			return $this->stats_gatherer->stats();
82
+		};
83
+	}
84
+
85
+
86
+	/**
87
+	 * Return whether notices can be displayed or not
88
+	 *
89
+	 * @return bool
90
+	 */
91
+	private function canDisplayNotices()
92
+	{
93
+		return ! $this->config->hasNotifiedForUxip()
94
+			   && $this->maintenance_mode->level() !== EE_Maintenance_Mode::level_2_complete_maintenance;
95
+	}
96
+
97
+
98
+	/**
99
+	 * Callback for the admin_notices hook that outputs the UXIP optin-in notice.
100
+	 */
101
+	public function optinNotice()
102
+	{
103
+		?>
104 104
         <div class="updated data-collect-optin" id="espresso-data-collect-optin-container">
105 105
             <div id="data-collect-optin-options-container">
106 106
                 <span class="dashicons dashicons-admin-site"></span>
@@ -113,128 +113,128 @@  discard block
 block discarded – undo
113 113
             </div>
114 114
         </div>
115 115
         <?php
116
-    }
117
-
118
-
119
-    /**
120
-     * Retrieves the optin text (static so it can be used in multiple places as necessary).
121
-     *
122
-     * @param bool $extra
123
-     */
124
-    public static function optinText($extra = true)
125
-    {
126
-        if (! $extra) {
127
-            echo '<h2 class="ee-admin-settings-hdr" '
128
-                 . (! $extra ? 'id="UXIP_settings"' : '')
129
-                 . '>'
130
-                 . esc_html__('User eXperience Improvement Program (UXIP)', 'event_espresso')
131
-                 . EEH_Template::get_help_tab_link('organization_logo_info')
132
-                 . '</h2>';
133
-            printf(
134
-                esc_html__(
135
-                    '%1$sPlease help us make Event Espresso better and vote for your favorite features.%2$s The %3$sUser eXperience Improvement Program (UXIP)%4$s, has been created so when you use Event Espresso you are voting for the features and settings that are important to you. The UXIP helps us understand how you use our products and services, track problems and in what context. If you opt-out of the UXIP you essentially elect for us to disregard how you use Event Espresso as we build new features and make changes. Participation in the program is completely voluntary and it is disabled by default. The end results of the UXIP are software improvements to better meet your needs. The data we collect will never be sold, traded, or misused in any way. %5$sPlease see our %6$sPrivacy Policy%7$s for more information.',
136
-                    'event_espresso'
137
-                ),
138
-                '<p><em>',
139
-                '</em></p>',
140
-                '<a href="https://eventespresso.com/about/user-experience-improvement-program-uxip/" target="_blank">',
141
-                '</a>',
142
-                '<br><br>',
143
-                '<a href="https://eventespresso.com/about/privacy-policy/" target="_blank">',
144
-                '</a>'
145
-            );
146
-        } else {
147
-            $settings_url = EEH_URL::add_query_args_and_nonce(
148
-                array('action' => 'default'),
149
-                admin_url('admin.php?page=espresso_general_settings')
150
-            );
151
-            $settings_url .= '#UXIP_settings';
152
-            printf(
153
-                esc_html__(
154
-                    'The Event Espresso UXIP feature is not yet active on your site. For %1$smore info%2$s and to opt-in %3$sclick here%4$s.',
155
-                    'event_espresso'
156
-                ),
157
-                '<a href="https://eventespresso.com/about/user-experience-improvement-program-uxip/" target="_blank">',
158
-                '</a>',
159
-                '<a href="' . $settings_url . '" target="_blank">',
160
-                '</a>'
161
-            );
162
-        }
163
-    }
164
-
165
-
166
-    /**
167
-     * Callback for admin_enqueue_scripts that sets up the scripts and styles for the uxip notice
168
-     */
169
-    public function enqueueScripts()
170
-    {
171
-        wp_register_script(
172
-            'ee-data-optin-js',
173
-            EE_GLOBAL_ASSETS_URL . 'scripts/ee-data-optin.js',
174
-            array('jquery'),
175
-            EVENT_ESPRESSO_VERSION,
176
-            true
177
-        );
178
-        wp_register_style(
179
-            'ee-data-optin-css',
180
-            EE_GLOBAL_ASSETS_URL . 'css/ee-data-optin.css',
181
-            array(),
182
-            EVENT_ESPRESSO_VERSION
183
-        );
184
-
185
-        wp_enqueue_script('ee-data-optin-js');
186
-        wp_enqueue_style('ee-data-optin-css');
187
-    }
188
-
189
-
190
-    /**
191
-     * Callback for wp_ajax_espresso_data_optin that handles the ajax request
192
-     */
193
-    public function ajaxHandler()
194
-    {
195
-        /** @var RequestInterface $request */
196
-        $request = LoaderFactory::getLoader()->getShared(RequestInterface::class);
197
-        $nonce = $request->getRequestParam('nonce');
198
-        // verify nonce
199
-        if (! $nonce || ! wp_verify_nonce($nonce, 'ee-data-optin')) {
200
-            exit();
201
-        }
202
-
203
-        // update has notified option
204
-        $this->config->setHasNotifiedAboutUxip();
205
-        exit();
206
-    }
207
-
208
-
209
-    /**
210
-     * Used to determine whether additional stats are sent.
211
-     */
212
-    private function sendStats()
213
-    {
214
-        return $this->config->isOptedInForUxip()
215
-               && $this->maintenance_mode->level() !== EE_Maintenance_Mode::level_2_complete_maintenance
216
-               && $this->statSendTimestampExpired();
217
-    }
218
-
219
-
220
-    /**
221
-     * Returns true when the timestamp used to track whether stats get sent (currently a weekly interval) is expired.
222
-     * Returns false otherwise.
223
-     *
224
-     * @return bool
225
-     */
226
-    private function statSendTimestampExpired()
227
-    {
228
-        $current_expiry = get_option(self::OPTIONS_KEY_EXPIRY_TIMESTAMP_FOR_SENDING_STATS, null);
229
-        if ($current_expiry === null) {
230
-            add_option(self::OPTIONS_KEY_EXPIRY_TIMESTAMP_FOR_SENDING_STATS, time() + WEEK_IN_SECONDS, '', 'no');
231
-            return true;
232
-        }
233
-
234
-        if (time() > (int) $current_expiry) {
235
-            update_option(self::OPTIONS_KEY_EXPIRY_TIMESTAMP_FOR_SENDING_STATS, time() + WEEK_IN_SECONDS);
236
-            return true;
237
-        }
238
-        return false;
239
-    }
116
+	}
117
+
118
+
119
+	/**
120
+	 * Retrieves the optin text (static so it can be used in multiple places as necessary).
121
+	 *
122
+	 * @param bool $extra
123
+	 */
124
+	public static function optinText($extra = true)
125
+	{
126
+		if (! $extra) {
127
+			echo '<h2 class="ee-admin-settings-hdr" '
128
+				 . (! $extra ? 'id="UXIP_settings"' : '')
129
+				 . '>'
130
+				 . esc_html__('User eXperience Improvement Program (UXIP)', 'event_espresso')
131
+				 . EEH_Template::get_help_tab_link('organization_logo_info')
132
+				 . '</h2>';
133
+			printf(
134
+				esc_html__(
135
+					'%1$sPlease help us make Event Espresso better and vote for your favorite features.%2$s The %3$sUser eXperience Improvement Program (UXIP)%4$s, has been created so when you use Event Espresso you are voting for the features and settings that are important to you. The UXIP helps us understand how you use our products and services, track problems and in what context. If you opt-out of the UXIP you essentially elect for us to disregard how you use Event Espresso as we build new features and make changes. Participation in the program is completely voluntary and it is disabled by default. The end results of the UXIP are software improvements to better meet your needs. The data we collect will never be sold, traded, or misused in any way. %5$sPlease see our %6$sPrivacy Policy%7$s for more information.',
136
+					'event_espresso'
137
+				),
138
+				'<p><em>',
139
+				'</em></p>',
140
+				'<a href="https://eventespresso.com/about/user-experience-improvement-program-uxip/" target="_blank">',
141
+				'</a>',
142
+				'<br><br>',
143
+				'<a href="https://eventespresso.com/about/privacy-policy/" target="_blank">',
144
+				'</a>'
145
+			);
146
+		} else {
147
+			$settings_url = EEH_URL::add_query_args_and_nonce(
148
+				array('action' => 'default'),
149
+				admin_url('admin.php?page=espresso_general_settings')
150
+			);
151
+			$settings_url .= '#UXIP_settings';
152
+			printf(
153
+				esc_html__(
154
+					'The Event Espresso UXIP feature is not yet active on your site. For %1$smore info%2$s and to opt-in %3$sclick here%4$s.',
155
+					'event_espresso'
156
+				),
157
+				'<a href="https://eventespresso.com/about/user-experience-improvement-program-uxip/" target="_blank">',
158
+				'</a>',
159
+				'<a href="' . $settings_url . '" target="_blank">',
160
+				'</a>'
161
+			);
162
+		}
163
+	}
164
+
165
+
166
+	/**
167
+	 * Callback for admin_enqueue_scripts that sets up the scripts and styles for the uxip notice
168
+	 */
169
+	public function enqueueScripts()
170
+	{
171
+		wp_register_script(
172
+			'ee-data-optin-js',
173
+			EE_GLOBAL_ASSETS_URL . 'scripts/ee-data-optin.js',
174
+			array('jquery'),
175
+			EVENT_ESPRESSO_VERSION,
176
+			true
177
+		);
178
+		wp_register_style(
179
+			'ee-data-optin-css',
180
+			EE_GLOBAL_ASSETS_URL . 'css/ee-data-optin.css',
181
+			array(),
182
+			EVENT_ESPRESSO_VERSION
183
+		);
184
+
185
+		wp_enqueue_script('ee-data-optin-js');
186
+		wp_enqueue_style('ee-data-optin-css');
187
+	}
188
+
189
+
190
+	/**
191
+	 * Callback for wp_ajax_espresso_data_optin that handles the ajax request
192
+	 */
193
+	public function ajaxHandler()
194
+	{
195
+		/** @var RequestInterface $request */
196
+		$request = LoaderFactory::getLoader()->getShared(RequestInterface::class);
197
+		$nonce = $request->getRequestParam('nonce');
198
+		// verify nonce
199
+		if (! $nonce || ! wp_verify_nonce($nonce, 'ee-data-optin')) {
200
+			exit();
201
+		}
202
+
203
+		// update has notified option
204
+		$this->config->setHasNotifiedAboutUxip();
205
+		exit();
206
+	}
207
+
208
+
209
+	/**
210
+	 * Used to determine whether additional stats are sent.
211
+	 */
212
+	private function sendStats()
213
+	{
214
+		return $this->config->isOptedInForUxip()
215
+			   && $this->maintenance_mode->level() !== EE_Maintenance_Mode::level_2_complete_maintenance
216
+			   && $this->statSendTimestampExpired();
217
+	}
218
+
219
+
220
+	/**
221
+	 * Returns true when the timestamp used to track whether stats get sent (currently a weekly interval) is expired.
222
+	 * Returns false otherwise.
223
+	 *
224
+	 * @return bool
225
+	 */
226
+	private function statSendTimestampExpired()
227
+	{
228
+		$current_expiry = get_option(self::OPTIONS_KEY_EXPIRY_TIMESTAMP_FOR_SENDING_STATS, null);
229
+		if ($current_expiry === null) {
230
+			add_option(self::OPTIONS_KEY_EXPIRY_TIMESTAMP_FOR_SENDING_STATS, time() + WEEK_IN_SECONDS, '', 'no');
231
+			return true;
232
+		}
233
+
234
+		if (time() > (int) $current_expiry) {
235
+			update_option(self::OPTIONS_KEY_EXPIRY_TIMESTAMP_FOR_SENDING_STATS, time() + WEEK_IN_SECONDS);
236
+			return true;
237
+		}
238
+		return false;
239
+	}
240 240
 }
Please login to merge, or discard this patch.
core/EE_Log.core.php 2 patches
Spacing   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -48,7 +48,7 @@  discard block
 block discarded – undo
48 48
      */
49 49
     public static function instance()
50 50
     {
51
-        if (! self::$_instance instanceof EE_Log) {
51
+        if ( ! self::$_instance instanceof EE_Log) {
52 52
             self::$_instance = new self();
53 53
         }
54 54
         return self::$_instance;
@@ -61,7 +61,7 @@  discard block
 block discarded – undo
61 61
     private function __construct()
62 62
     {
63 63
 
64
-        if (! EE_Registry::instance()->CFG->admin->use_remote_logging) {
64
+        if ( ! EE_Registry::instance()->CFG->admin->use_remote_logging) {
65 65
             return;
66 66
         }
67 67
 
@@ -105,14 +105,14 @@  discard block
 block discarded – undo
105 105
      */
106 106
     private function _format_message($file = '', $function = '', $message = '', $type = '')
107 107
     {
108
-        $msg = '----------------------------------------------------------------------------------------' . PHP_EOL;
109
-        $msg .= '[' . current_time('mysql') . '] ';
108
+        $msg = '----------------------------------------------------------------------------------------'.PHP_EOL;
109
+        $msg .= '['.current_time('mysql').'] ';
110 110
         $msg .= ! empty($file) ? basename($file) : '';
111 111
         $msg .= ! empty($file) && ! empty($function) ? ' -> ' : '';
112
-        $msg .= ! empty($function) ? $function . '()' : '';
112
+        $msg .= ! empty($function) ? $function.'()' : '';
113 113
         $msg .= PHP_EOL;
114 114
         $type = ! empty($type) ? $type : 'log message';
115
-        $msg .= ! empty($message) ? "\t" . '[' . $type . '] ' . $message . PHP_EOL : '';
115
+        $msg .= ! empty($message) ? "\t".'['.$type.'] '.$message.PHP_EOL : '';
116 116
         return $msg;
117 117
     }
118 118
 
@@ -164,18 +164,18 @@  discard block
 block discarded – undo
164 164
 
165 165
         /** @var RequestInterface $request */
166 166
         $request = LoaderFactory::getLoader()->getShared(RequestInterface::class);
167
-        $data = 'domain=' . $request->getServerParam('HTTP_HOST');
168
-        $data .= '&ip=' . $request->getServerParam('SERVER_ADDR');
169
-        $data .= '&server_type=' . $request->getServerParam('SERVER_SOFTWARE');
170
-        $data .= '&time=' . time();
171
-        $data .= '&remote_log=' . $this->_log;
167
+        $data = 'domain='.$request->getServerParam('HTTP_HOST');
168
+        $data .= '&ip='.$request->getServerParam('SERVER_ADDR');
169
+        $data .= '&server_type='.$request->getServerParam('SERVER_SOFTWARE');
170
+        $data .= '&time='.time();
171
+        $data .= '&remote_log='.$this->_log;
172 172
         $data .= '&action=save';
173 173
 
174 174
         if (defined('EELOGGING_PASS')) {
175
-            $data .= '&pass=' . EELOGGING_PASS;
175
+            $data .= '&pass='.EELOGGING_PASS;
176 176
         }
177 177
         if (defined('EELOGGING_KEY')) {
178
-            $data .= '&key=' . EELOGGING_KEY;
178
+            $data .= '&key='.EELOGGING_KEY;
179 179
         }
180 180
 
181 181
         $c = curl_init($this->_remote_logging_url);
Please login to merge, or discard this patch.
Indentation   +193 added lines, -193 removed lines patch added patch discarded remove patch
@@ -19,197 +19,197 @@
 block discarded – undo
19 19
  */
20 20
 class EE_Log
21 21
 {
22
-    /**
23
-     * @var string
24
-     */
25
-    private $_log = '';
26
-
27
-    /**
28
-     * Used for remote logging
29
-     *
30
-     * @var string
31
-     */
32
-    private $_remote_logging_url = '';
33
-
34
-    /**
35
-     * @var string
36
-     */
37
-    private $_remote_log = '';
38
-
39
-    /**
40
-     * @var EE_Log
41
-     */
42
-    private static $_instance;
43
-
44
-
45
-    /**
46
-     * @return EE_Log
47
-     */
48
-    public static function instance()
49
-    {
50
-        if (! self::$_instance instanceof EE_Log) {
51
-            self::$_instance = new self();
52
-        }
53
-        return self::$_instance;
54
-    }
55
-
56
-    /**
57
-     * @access private
58
-     * @return EE_Log
59
-     */
60
-    private function __construct()
61
-    {
62
-
63
-        if (! EE_Registry::instance()->CFG->admin->use_remote_logging) {
64
-            return;
65
-        }
66
-
67
-        $this->_remote_logging_url = EE_Registry::instance()->CFG->admin->remote_logging_url;
68
-        $this->_remote_log = '';
69
-
70
-        if (EE_Registry::instance()->CFG->admin->use_remote_logging) {
71
-            add_action('shutdown', array($this, 'send_log'), 9999);
72
-        }
73
-    }
74
-
75
-
76
-    /**
77
-     *    verify_filesystem
78
-     * tests that the required files and folders exist and are writable
79
-     *
80
-     */
81
-    public function verify_filesystem()
82
-    {
83
-        $msg = esc_html__(
84
-            'The Local File Logging functionality was removed permanently. Remote Logging is recommended instead.',
85
-            'event_espresso'
86
-        );
87
-        EE_Error::doing_it_wrong(
88
-            __METHOD__,
89
-            $msg,
90
-            '4.10.1.p'
91
-        );
92
-    }
93
-
94
-
95
-    /**
96
-     *    _format_message
97
-     *    makes yer log entries look all purdy
98
-     *
99
-     * @param string $file
100
-     * @param string $function
101
-     * @param string $message
102
-     * @param string $type
103
-     * @return string
104
-     */
105
-    private function _format_message($file = '', $function = '', $message = '', $type = '')
106
-    {
107
-        $msg = '----------------------------------------------------------------------------------------' . PHP_EOL;
108
-        $msg .= '[' . current_time('mysql') . '] ';
109
-        $msg .= ! empty($file) ? basename($file) : '';
110
-        $msg .= ! empty($file) && ! empty($function) ? ' -> ' : '';
111
-        $msg .= ! empty($function) ? $function . '()' : '';
112
-        $msg .= PHP_EOL;
113
-        $type = ! empty($type) ? $type : 'log message';
114
-        $msg .= ! empty($message) ? "\t" . '[' . $type . '] ' . $message . PHP_EOL : '';
115
-        return $msg;
116
-    }
117
-
118
-
119
-    /**
120
-     *    log
121
-     * adds content to the EE_Log->_log property which gets written to file during the WP 'shutdown' hookpoint via the
122
-     * EE_Log::write_log() callback
123
-     *
124
-     * @param string $file
125
-     * @param string $function
126
-     * @param string $message
127
-     * @param string $type
128
-     */
129
-    public function log($file = '', $function = '', $message = '', $type = '')
130
-    {
131
-        $this->_log .= $this->_format_message($file, $function, $message, $type);
132
-    }
133
-
134
-
135
-    /**
136
-     * write_log
137
-     * appends the results of the 'AHEE_log' filter to the espresso log file
138
-     */
139
-    public function write_log()
140
-    {
141
-        $msg = esc_html__(
142
-            'The Local File Logging functionality was removed permanently. Remote Logging is recommended instead.',
143
-            'event_espresso'
144
-        );
145
-        EE_Error::doing_it_wrong(
146
-            __METHOD__,
147
-            $msg,
148
-            '4.10.1.p'
149
-        );
150
-    }
151
-
152
-
153
-    /**
154
-     * send_log
155
-     * sends the espresso log to a remote URL via a PHP cURL request
156
-     */
157
-    public function send_log()
158
-    {
159
-
160
-        if (empty($this->_remote_logging_url)) {
161
-            return;
162
-        }
163
-
164
-        /** @var RequestInterface $request */
165
-        $request = LoaderFactory::getLoader()->getShared(RequestInterface::class);
166
-        $data = 'domain=' . $request->getServerParam('HTTP_HOST');
167
-        $data .= '&ip=' . $request->getServerParam('SERVER_ADDR');
168
-        $data .= '&server_type=' . $request->getServerParam('SERVER_SOFTWARE');
169
-        $data .= '&time=' . time();
170
-        $data .= '&remote_log=' . $this->_log;
171
-        $data .= '&action=save';
172
-
173
-        if (defined('EELOGGING_PASS')) {
174
-            $data .= '&pass=' . EELOGGING_PASS;
175
-        }
176
-        if (defined('EELOGGING_KEY')) {
177
-            $data .= '&key=' . EELOGGING_KEY;
178
-        }
179
-
180
-        $c = curl_init($this->_remote_logging_url);
181
-        curl_setopt($c, CURLOPT_POST, true);
182
-        curl_setopt($c, CURLOPT_POSTFIELDS, $data);
183
-        curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
184
-        curl_exec($c);
185
-        curl_close($c);
186
-    }
187
-
188
-
189
-    /**
190
-     * write_debug
191
-     * writes the contents of the current request's data to a log file.
192
-     * previous entries are overwritten
193
-     */
194
-    public function write_debug()
195
-    {
196
-        $msg = esc_html__(
197
-            'The Local File Logging functionality was removed permanently. Remote Logging is recommended instead.',
198
-            'event_espresso'
199
-        );
200
-        EE_Error::doing_it_wrong(
201
-            __METHOD__,
202
-            $msg,
203
-            '4.10.1.p'
204
-        );
205
-    }
206
-
207
-
208
-    /**
209
-     * __clone
210
-     */
211
-    public function __clone()
212
-    {
213
-        trigger_error(esc_html__('Clone is not allowed.', 'event_espresso'), E_USER_ERROR);
214
-    }
22
+	/**
23
+	 * @var string
24
+	 */
25
+	private $_log = '';
26
+
27
+	/**
28
+	 * Used for remote logging
29
+	 *
30
+	 * @var string
31
+	 */
32
+	private $_remote_logging_url = '';
33
+
34
+	/**
35
+	 * @var string
36
+	 */
37
+	private $_remote_log = '';
38
+
39
+	/**
40
+	 * @var EE_Log
41
+	 */
42
+	private static $_instance;
43
+
44
+
45
+	/**
46
+	 * @return EE_Log
47
+	 */
48
+	public static function instance()
49
+	{
50
+		if (! self::$_instance instanceof EE_Log) {
51
+			self::$_instance = new self();
52
+		}
53
+		return self::$_instance;
54
+	}
55
+
56
+	/**
57
+	 * @access private
58
+	 * @return EE_Log
59
+	 */
60
+	private function __construct()
61
+	{
62
+
63
+		if (! EE_Registry::instance()->CFG->admin->use_remote_logging) {
64
+			return;
65
+		}
66
+
67
+		$this->_remote_logging_url = EE_Registry::instance()->CFG->admin->remote_logging_url;
68
+		$this->_remote_log = '';
69
+
70
+		if (EE_Registry::instance()->CFG->admin->use_remote_logging) {
71
+			add_action('shutdown', array($this, 'send_log'), 9999);
72
+		}
73
+	}
74
+
75
+
76
+	/**
77
+	 *    verify_filesystem
78
+	 * tests that the required files and folders exist and are writable
79
+	 *
80
+	 */
81
+	public function verify_filesystem()
82
+	{
83
+		$msg = esc_html__(
84
+			'The Local File Logging functionality was removed permanently. Remote Logging is recommended instead.',
85
+			'event_espresso'
86
+		);
87
+		EE_Error::doing_it_wrong(
88
+			__METHOD__,
89
+			$msg,
90
+			'4.10.1.p'
91
+		);
92
+	}
93
+
94
+
95
+	/**
96
+	 *    _format_message
97
+	 *    makes yer log entries look all purdy
98
+	 *
99
+	 * @param string $file
100
+	 * @param string $function
101
+	 * @param string $message
102
+	 * @param string $type
103
+	 * @return string
104
+	 */
105
+	private function _format_message($file = '', $function = '', $message = '', $type = '')
106
+	{
107
+		$msg = '----------------------------------------------------------------------------------------' . PHP_EOL;
108
+		$msg .= '[' . current_time('mysql') . '] ';
109
+		$msg .= ! empty($file) ? basename($file) : '';
110
+		$msg .= ! empty($file) && ! empty($function) ? ' -> ' : '';
111
+		$msg .= ! empty($function) ? $function . '()' : '';
112
+		$msg .= PHP_EOL;
113
+		$type = ! empty($type) ? $type : 'log message';
114
+		$msg .= ! empty($message) ? "\t" . '[' . $type . '] ' . $message . PHP_EOL : '';
115
+		return $msg;
116
+	}
117
+
118
+
119
+	/**
120
+	 *    log
121
+	 * adds content to the EE_Log->_log property which gets written to file during the WP 'shutdown' hookpoint via the
122
+	 * EE_Log::write_log() callback
123
+	 *
124
+	 * @param string $file
125
+	 * @param string $function
126
+	 * @param string $message
127
+	 * @param string $type
128
+	 */
129
+	public function log($file = '', $function = '', $message = '', $type = '')
130
+	{
131
+		$this->_log .= $this->_format_message($file, $function, $message, $type);
132
+	}
133
+
134
+
135
+	/**
136
+	 * write_log
137
+	 * appends the results of the 'AHEE_log' filter to the espresso log file
138
+	 */
139
+	public function write_log()
140
+	{
141
+		$msg = esc_html__(
142
+			'The Local File Logging functionality was removed permanently. Remote Logging is recommended instead.',
143
+			'event_espresso'
144
+		);
145
+		EE_Error::doing_it_wrong(
146
+			__METHOD__,
147
+			$msg,
148
+			'4.10.1.p'
149
+		);
150
+	}
151
+
152
+
153
+	/**
154
+	 * send_log
155
+	 * sends the espresso log to a remote URL via a PHP cURL request
156
+	 */
157
+	public function send_log()
158
+	{
159
+
160
+		if (empty($this->_remote_logging_url)) {
161
+			return;
162
+		}
163
+
164
+		/** @var RequestInterface $request */
165
+		$request = LoaderFactory::getLoader()->getShared(RequestInterface::class);
166
+		$data = 'domain=' . $request->getServerParam('HTTP_HOST');
167
+		$data .= '&ip=' . $request->getServerParam('SERVER_ADDR');
168
+		$data .= '&server_type=' . $request->getServerParam('SERVER_SOFTWARE');
169
+		$data .= '&time=' . time();
170
+		$data .= '&remote_log=' . $this->_log;
171
+		$data .= '&action=save';
172
+
173
+		if (defined('EELOGGING_PASS')) {
174
+			$data .= '&pass=' . EELOGGING_PASS;
175
+		}
176
+		if (defined('EELOGGING_KEY')) {
177
+			$data .= '&key=' . EELOGGING_KEY;
178
+		}
179
+
180
+		$c = curl_init($this->_remote_logging_url);
181
+		curl_setopt($c, CURLOPT_POST, true);
182
+		curl_setopt($c, CURLOPT_POSTFIELDS, $data);
183
+		curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
184
+		curl_exec($c);
185
+		curl_close($c);
186
+	}
187
+
188
+
189
+	/**
190
+	 * write_debug
191
+	 * writes the contents of the current request's data to a log file.
192
+	 * previous entries are overwritten
193
+	 */
194
+	public function write_debug()
195
+	{
196
+		$msg = esc_html__(
197
+			'The Local File Logging functionality was removed permanently. Remote Logging is recommended instead.',
198
+			'event_espresso'
199
+		);
200
+		EE_Error::doing_it_wrong(
201
+			__METHOD__,
202
+			$msg,
203
+			'4.10.1.p'
204
+		);
205
+	}
206
+
207
+
208
+	/**
209
+	 * __clone
210
+	 */
211
+	public function __clone()
212
+	{
213
+		trigger_error(esc_html__('Clone is not allowed.', 'event_espresso'), E_USER_ERROR);
214
+	}
215 215
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Import.class.php 2 patches
Spacing   +40 added lines, -40 removed lines patch added patch discarded remove patch
@@ -141,7 +141,7 @@  discard block
 block discarded – undo
141 141
     public function import()
142 142
     {
143 143
 
144
-        require_once(EE_CLASSES . 'EE_CSV.class.php');
144
+        require_once(EE_CLASSES.'EE_CSV.class.php');
145 145
         $this->EE_CSV = EE_CSV::instance();
146 146
 
147 147
         /** @var RequestInterface $request */
@@ -188,18 +188,18 @@  discard block
 block discarded – undo
188 188
                     break;
189 189
             }
190 190
 
191
-            if (! $error_msg) {
191
+            if ( ! $error_msg) {
192 192
                 $filename = $files['file']['name'][0];
193 193
                 $file_ext = substr(strrchr($filename, '.'), 1);
194 194
                 $file_type = $files['file']['type'][0];
195 195
                 $temp_file = $files['file']['tmp_name'][0];
196
-                $filesize = $files['file']['size'][0] / 1024;// convert from bytes to KB
196
+                $filesize = $files['file']['size'][0] / 1024; // convert from bytes to KB
197 197
 
198 198
                 if ($file_ext == 'csv') {
199
-                    $max_upload = $this->EE_CSV->get_max_upload_size();// max upload size in KB
199
+                    $max_upload = $this->EE_CSV->get_max_upload_size(); // max upload size in KB
200 200
                     if ($filesize < $max_upload || true) {
201 201
                         $wp_upload_dir = str_replace(array('\\', '/'), '/', wp_upload_dir());
202
-                        $path_to_file = $wp_upload_dir['basedir'] . '/espresso/' . $filename;
202
+                        $path_to_file = $wp_upload_dir['basedir'].'/espresso/'.$filename;
203 203
 
204 204
                         if (move_uploaded_file($temp_file, $path_to_file)) {
205 205
                             // convert csv to array
@@ -334,8 +334,8 @@  discard block
 block discarded – undo
334 334
         // begin looking through the $csv_data_array, expecting the toplevel key to be the model's name...
335 335
         $old_site_url = 'none-specified';
336 336
         // hanlde metadata
337
-        if (isset($csv_data_array[ EE_CSV::metadata_header ])) {
338
-            $csv_metadata = array_shift($csv_data_array[ EE_CSV::metadata_header ]);
337
+        if (isset($csv_data_array[EE_CSV::metadata_header])) {
338
+            $csv_metadata = array_shift($csv_data_array[EE_CSV::metadata_header]);
339 339
             // ok so its metadata, dont try to save it to ehte db obviously...
340 340
             if (isset($csv_metadata['site_url']) && $csv_metadata['site_url'] == site_url()) {
341 341
                 EE_Error::add_attention(
@@ -360,14 +360,14 @@  discard block
 block discarded – undo
360 360
                     )
361 361
                 );
362 362
             };
363
-            unset($csv_data_array[ EE_CSV::metadata_header ]);
363
+            unset($csv_data_array[EE_CSV::metadata_header]);
364 364
         }
365 365
         /**
366 366
          * @var $old_db_to_new_db_mapping 2d array: toplevel keys being model names, bottom-level keys being the original key, and
367 367
          * the value will be the newly-inserted ID.
368 368
          * If we have already imported data from the same website via CSV, it shoudl be kept in this wp option
369 369
          */
370
-        $old_db_to_new_db_mapping = get_option('ee_id_mapping_from' . sanitize_title($old_site_url), array());
370
+        $old_db_to_new_db_mapping = get_option('ee_id_mapping_from'.sanitize_title($old_site_url), array());
371 371
         if ($old_db_to_new_db_mapping) {
372 372
             EE_Error::add_attention(
373 373
                 sprintf(
@@ -387,7 +387,7 @@  discard block
 block discarded – undo
387 387
         );
388 388
 
389 389
         // save the mapping from old db to new db in case they try re-importing the same data from the same website again
390
-        update_option('ee_id_mapping_from' . sanitize_title($old_site_url), $old_db_to_new_db_mapping);
390
+        update_option('ee_id_mapping_from'.sanitize_title($old_site_url), $old_db_to_new_db_mapping);
391 391
 
392 392
         if ($this->_total_updates > 0) {
393 393
             EE_Error::add_success(
@@ -510,7 +510,7 @@  discard block
 block discarded – undo
510 510
                 // find the PK in the row of data (or a combined key if
511 511
                 // there is no primary key)
512 512
                 if ($model->has_primary_key_field()) {
513
-                    $id_in_csv = $model_object_data[ $model->primary_key_name() ];
513
+                    $id_in_csv = $model_object_data[$model->primary_key_name()];
514 514
                 } else {
515 515
                     $id_in_csv = $model->get_index_primary_key_string($model_object_data);
516 516
                 }
@@ -554,14 +554,14 @@  discard block
 block discarded – undo
554 554
                         $what_to_do = self::do_update;
555 555
                         // and if this model has a primary key, remember its mapping
556 556
                         if ($model->has_primary_key_field()) {
557
-                            $old_db_to_new_db_mapping[ $model_name ][ $id_in_csv ] = $conflicting->ID();
558
-                            $model_object_data[ $model->primary_key_name() ] = $conflicting->ID();
557
+                            $old_db_to_new_db_mapping[$model_name][$id_in_csv] = $conflicting->ID();
558
+                            $model_object_data[$model->primary_key_name()] = $conflicting->ID();
559 559
                         } else {
560 560
                             // we want to update this conflicting item, instead of inserting a conflicting item
561 561
                             // so we need to make sure they match entirely (its possible that they only conflicted on one field, but we need them to match on other fields
562 562
                             // for the WHERE conditions in the update). At the time of this comment, there were no models like this
563 563
                             foreach ($model->get_combined_primary_key_fields() as $key_field) {
564
-                                $model_object_data[ $key_field->get_name() ] = $conflicting->get(
564
+                                $model_object_data[$key_field->get_name()] = $conflicting->get(
565 565
                                     $key_field->get_name()
566 566
                                 );
567 567
                             }
@@ -621,7 +621,7 @@  discard block
 block discarded – undo
621 621
         $model_name = $model->get_this_model_name();
622 622
         // if it's a site-to-site export-and-import, see if this modelobject's id
623 623
         // in the old data that we know of
624
-        if (isset($old_db_to_new_db_mapping[ $model_name ][ $id_in_csv ])) {
624
+        if (isset($old_db_to_new_db_mapping[$model_name][$id_in_csv])) {
625 625
             return self::do_update;
626 626
         } else {
627 627
             return self::do_insert;
@@ -677,13 +677,13 @@  discard block
 block discarded – undo
677 677
         if (
678 678
             $model->has_primary_key_field() &&
679 679
             $model->get_primary_key_field()->is_auto_increment() &&
680
-            isset($old_db_to_new_db_mapping[ $model->get_this_model_name() ]) &&
680
+            isset($old_db_to_new_db_mapping[$model->get_this_model_name()]) &&
681 681
             isset(
682
-                $old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $model_object_data[ $model->primary_key_name() ] ]
682
+                $old_db_to_new_db_mapping[$model->get_this_model_name()][$model_object_data[$model->primary_key_name()]]
683 683
             )
684 684
         ) {
685
-            $model_object_data[ $model->primary_key_name() ] = $old_db_to_new_db_mapping[ $model->get_this_model_name(
686
-            ) ][ $model_object_data[ $model->primary_key_name() ] ];
685
+            $model_object_data[$model->primary_key_name()] = $old_db_to_new_db_mapping[$model->get_this_model_name(
686
+            )][$model_object_data[$model->primary_key_name()]];
687 687
         }
688 688
 
689 689
         try {
@@ -699,10 +699,10 @@  discard block
 block discarded – undo
699 699
                 $found_a_mapping = false;
700 700
                 foreach ($models_pointed_to as $model_pointed_to_by_fk) {
701 701
                     if ($model_name_field) {
702
-                        $value_of_model_name_field = $model_object_data[ $model_name_field->get_name() ];
702
+                        $value_of_model_name_field = $model_object_data[$model_name_field->get_name()];
703 703
                         if ($value_of_model_name_field == $model_pointed_to_by_fk) {
704
-                            $model_object_data[ $field_obj->get_name() ] = $this->_find_mapping_in(
705
-                                $model_object_data[ $field_obj->get_name() ],
704
+                            $model_object_data[$field_obj->get_name()] = $this->_find_mapping_in(
705
+                                $model_object_data[$field_obj->get_name()],
706 706
                                 $model_pointed_to_by_fk,
707 707
                                 $old_db_to_new_db_mapping,
708 708
                                 $export_from_site_a_to_b
@@ -711,8 +711,8 @@  discard block
 block discarded – undo
711 711
                             break;
712 712
                         }
713 713
                     } else {
714
-                        $model_object_data[ $field_obj->get_name() ] = $this->_find_mapping_in(
715
-                            $model_object_data[ $field_obj->get_name() ],
714
+                        $model_object_data[$field_obj->get_name()] = $this->_find_mapping_in(
715
+                            $model_object_data[$field_obj->get_name()],
716 716
                             $model_pointed_to_by_fk,
717 717
                             $old_db_to_new_db_mapping,
718 718
                             $export_from_site_a_to_b
@@ -777,8 +777,8 @@  discard block
 block discarded – undo
777 777
      */
778 778
     protected function _find_mapping_in($object_id, $model_name, $old_db_to_new_db_mapping, $export_from_site_a_to_b)
779 779
     {
780
-        if (isset($old_db_to_new_db_mapping[ $model_name ][ $object_id ])) {
781
-            return $old_db_to_new_db_mapping[ $model_name ][ $object_id ];
780
+        if (isset($old_db_to_new_db_mapping[$model_name][$object_id])) {
781
+            return $old_db_to_new_db_mapping[$model_name][$object_id];
782 782
         } elseif ($object_id == '0' || $object_id == '') {
783 783
             // leave as-is
784 784
             return $object_id;
@@ -786,7 +786,7 @@  discard block
 block discarded – undo
786 786
             // we couldn't find a mapping for this, and it's from a different site,
787 787
             // so blank it out
788 788
             return null;
789
-        } elseif (! $export_from_site_a_to_b) {
789
+        } elseif ( ! $export_from_site_a_to_b) {
790 790
             // we coudln't find a mapping for this, but it's from thsi DB anyway
791 791
             // so let's just leave it as-is
792 792
             return $object_id;
@@ -806,8 +806,8 @@  discard block
 block discarded – undo
806 806
         // remove the primary key, if there is one (we don't want it for inserts OR updates)
807 807
         // we'll put it back in if we need it
808 808
         if ($model->has_primary_key_field() && $model->get_primary_key_field()->is_auto_increment()) {
809
-            $effective_id = $model_object_data[ $model->primary_key_name() ];
810
-            unset($model_object_data[ $model->primary_key_name() ]);
809
+            $effective_id = $model_object_data[$model->primary_key_name()];
810
+            unset($model_object_data[$model->primary_key_name()]);
811 811
         } else {
812 812
             $effective_id = $model->get_index_primary_key_string($model_object_data);
813 813
         }
@@ -815,7 +815,7 @@  discard block
 block discarded – undo
815 815
         try {
816 816
             $new_id = $model->insert($model_object_data);
817 817
             if ($new_id) {
818
-                $old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $id_in_csv ] = $new_id;
818
+                $old_db_to_new_db_mapping[$model->get_this_model_name()][$id_in_csv] = $new_id;
819 819
                 $this->_total_inserts++;
820 820
                 EE_Error::add_success(
821 821
                     sprintf(
@@ -829,7 +829,7 @@  discard block
 block discarded – undo
829 829
                 $this->_total_insert_errors++;
830 830
                 // put the ID used back in there for the error message
831 831
                 if ($model->has_primary_key_field()) {
832
-                    $model_object_data[ $model->primary_key_name() ] = $effective_id;
832
+                    $model_object_data[$model->primary_key_name()] = $effective_id;
833 833
                 }
834 834
                 EE_Error::add_error(
835 835
                     sprintf(
@@ -845,7 +845,7 @@  discard block
 block discarded – undo
845 845
         } catch (EE_Error $e) {
846 846
             $this->_total_insert_errors++;
847 847
             if ($model->has_primary_key_field()) {
848
-                $model_object_data[ $model->primary_key_name() ] = $effective_id;
848
+                $model_object_data[$model->primary_key_name()] = $effective_id;
849 849
             }
850 850
             EE_Error::add_error(
851 851
                 sprintf(
@@ -878,17 +878,17 @@  discard block
 block discarded – undo
878 878
             // one for performing an update, one for everthing else
879 879
             $model_object_data_for_update = $model_object_data;
880 880
             if ($model->has_primary_key_field()) {
881
-                $conditions = array($model->primary_key_name() => $model_object_data[ $model->primary_key_name() ]);
881
+                $conditions = array($model->primary_key_name() => $model_object_data[$model->primary_key_name()]);
882 882
                 // remove the primary key because we shouldn't use it for updating
883
-                unset($model_object_data_for_update[ $model->primary_key_name() ]);
883
+                unset($model_object_data_for_update[$model->primary_key_name()]);
884 884
             } elseif ($model->get_combined_primary_key_fields() > 1) {
885 885
                 $conditions = array();
886 886
                 foreach ($model->get_combined_primary_key_fields() as $key_field) {
887
-                    $conditions[ $key_field->get_name() ] = $model_object_data[ $key_field->get_name() ];
887
+                    $conditions[$key_field->get_name()] = $model_object_data[$key_field->get_name()];
888 888
                 }
889 889
             } else {
890 890
                 $model->primary_key_name(
891
-                );// this shoudl just throw an exception, explaining that we dont have a primary key (or a combine dkey)
891
+                ); // this shoudl just throw an exception, explaining that we dont have a primary key (or a combine dkey)
892 892
             }
893 893
 
894 894
             $success = $model->update($model_object_data_for_update, array($conditions));
@@ -906,15 +906,15 @@  discard block
 block discarded – undo
906 906
                 // we would have last-minute decided to update. So we'd like to know what we updated
907 907
                 // and so we record what record ended up being updated using the mapping
908 908
                 if ($model->has_primary_key_field()) {
909
-                    $new_key_for_mapping = $model_object_data[ $model->primary_key_name() ];
909
+                    $new_key_for_mapping = $model_object_data[$model->primary_key_name()];
910 910
                 } else {
911 911
                     // no primary key just a combined key
912 912
                     $new_key_for_mapping = $model->get_index_primary_key_string($model_object_data);
913 913
                 }
914
-                $old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $id_in_csv ] = $new_key_for_mapping;
914
+                $old_db_to_new_db_mapping[$model->get_this_model_name()][$id_in_csv] = $new_key_for_mapping;
915 915
             } else {
916 916
                 $matched_items = $model->get_all(array($conditions));
917
-                if (! $matched_items) {
917
+                if ( ! $matched_items) {
918 918
                     // no items were matched (so we shouldn't have updated)... but then we should have inserted? what the heck?
919 919
                     $this->_total_update_errors++;
920 920
                     EE_Error::add_error(
@@ -953,7 +953,7 @@  discard block
 block discarded – undo
953 953
                 implode(",", $model_object_data),
954 954
                 $e->getMessage()
955 955
             );
956
-            $debug_message = $basic_message . ' Stack trace: ' . $e->getTraceAsString();
956
+            $debug_message = $basic_message.' Stack trace: '.$e->getTraceAsString();
957 957
             EE_Error::add_error("$basic_message | $debug_message", __FILE__, __FUNCTION__, __LINE__);
958 958
         }
959 959
         return $old_db_to_new_db_mapping;
Please login to merge, or discard this patch.
Indentation   +964 added lines, -964 removed lines patch added patch discarded remove patch
@@ -13,97 +13,97 @@  discard block
 block discarded – undo
13 13
  */
14 14
 class EE_Import implements ResettableInterface
15 15
 {
16
-    const do_insert = 'insert';
17
-    const do_update = 'update';
18
-    const do_nothing = 'nothing';
19
-
20
-
21
-    // instance of the EE_Import object
22
-    private static $_instance;
23
-
24
-    private static $_csv_array = array();
25
-
26
-    /**
27
-     *
28
-     * @var array of model names
29
-     */
30
-    private static $_model_list = array();
31
-
32
-    private static $_columns_to_save = array();
33
-
34
-    protected $_total_inserts = 0;
35
-    protected $_total_updates = 0;
36
-    protected $_total_insert_errors = 0;
37
-    protected $_total_update_errors = 0;
38
-
39
-    /**
40
-     * @var EE_CSV
41
-     * @since 4.10.14.p
42
-     */
43
-    private $EE_CSV;
44
-
45
-
46
-    /**
47
-     *        private constructor to prevent direct creation
48
-     *
49
-     * @Constructor
50
-     * @access private
51
-     * @return void
52
-     */
53
-    private function __construct()
54
-    {
55
-        $this->_total_inserts = 0;
56
-        $this->_total_updates = 0;
57
-        $this->_total_insert_errors = 0;
58
-        $this->_total_update_errors = 0;
59
-    }
60
-
61
-
62
-    /**
63
-     *    @ singleton method used to instantiate class object
64
-     *    @ access public
65
-     *
66
-     * @return EE_Import
67
-     */
68
-    public static function instance()
69
-    {
70
-        // check if class object is instantiated
71
-        if (self::$_instance === null or ! is_object(self::$_instance) or ! (self::$_instance instanceof EE_Import)) {
72
-            self::$_instance = new self();
73
-        }
74
-        return self::$_instance;
75
-    }
76
-
77
-    /**
78
-     * Resets the importer
79
-     *
80
-     * @return EE_Import
81
-     */
82
-    public static function reset()
83
-    {
84
-        self::$_instance = null;
85
-        return self::instance();
86
-    }
87
-
88
-
89
-    /**
90
-     *    @ generates HTML for a file upload input and form
91
-     *    @ access    public
92
-     *
93
-     * @param    string $title  - heading for the form
94
-     * @param    string $intro  - additional text explaing what to do
95
-     * @param    string $page   - EE Admin page to direct form to - in the form "espresso_{pageslug}"
96
-     * @param    string $action - EE Admin page route array "action" that form will direct to
97
-     * @param    string $type   - type of file to import
98
-     *                          @ return    string
99
-     */
100
-    public function upload_form($title, $intro, $form_url, $action, $type)
101
-    {
102
-
103
-        $form_url = EE_Admin_Page::add_query_args_and_nonce(array('action' => $action), $form_url);
104
-
105
-        ob_start();
106
-        ?>
16
+	const do_insert = 'insert';
17
+	const do_update = 'update';
18
+	const do_nothing = 'nothing';
19
+
20
+
21
+	// instance of the EE_Import object
22
+	private static $_instance;
23
+
24
+	private static $_csv_array = array();
25
+
26
+	/**
27
+	 *
28
+	 * @var array of model names
29
+	 */
30
+	private static $_model_list = array();
31
+
32
+	private static $_columns_to_save = array();
33
+
34
+	protected $_total_inserts = 0;
35
+	protected $_total_updates = 0;
36
+	protected $_total_insert_errors = 0;
37
+	protected $_total_update_errors = 0;
38
+
39
+	/**
40
+	 * @var EE_CSV
41
+	 * @since 4.10.14.p
42
+	 */
43
+	private $EE_CSV;
44
+
45
+
46
+	/**
47
+	 *        private constructor to prevent direct creation
48
+	 *
49
+	 * @Constructor
50
+	 * @access private
51
+	 * @return void
52
+	 */
53
+	private function __construct()
54
+	{
55
+		$this->_total_inserts = 0;
56
+		$this->_total_updates = 0;
57
+		$this->_total_insert_errors = 0;
58
+		$this->_total_update_errors = 0;
59
+	}
60
+
61
+
62
+	/**
63
+	 *    @ singleton method used to instantiate class object
64
+	 *    @ access public
65
+	 *
66
+	 * @return EE_Import
67
+	 */
68
+	public static function instance()
69
+	{
70
+		// check if class object is instantiated
71
+		if (self::$_instance === null or ! is_object(self::$_instance) or ! (self::$_instance instanceof EE_Import)) {
72
+			self::$_instance = new self();
73
+		}
74
+		return self::$_instance;
75
+	}
76
+
77
+	/**
78
+	 * Resets the importer
79
+	 *
80
+	 * @return EE_Import
81
+	 */
82
+	public static function reset()
83
+	{
84
+		self::$_instance = null;
85
+		return self::instance();
86
+	}
87
+
88
+
89
+	/**
90
+	 *    @ generates HTML for a file upload input and form
91
+	 *    @ access    public
92
+	 *
93
+	 * @param    string $title  - heading for the form
94
+	 * @param    string $intro  - additional text explaing what to do
95
+	 * @param    string $page   - EE Admin page to direct form to - in the form "espresso_{pageslug}"
96
+	 * @param    string $action - EE Admin page route array "action" that form will direct to
97
+	 * @param    string $type   - type of file to import
98
+	 *                          @ return    string
99
+	 */
100
+	public function upload_form($title, $intro, $form_url, $action, $type)
101
+	{
102
+
103
+		$form_url = EE_Admin_Page::add_query_args_and_nonce(array('action' => $action), $form_url);
104
+
105
+		ob_start();
106
+		?>
107 107
         <div class="ee-upload-form-dv">
108 108
             <h3><?php echo esc_html($title); ?></h3>
109 109
             <p><?php echo esc_html($intro); ?></p>
@@ -119,882 +119,882 @@  discard block
 block discarded – undo
119 119
                 <b><?php esc_html_e('Attention', 'event_espresso'); ?></b><br/>
120 120
                 <?php echo sprintf(esc_html__('Accepts .%s file types only.', 'event_espresso'), $type); ?>
121 121
                 <?php echo esc_html__(
122
-                    'Please only import CSV files exported from Event Espresso, or compatible 3rd-party software.',
123
-                    'event_espresso'
124
-                ); ?>
122
+					'Please only import CSV files exported from Event Espresso, or compatible 3rd-party software.',
123
+					'event_espresso'
124
+				); ?>
125 125
             </p>
126 126
 
127 127
         </div>
128 128
 
129 129
         <?php
130
-        $uploader = ob_get_clean();
131
-        return $uploader;
132
-    }
133
-
134
-
135
-    /**
136
-     * @Import Event Espresso data - some code "borrowed" from event espresso csv_import.php
137
-     * @access public
138
-     * @return boolean success
139
-     */
140
-    public function import()
141
-    {
142
-
143
-        require_once(EE_CLASSES . 'EE_CSV.class.php');
144
-        $this->EE_CSV = EE_CSV::instance();
145
-
146
-        /** @var RequestInterface $request */
147
-        $request = LoaderFactory::getLoader()->getShared(RequestInterface::class);
148
-
149
-        if ($request->requestParamIsSet('import') && $request->requestParamIsSet('csv_submitted')) {
150
-            $files = $request->filesParams();
151
-            switch ($files['file']['error'][0]) {
152
-                case UPLOAD_ERR_OK:
153
-                    $error_msg = false;
154
-                    break;
155
-                case UPLOAD_ERR_INI_SIZE:
156
-                    $error_msg = esc_html__(
157
-                        "'The uploaded file exceeds the upload_max_filesize directive in php.ini.'",
158
-                        "event_espresso"
159
-                    );
160
-                    break;
161
-                case UPLOAD_ERR_FORM_SIZE:
162
-                    $error_msg = esc_html__(
163
-                        'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.',
164
-                        "event_espresso"
165
-                    );
166
-                    break;
167
-                case UPLOAD_ERR_PARTIAL:
168
-                    $error_msg = esc_html__('The uploaded file was only partially uploaded.', "event_espresso");
169
-                    break;
170
-                case UPLOAD_ERR_NO_FILE:
171
-                    $error_msg = esc_html__('No file was uploaded.', "event_espresso");
172
-                    break;
173
-                case UPLOAD_ERR_NO_TMP_DIR:
174
-                    $error_msg = esc_html__('Missing a temporary folder.', "event_espresso");
175
-                    break;
176
-                case UPLOAD_ERR_CANT_WRITE:
177
-                    $error_msg = esc_html__('Failed to write file to disk.', "event_espresso");
178
-                    break;
179
-                case UPLOAD_ERR_EXTENSION:
180
-                    $error_msg = esc_html__('File upload stopped by extension.', "event_espresso");
181
-                    break;
182
-                default:
183
-                    $error_msg = esc_html__(
184
-                        'An unknown error occurred and the file could not be uploaded',
185
-                        "event_espresso"
186
-                    );
187
-                    break;
188
-            }
189
-
190
-            if (! $error_msg) {
191
-                $filename = $files['file']['name'][0];
192
-                $file_ext = substr(strrchr($filename, '.'), 1);
193
-                $file_type = $files['file']['type'][0];
194
-                $temp_file = $files['file']['tmp_name'][0];
195
-                $filesize = $files['file']['size'][0] / 1024;// convert from bytes to KB
196
-
197
-                if ($file_ext == 'csv') {
198
-                    $max_upload = $this->EE_CSV->get_max_upload_size();// max upload size in KB
199
-                    if ($filesize < $max_upload || true) {
200
-                        $wp_upload_dir = str_replace(array('\\', '/'), '/', wp_upload_dir());
201
-                        $path_to_file = $wp_upload_dir['basedir'] . '/espresso/' . $filename;
202
-
203
-                        if (move_uploaded_file($temp_file, $path_to_file)) {
204
-                            // convert csv to array
205
-                            $this->csv_array = $this->EE_CSV->import_csv_to_model_data_array($path_to_file);
206
-
207
-                            $action = $request->getRequestParam('action');
208
-
209
-                            // was data successfully stored in an array?
210
-                            if (is_array($this->csv_array)) {
211
-                                $import_what = str_replace('csv_import_', '', $action);
212
-                                $import_what = str_replace('_', ' ', ucwords($import_what));
213
-                                $processed_data = $this->csv_array;
214
-                                $this->columns_to_save = false;
215
-
216
-                                // if any imports require funky processing, we'll catch them in the switch
217
-                                switch ($action) {
218
-                                    case "import_events":
219
-                                    case "event_list":
220
-                                        $import_what = 'Event Details';
221
-                                        break;
222
-
223
-                                    case 'groupon_import_csv':
224
-                                        $import_what = 'Groupon Codes';
225
-                                        $processed_data = $this->process_groupon_codes();
226
-                                        break;
227
-                                }
228
-                                // save processed codes to db
229
-                                if ($this->save_csv_data_array_to_db($processed_data, $this->columns_to_save)) {
230
-                                    return true;
231
-                                }
232
-                            } else {
233
-                                // no array? must be an error
234
-                                EE_Error::add_error(
235
-                                    sprintf(esc_html__("No file seems to have been uploaded", "event_espresso")),
236
-                                    __FILE__,
237
-                                    __FUNCTION__,
238
-                                    __LINE__
239
-                                );
240
-                                return false;
241
-                            }
242
-                        } else {
243
-                            EE_Error::add_error(
244
-                                sprintf(esc_html__("%s was not successfully uploaded", "event_espresso"), $filename),
245
-                                __FILE__,
246
-                                __FUNCTION__,
247
-                                __LINE__
248
-                            );
249
-                            return false;
250
-                        }
251
-                    } else {
252
-                        EE_Error::add_error(
253
-                            sprintf(
254
-                                esc_html__(
255
-                                    "%s was too large of a file and could not be uploaded. The max filesize is %s' KB.",
256
-                                    "event_espresso"
257
-                                ),
258
-                                $filename,
259
-                                $max_upload
260
-                            ),
261
-                            __FILE__,
262
-                            __FUNCTION__,
263
-                            __LINE__
264
-                        );
265
-                        return false;
266
-                    }
267
-                } else {
268
-                    EE_Error::add_error(
269
-                        sprintf(esc_html__("%s  had an invalid file extension, not uploaded", "event_espresso"), $filename),
270
-                        __FILE__,
271
-                        __FUNCTION__,
272
-                        __LINE__
273
-                    );
274
-                    return false;
275
-                }
276
-            } else {
277
-                EE_Error::add_error($error_msg, __FILE__, __FUNCTION__, __LINE__);
278
-                return false;
279
-            }
280
-        }
281
-        return false;
282
-    }
283
-
284
-
285
-    /**
286
-     *    Given an array of data (usually from a CSV import) attempts to save that data to the db.
287
-     *    If $model_name ISN'T provided, assumes that this is a 3d array, with toplevel keys being model names,
288
-     *    next level being numeric indexes adn each value representing a model object, and the last layer down
289
-     *    being keys of model fields and their proposed values.
290
-     *    If $model_name IS provided, assumes a 2d array of the bottom two layers previously mentioned.
291
-     *    If the CSV data says (in the metadata row) that it's from the SAME database,
292
-     *    we treat the IDs in the CSV as the normal IDs, and try to update those records. However, if those
293
-     *    IDs DON'T exist in the database, they're treated as temporary IDs,
294
-     *    which can used elsewhere to refer to the same object. Once an item
295
-     *    with a temporary ID gets inserted, we record its mapping from temporary
296
-     *    ID to real ID, and use the real ID in place of the temporary ID
297
-     *    when that temporary ID was used as a foreign key.
298
-     *    If the CSV data says (in the metadata again) that it's from a DIFFERENT database,
299
-     *    we treat all the IDs in the CSV as temporary ID- eg, if the CSV specifies an event with
300
-     *    ID 1, and the database already has an event with ID 1, we assume that's just a coincidence,
301
-     *    and insert a new event, and map it's temporary ID of 1 over to its new real ID.
302
-     *    An important exception are non-auto-increment primary keys. If one entry in the
303
-     *    CSV file has the same ID as one in the DB, we assume they are meant to be
304
-     *    the same item, and instead update the item in the DB with that same ID.
305
-     *    Also note, we remember the mappings permanently. So the 2nd, 3rd, and 10000th
306
-     *    time you import a CSV from a different site, we remember their mappings, and
307
-     * will try to update the item in the DB instead of inserting another item (eg
308
-     * if we previously imported an event with temporary ID 1, and then it got a
309
-     * real ID of 123, we remember that. So the next time we import an event with
310
-     * temporary ID, from the same site, we know that it's real ID is 123, and will
311
-     * update that event, instead of adding a new event).
312
-     *
313
-     * @access public
314
-     * @param array $csv_data_array - the array containing the csv data produced from
315
-     *                              EE_CSV::import_csv_to_model_data_array()
316
-     * @param array $fields_to_save - an array containing the csv column names as keys with the corresponding db table
317
-     *                              fields they will be saved to
318
-     * @return TRUE on success, FALSE on fail
319
-     * @throws \EE_Error
320
-     */
321
-    public function save_csv_data_array_to_db($csv_data_array, $model_name = false)
322
-    {
323
-        $success = false;
324
-        $error = false;
325
-        // whther to treat this import as if it's data froma different database or not
326
-        // ie, if it IS from a different database, ignore foreign keys whihf
327
-        $export_from_site_a_to_b = true;
328
-        // first level of array is not table information but a table name was passed to the function
329
-        // array is only two levels deep, so let's fix that by adding a level, else the next steps will fail
330
-        if ($model_name) {
331
-            $csv_data_array = array($csv_data_array);
332
-        }
333
-        // begin looking through the $csv_data_array, expecting the toplevel key to be the model's name...
334
-        $old_site_url = 'none-specified';
335
-        // hanlde metadata
336
-        if (isset($csv_data_array[ EE_CSV::metadata_header ])) {
337
-            $csv_metadata = array_shift($csv_data_array[ EE_CSV::metadata_header ]);
338
-            // ok so its metadata, dont try to save it to ehte db obviously...
339
-            if (isset($csv_metadata['site_url']) && $csv_metadata['site_url'] == site_url()) {
340
-                EE_Error::add_attention(
341
-                    sprintf(
342
-                        esc_html__(
343
-                            "CSV Data appears to be from the same database, so attempting to update data",
344
-                            "event_espresso"
345
-                        )
346
-                    )
347
-                );
348
-                $export_from_site_a_to_b = false;
349
-            } else {
350
-                $old_site_url = isset($csv_metadata['site_url']) ? $csv_metadata['site_url'] : $old_site_url;
351
-                EE_Error::add_attention(
352
-                    sprintf(
353
-                        esc_html__(
354
-                            "CSV Data appears to be from a different database (%s instead of %s), so we assume IDs in the CSV data DO NOT correspond to IDs in this database",
355
-                            "event_espresso"
356
-                        ),
357
-                        $old_site_url,
358
-                        site_url()
359
-                    )
360
-                );
361
-            };
362
-            unset($csv_data_array[ EE_CSV::metadata_header ]);
363
-        }
364
-        /**
365
-         * @var $old_db_to_new_db_mapping 2d array: toplevel keys being model names, bottom-level keys being the original key, and
366
-         * the value will be the newly-inserted ID.
367
-         * If we have already imported data from the same website via CSV, it shoudl be kept in this wp option
368
-         */
369
-        $old_db_to_new_db_mapping = get_option('ee_id_mapping_from' . sanitize_title($old_site_url), array());
370
-        if ($old_db_to_new_db_mapping) {
371
-            EE_Error::add_attention(
372
-                sprintf(
373
-                    esc_html__(
374
-                        "We noticed you have imported data via CSV from %s before. Because of this, IDs in your CSV have been mapped to their new IDs in %s",
375
-                        "event_espresso"
376
-                    ),
377
-                    $old_site_url,
378
-                    site_url()
379
-                )
380
-            );
381
-        }
382
-        $old_db_to_new_db_mapping = $this->save_data_rows_to_db(
383
-            $csv_data_array,
384
-            $export_from_site_a_to_b,
385
-            $old_db_to_new_db_mapping
386
-        );
387
-
388
-        // save the mapping from old db to new db in case they try re-importing the same data from the same website again
389
-        update_option('ee_id_mapping_from' . sanitize_title($old_site_url), $old_db_to_new_db_mapping);
390
-
391
-        if ($this->_total_updates > 0) {
392
-            EE_Error::add_success(
393
-                sprintf(
394
-                    esc_html__("%s existing records in the database were updated.", "event_espresso"),
395
-                    $this->_total_updates
396
-                )
397
-            );
398
-            $success = true;
399
-        }
400
-        if ($this->_total_inserts > 0) {
401
-            EE_Error::add_success(
402
-                sprintf(esc_html__("%s new records were added to the database.", "event_espresso"), $this->_total_inserts)
403
-            );
404
-            $success = true;
405
-        }
406
-
407
-        if ($this->_total_update_errors > 0) {
408
-            EE_Error::add_error(
409
-                sprintf(
410
-                    esc_html__(
411
-                        "'One or more errors occurred, and a total of %s existing records in the database were <strong>not</strong> updated.'",
412
-                        "event_espresso"
413
-                    ),
414
-                    $this->_total_update_errors
415
-                ),
416
-                __FILE__,
417
-                __FUNCTION__,
418
-                __LINE__
419
-            );
420
-            $error = true;
421
-        }
422
-        if ($this->_total_insert_errors > 0) {
423
-            EE_Error::add_error(
424
-                sprintf(
425
-                    esc_html__(
426
-                        "One or more errors occurred, and a total of %s new records were <strong>not</strong> added to the database.'",
427
-                        "event_espresso"
428
-                    ),
429
-                    $this->_total_insert_errors
430
-                ),
431
-                __FILE__,
432
-                __FUNCTION__,
433
-                __LINE__
434
-            );
435
-            $error = true;
436
-        }
437
-
438
-        // lastly, we need to update the datetime and ticket sold amounts
439
-        // as those may have been affected by this
440
-        EEM_Ticket::instance()->update_tickets_sold(EEM_Ticket::instance()->get_all());
441
-
442
-        // if there was at least one success and absolutely no errors
443
-        if ($success && ! $error) {
444
-            return true;
445
-        } else {
446
-            return false;
447
-        }
448
-    }
449
-
450
-
451
-    /**
452
-     * Processes the array of data, given the knowledge that it's from the same database or a different one,
453
-     * and the mapping from temporary IDs to real IDs.
454
-     * If the data is from a different database, we treat the primary keys and their corresponding
455
-     * foreign keys as "temp Ids", basically identifiers that get mapped to real primary keys
456
-     * in the real target database. As items are inserted, their temporary primary keys
457
-     * are mapped to the real IDs in the target database. Also, before doing any update or
458
-     * insert, we replace all the temp ID which are foreign keys with their mapped real IDs.
459
-     * An exception: string primary keys are treated as real IDs, or else we'd need to
460
-     * dynamically generate new string primary keys which would be very awkard for the country table etc.
461
-     * Also, models with no primary key are strange too. We combine use their primar key INDEX (a
462
-     * combination of fields) to create a unique string identifying the row and store
463
-     * those in the mapping.
464
-     *
465
-     * If the data is from the same database, we usually treat primary keys as real IDs.
466
-     * An exception is if there is nothing in the database for that ID. If that's the case,
467
-     * we need to insert a new row for that ID, and then map from the non-existent ID
468
-     * to the newly-inserted real ID.
469
-     *
470
-     * @param type $csv_data_array
471
-     * @param type $export_from_site_a_to_b
472
-     * @param type $old_db_to_new_db_mapping
473
-     * @return array updated $old_db_to_new_db_mapping
474
-     */
475
-    public function save_data_rows_to_db($csv_data_array, $export_from_site_a_to_b, $old_db_to_new_db_mapping)
476
-    {
477
-        foreach ($csv_data_array as $model_name_in_csv_data => $model_data_from_import) {
478
-            // now check that assumption was correct. If
479
-            if (EE_Registry::instance()->is_model_name($model_name_in_csv_data)) {
480
-                $model_name = $model_name_in_csv_data;
481
-            } else {
482
-                // no table info in the array and no table name passed to the function?? FAIL
483
-                EE_Error::add_error(
484
-                    esc_html__(
485
-                        'No table information was specified and/or found, therefore the import could not be completed',
486
-                        'event_espresso'
487
-                    ),
488
-                    __FILE__,
489
-                    __FUNCTION__,
490
-                    __LINE__
491
-                );
492
-                return false;
493
-            }
494
-            /* @var $model EEM_Base */
495
-            $model = EE_Registry::instance()->load_model($model_name);
496
-
497
-            // so without further ado, scanning all the data provided for primary keys and their inital values
498
-            foreach ($model_data_from_import as $model_object_data) {
499
-                // before we do ANYTHING, make sure the csv row wasn't just completely blank
500
-                $row_is_completely_empty = true;
501
-                foreach ($model_object_data as $field) {
502
-                    if ($field) {
503
-                        $row_is_completely_empty = false;
504
-                    }
505
-                }
506
-                if ($row_is_completely_empty) {
507
-                    continue;
508
-                }
509
-                // find the PK in the row of data (or a combined key if
510
-                // there is no primary key)
511
-                if ($model->has_primary_key_field()) {
512
-                    $id_in_csv = $model_object_data[ $model->primary_key_name() ];
513
-                } else {
514
-                    $id_in_csv = $model->get_index_primary_key_string($model_object_data);
515
-                }
516
-
517
-
518
-                $model_object_data = $this->_replace_temp_ids_with_mappings(
519
-                    $model_object_data,
520
-                    $model,
521
-                    $old_db_to_new_db_mapping,
522
-                    $export_from_site_a_to_b
523
-                );
524
-                // now we need to decide if we're going to add a new model object given the $model_object_data,
525
-                // or just update.
526
-                if ($export_from_site_a_to_b) {
527
-                    $what_to_do = $this->_decide_whether_to_insert_or_update_given_data_from_other_db(
528
-                        $id_in_csv,
529
-                        $model_object_data,
530
-                        $model,
531
-                        $old_db_to_new_db_mapping
532
-                    );
533
-                } else {// this is just a re-import
534
-                    $what_to_do = $this->_decide_whether_to_insert_or_update_given_data_from_same_db(
535
-                        $id_in_csv,
536
-                        $model_object_data,
537
-                        $model,
538
-                        $old_db_to_new_db_mapping
539
-                    );
540
-                }
541
-                if ($what_to_do == self::do_nothing) {
542
-                    continue;
543
-                }
544
-
545
-                // double-check we actually want to insert, if that's what we're planning
546
-                // based on whether this item would be unique in the DB or not
547
-                if ($what_to_do == self::do_insert) {
548
-                    // we're supposed to be inserting. But wait, will this thing
549
-                    // be acceptable if inserted?
550
-                    $conflicting = $model->get_one_conflicting($model_object_data, false);
551
-                    if ($conflicting) {
552
-                        // ok, this item would conflict if inserted. Just update the item that it conflicts with.
553
-                        $what_to_do = self::do_update;
554
-                        // and if this model has a primary key, remember its mapping
555
-                        if ($model->has_primary_key_field()) {
556
-                            $old_db_to_new_db_mapping[ $model_name ][ $id_in_csv ] = $conflicting->ID();
557
-                            $model_object_data[ $model->primary_key_name() ] = $conflicting->ID();
558
-                        } else {
559
-                            // we want to update this conflicting item, instead of inserting a conflicting item
560
-                            // so we need to make sure they match entirely (its possible that they only conflicted on one field, but we need them to match on other fields
561
-                            // for the WHERE conditions in the update). At the time of this comment, there were no models like this
562
-                            foreach ($model->get_combined_primary_key_fields() as $key_field) {
563
-                                $model_object_data[ $key_field->get_name() ] = $conflicting->get(
564
-                                    $key_field->get_name()
565
-                                );
566
-                            }
567
-                        }
568
-                    }
569
-                }
570
-                if ($what_to_do == self::do_insert) {
571
-                    $old_db_to_new_db_mapping = $this->_insert_from_data_array(
572
-                        $id_in_csv,
573
-                        $model_object_data,
574
-                        $model,
575
-                        $old_db_to_new_db_mapping
576
-                    );
577
-                } elseif ($what_to_do == self::do_update) {
578
-                    $old_db_to_new_db_mapping = $this->_update_from_data_array(
579
-                        $id_in_csv,
580
-                        $model_object_data,
581
-                        $model,
582
-                        $old_db_to_new_db_mapping
583
-                    );
584
-                } else {
585
-                    throw new EE_Error(
586
-                        sprintf(
587
-                            esc_html__(
588
-                                'Programming error. We shoudl be inserting or updating, but instead we are being told to "%s", whifh is invalid',
589
-                                'event_espresso'
590
-                            ),
591
-                            $what_to_do
592
-                        )
593
-                    );
594
-                }
595
-            }
596
-        }
597
-        return $old_db_to_new_db_mapping;
598
-    }
599
-
600
-
601
-    /**
602
-     * Decides whether or not to insert, given that this data is from another database.
603
-     * So, if the primary key of this $model_object_data already exists in the database,
604
-     * it's just a coincidence and we should still insert. The only time we should
605
-     * update is when we know what it maps to, or there's something that would
606
-     * conflict (and we should instead just update that conflicting thing)
607
-     *
608
-     * @param string   $id_in_csv
609
-     * @param array    $model_object_data        by reference so it can be modified
610
-     * @param EEM_Base $model
611
-     * @param array    $old_db_to_new_db_mapping by reference so it can be modified
612
-     * @return string one of the consts on this class that starts with do_*
613
-     */
614
-    protected function _decide_whether_to_insert_or_update_given_data_from_other_db(
615
-        $id_in_csv,
616
-        $model_object_data,
617
-        $model,
618
-        $old_db_to_new_db_mapping
619
-    ) {
620
-        $model_name = $model->get_this_model_name();
621
-        // if it's a site-to-site export-and-import, see if this modelobject's id
622
-        // in the old data that we know of
623
-        if (isset($old_db_to_new_db_mapping[ $model_name ][ $id_in_csv ])) {
624
-            return self::do_update;
625
-        } else {
626
-            return self::do_insert;
627
-        }
628
-    }
629
-
630
-    /**
631
-     * If this thing basically already exists in the database, we want to update it;
632
-     * otherwise insert it (ie, someone tweaked the CSV file, or the item was
633
-     * deleted in the database so it should be re-inserted)
634
-     *
635
-     * @param type     $id_in_csv
636
-     * @param type     $model_object_data
637
-     * @param EEM_Base $model
638
-     * @param type     $old_db_to_new_db_mapping
639
-     * @return
640
-     */
641
-    protected function _decide_whether_to_insert_or_update_given_data_from_same_db(
642
-        $id_in_csv,
643
-        $model_object_data,
644
-        $model
645
-    ) {
646
-        // in this case, check if this thing ACTUALLY exists in the database
647
-        if ($model->get_one_conflicting($model_object_data)) {
648
-            return self::do_update;
649
-        } else {
650
-            return self::do_insert;
651
-        }
652
-    }
653
-
654
-    /**
655
-     * Using the $old_db_to_new_db_mapping array, replaces all the temporary IDs
656
-     * with their mapped real IDs. Eg, if importing from site A to B, the mapping
657
-     * file may indicate that the ID "my_event_id" maps to an actual event ID of 123.
658
-     * So this function searches for any event temp Ids called "my_event_id" and
659
-     * replaces them with 123.
660
-     * Also, if there is no temp ID for the INT foreign keys from another database,
661
-     * replaces them with 0 or the field's default.
662
-     *
663
-     * @param type     $model_object_data
664
-     * @param EEM_Base $model
665
-     * @param type     $old_db_to_new_db_mapping
666
-     * @param boolean  $export_from_site_a_to_b
667
-     * @return array updated model object data with temp IDs removed
668
-     */
669
-    protected function _replace_temp_ids_with_mappings(
670
-        $model_object_data,
671
-        $model,
672
-        $old_db_to_new_db_mapping,
673
-        $export_from_site_a_to_b
674
-    ) {
675
-        // if this model object's primary key is in the mapping, replace it
676
-        if (
677
-            $model->has_primary_key_field() &&
678
-            $model->get_primary_key_field()->is_auto_increment() &&
679
-            isset($old_db_to_new_db_mapping[ $model->get_this_model_name() ]) &&
680
-            isset(
681
-                $old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $model_object_data[ $model->primary_key_name() ] ]
682
-            )
683
-        ) {
684
-            $model_object_data[ $model->primary_key_name() ] = $old_db_to_new_db_mapping[ $model->get_this_model_name(
685
-            ) ][ $model_object_data[ $model->primary_key_name() ] ];
686
-        }
687
-
688
-        try {
689
-            $model_name_field = $model->get_field_containing_related_model_name();
690
-            $models_pointed_to_by_model_name_field = $model_name_field->get_model_names_pointed_to();
691
-        } catch (EE_Error $e) {
692
-            $model_name_field = null;
693
-            $models_pointed_to_by_model_name_field = array();
694
-        }
695
-        foreach ($model->field_settings(true) as $field_obj) {
696
-            if ($field_obj instanceof EE_Foreign_Key_Int_Field) {
697
-                $models_pointed_to = $field_obj->get_model_names_pointed_to();
698
-                $found_a_mapping = false;
699
-                foreach ($models_pointed_to as $model_pointed_to_by_fk) {
700
-                    if ($model_name_field) {
701
-                        $value_of_model_name_field = $model_object_data[ $model_name_field->get_name() ];
702
-                        if ($value_of_model_name_field == $model_pointed_to_by_fk) {
703
-                            $model_object_data[ $field_obj->get_name() ] = $this->_find_mapping_in(
704
-                                $model_object_data[ $field_obj->get_name() ],
705
-                                $model_pointed_to_by_fk,
706
-                                $old_db_to_new_db_mapping,
707
-                                $export_from_site_a_to_b
708
-                            );
709
-                            $found_a_mapping = true;
710
-                            break;
711
-                        }
712
-                    } else {
713
-                        $model_object_data[ $field_obj->get_name() ] = $this->_find_mapping_in(
714
-                            $model_object_data[ $field_obj->get_name() ],
715
-                            $model_pointed_to_by_fk,
716
-                            $old_db_to_new_db_mapping,
717
-                            $export_from_site_a_to_b
718
-                        );
719
-                        $found_a_mapping = true;
720
-                    }
721
-                    // once we've found a mapping for this field no need to continue
722
-                    if ($found_a_mapping) {
723
-                        break;
724
-                    }
725
-                }
726
-            } else {
727
-                // it's a string foreign key (which we leave alone, because those are things
728
-                // like country names, which we'd really rather not make 2 USAs etc (we'd actually
729
-                // prefer to just update one)
730
-                // or it's just a regular value that ought to be replaced
731
-            }
732
-        }
733
-        //
734
-        if ($model instanceof EEM_Term_Taxonomy) {
735
-            $model_object_data = $this->_handle_split_term_ids($model_object_data);
736
-        }
737
-        return $model_object_data;
738
-    }
739
-
740
-    /**
741
-     * If the data was exported PRE-4.2, but then imported POST-4.2, then the term_id
742
-     * this term-taxonomy refers to may be out-of-date so we need to update it.
743
-     * see https://make.wordpress.org/core/2015/02/16/taxonomy-term-splitting-in-4-2-a-developer-guide/
744
-     *
745
-     * @param type $model_object_data
746
-     * @return array new model object data
747
-     */
748
-    protected function _handle_split_term_ids($model_object_data)
749
-    {
750
-        if (
751
-            isset($model_object_data['term_id'])
752
-            && isset($model_object_data['taxonomy'])
753
-            && apply_filters(
754
-                'FHEE__EE_Import__handle_split_term_ids__function_exists',
755
-                function_exists('wp_get_split_term'),
756
-                $model_object_data
757
-            )
758
-        ) {
759
-            $new_term_id = wp_get_split_term($model_object_data['term_id'], $model_object_data['taxonomy']);
760
-            if ($new_term_id) {
761
-                $model_object_data['term_id'] = $new_term_id;
762
-            }
763
-        }
764
-        return $model_object_data;
765
-    }
766
-
767
-    /**
768
-     * Given the object's ID and its model's name, find it int he mapping data,
769
-     * bearing in mind where it came from
770
-     *
771
-     * @param type   $object_id
772
-     * @param string $model_name
773
-     * @param array  $old_db_to_new_db_mapping
774
-     * @param type   $export_from_site_a_to_b
775
-     * @return int
776
-     */
777
-    protected function _find_mapping_in($object_id, $model_name, $old_db_to_new_db_mapping, $export_from_site_a_to_b)
778
-    {
779
-        if (isset($old_db_to_new_db_mapping[ $model_name ][ $object_id ])) {
780
-            return $old_db_to_new_db_mapping[ $model_name ][ $object_id ];
781
-        } elseif ($object_id == '0' || $object_id == '') {
782
-            // leave as-is
783
-            return $object_id;
784
-        } elseif ($export_from_site_a_to_b) {
785
-            // we couldn't find a mapping for this, and it's from a different site,
786
-            // so blank it out
787
-            return null;
788
-        } elseif (! $export_from_site_a_to_b) {
789
-            // we coudln't find a mapping for this, but it's from thsi DB anyway
790
-            // so let's just leave it as-is
791
-            return $object_id;
792
-        }
793
-    }
794
-
795
-    /**
796
-     *
797
-     * @param type     $id_in_csv
798
-     * @param type     $model_object_data
799
-     * @param EEM_Base $model
800
-     * @param type     $old_db_to_new_db_mapping
801
-     * @return array updated $old_db_to_new_db_mapping
802
-     */
803
-    protected function _insert_from_data_array($id_in_csv, $model_object_data, $model, $old_db_to_new_db_mapping)
804
-    {
805
-        // remove the primary key, if there is one (we don't want it for inserts OR updates)
806
-        // we'll put it back in if we need it
807
-        if ($model->has_primary_key_field() && $model->get_primary_key_field()->is_auto_increment()) {
808
-            $effective_id = $model_object_data[ $model->primary_key_name() ];
809
-            unset($model_object_data[ $model->primary_key_name() ]);
810
-        } else {
811
-            $effective_id = $model->get_index_primary_key_string($model_object_data);
812
-        }
813
-        // the model takes care of validating the CSV's input
814
-        try {
815
-            $new_id = $model->insert($model_object_data);
816
-            if ($new_id) {
817
-                $old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $id_in_csv ] = $new_id;
818
-                $this->_total_inserts++;
819
-                EE_Error::add_success(
820
-                    sprintf(
821
-                        esc_html__("Successfully added new %s (with id %s) with csv data %s", "event_espresso"),
822
-                        $model->get_this_model_name(),
823
-                        $new_id,
824
-                        implode(",", $model_object_data)
825
-                    )
826
-                );
827
-            } else {
828
-                $this->_total_insert_errors++;
829
-                // put the ID used back in there for the error message
830
-                if ($model->has_primary_key_field()) {
831
-                    $model_object_data[ $model->primary_key_name() ] = $effective_id;
832
-                }
833
-                EE_Error::add_error(
834
-                    sprintf(
835
-                        esc_html__("Could not insert new %s with the csv data: %s", "event_espresso"),
836
-                        $model->get_this_model_name(),
837
-                        http_build_query($model_object_data)
838
-                    ),
839
-                    __FILE__,
840
-                    __FUNCTION__,
841
-                    __LINE__
842
-                );
843
-            }
844
-        } catch (EE_Error $e) {
845
-            $this->_total_insert_errors++;
846
-            if ($model->has_primary_key_field()) {
847
-                $model_object_data[ $model->primary_key_name() ] = $effective_id;
848
-            }
849
-            EE_Error::add_error(
850
-                sprintf(
851
-                    esc_html__("Could not insert new %s with the csv data: %s because %s", "event_espresso"),
852
-                    $model->get_this_model_name(),
853
-                    implode(",", $model_object_data),
854
-                    $e->getMessage()
855
-                ),
856
-                __FILE__,
857
-                __FUNCTION__,
858
-                __LINE__
859
-            );
860
-        }
861
-        return $old_db_to_new_db_mapping;
862
-    }
863
-
864
-    /**
865
-     * Given the model object data, finds the row to update and updates it
866
-     *
867
-     * @param string|int $id_in_csv
868
-     * @param array      $model_object_data
869
-     * @param EEM_Base   $model
870
-     * @param array      $old_db_to_new_db_mapping
871
-     * @return array updated $old_db_to_new_db_mapping
872
-     */
873
-    protected function _update_from_data_array($id_in_csv, $model_object_data, $model, $old_db_to_new_db_mapping)
874
-    {
875
-        try {
876
-            // let's keep two copies of the model object data:
877
-            // one for performing an update, one for everthing else
878
-            $model_object_data_for_update = $model_object_data;
879
-            if ($model->has_primary_key_field()) {
880
-                $conditions = array($model->primary_key_name() => $model_object_data[ $model->primary_key_name() ]);
881
-                // remove the primary key because we shouldn't use it for updating
882
-                unset($model_object_data_for_update[ $model->primary_key_name() ]);
883
-            } elseif ($model->get_combined_primary_key_fields() > 1) {
884
-                $conditions = array();
885
-                foreach ($model->get_combined_primary_key_fields() as $key_field) {
886
-                    $conditions[ $key_field->get_name() ] = $model_object_data[ $key_field->get_name() ];
887
-                }
888
-            } else {
889
-                $model->primary_key_name(
890
-                );// this shoudl just throw an exception, explaining that we dont have a primary key (or a combine dkey)
891
-            }
892
-
893
-            $success = $model->update($model_object_data_for_update, array($conditions));
894
-            if ($success) {
895
-                $this->_total_updates++;
896
-                EE_Error::add_success(
897
-                    sprintf(
898
-                        esc_html__("Successfully updated %s with csv data %s", "event_espresso"),
899
-                        $model->get_this_model_name(),
900
-                        implode(",", $model_object_data_for_update)
901
-                    )
902
-                );
903
-                // we should still record the mapping even though it was an update
904
-                // because if we were going to insert somethign but it was going to conflict
905
-                // we would have last-minute decided to update. So we'd like to know what we updated
906
-                // and so we record what record ended up being updated using the mapping
907
-                if ($model->has_primary_key_field()) {
908
-                    $new_key_for_mapping = $model_object_data[ $model->primary_key_name() ];
909
-                } else {
910
-                    // no primary key just a combined key
911
-                    $new_key_for_mapping = $model->get_index_primary_key_string($model_object_data);
912
-                }
913
-                $old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $id_in_csv ] = $new_key_for_mapping;
914
-            } else {
915
-                $matched_items = $model->get_all(array($conditions));
916
-                if (! $matched_items) {
917
-                    // no items were matched (so we shouldn't have updated)... but then we should have inserted? what the heck?
918
-                    $this->_total_update_errors++;
919
-                    EE_Error::add_error(
920
-                        sprintf(
921
-                            esc_html__(
922
-                                "Could not update %s with the csv data: '%s' for an unknown reason (using WHERE conditions %s)",
923
-                                "event_espresso"
924
-                            ),
925
-                            $model->get_this_model_name(),
926
-                            http_build_query($model_object_data),
927
-                            http_build_query($conditions)
928
-                        ),
929
-                        __FILE__,
930
-                        __FUNCTION__,
931
-                        __LINE__
932
-                    );
933
-                } else {
934
-                    $this->_total_updates++;
935
-                    EE_Error::add_success(
936
-                        sprintf(
937
-                            esc_html__(
938
-                                "%s with csv data '%s' was found in the database and didn't need updating because all the data is identical.",
939
-                                "event_espresso"
940
-                            ),
941
-                            $model->get_this_model_name(),
942
-                            implode(",", $model_object_data)
943
-                        )
944
-                    );
945
-                }
946
-            }
947
-        } catch (EE_Error $e) {
948
-            $this->_total_update_errors++;
949
-            $basic_message = sprintf(
950
-                esc_html__("Could not update %s with the csv data: %s because %s", "event_espresso"),
951
-                $model->get_this_model_name(),
952
-                implode(",", $model_object_data),
953
-                $e->getMessage()
954
-            );
955
-            $debug_message = $basic_message . ' Stack trace: ' . $e->getTraceAsString();
956
-            EE_Error::add_error("$basic_message | $debug_message", __FILE__, __FUNCTION__, __LINE__);
957
-        }
958
-        return $old_db_to_new_db_mapping;
959
-    }
960
-
961
-    /**
962
-     * Gets the number of inserts performed since importer was instantiated or reset
963
-     *
964
-     * @return int
965
-     */
966
-    public function get_total_inserts()
967
-    {
968
-        return $this->_total_inserts;
969
-    }
970
-
971
-    /**
972
-     *  Gets the number of insert errors since importer was instantiated or reset
973
-     *
974
-     * @return int
975
-     */
976
-    public function get_total_insert_errors()
977
-    {
978
-        return $this->_total_insert_errors;
979
-    }
980
-
981
-    /**
982
-     *  Gets the number of updates performed since importer was instantiated or reset
983
-     *
984
-     * @return int
985
-     */
986
-    public function get_total_updates()
987
-    {
988
-        return $this->_total_updates;
989
-    }
990
-
991
-    /**
992
-     *  Gets the number of update errors since importer was instantiated or reset
993
-     *
994
-     * @return int
995
-     */
996
-    public function get_total_update_errors()
997
-    {
998
-        return $this->_total_update_errors;
999
-    }
130
+		$uploader = ob_get_clean();
131
+		return $uploader;
132
+	}
133
+
134
+
135
+	/**
136
+	 * @Import Event Espresso data - some code "borrowed" from event espresso csv_import.php
137
+	 * @access public
138
+	 * @return boolean success
139
+	 */
140
+	public function import()
141
+	{
142
+
143
+		require_once(EE_CLASSES . 'EE_CSV.class.php');
144
+		$this->EE_CSV = EE_CSV::instance();
145
+
146
+		/** @var RequestInterface $request */
147
+		$request = LoaderFactory::getLoader()->getShared(RequestInterface::class);
148
+
149
+		if ($request->requestParamIsSet('import') && $request->requestParamIsSet('csv_submitted')) {
150
+			$files = $request->filesParams();
151
+			switch ($files['file']['error'][0]) {
152
+				case UPLOAD_ERR_OK:
153
+					$error_msg = false;
154
+					break;
155
+				case UPLOAD_ERR_INI_SIZE:
156
+					$error_msg = esc_html__(
157
+						"'The uploaded file exceeds the upload_max_filesize directive in php.ini.'",
158
+						"event_espresso"
159
+					);
160
+					break;
161
+				case UPLOAD_ERR_FORM_SIZE:
162
+					$error_msg = esc_html__(
163
+						'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.',
164
+						"event_espresso"
165
+					);
166
+					break;
167
+				case UPLOAD_ERR_PARTIAL:
168
+					$error_msg = esc_html__('The uploaded file was only partially uploaded.', "event_espresso");
169
+					break;
170
+				case UPLOAD_ERR_NO_FILE:
171
+					$error_msg = esc_html__('No file was uploaded.', "event_espresso");
172
+					break;
173
+				case UPLOAD_ERR_NO_TMP_DIR:
174
+					$error_msg = esc_html__('Missing a temporary folder.', "event_espresso");
175
+					break;
176
+				case UPLOAD_ERR_CANT_WRITE:
177
+					$error_msg = esc_html__('Failed to write file to disk.', "event_espresso");
178
+					break;
179
+				case UPLOAD_ERR_EXTENSION:
180
+					$error_msg = esc_html__('File upload stopped by extension.', "event_espresso");
181
+					break;
182
+				default:
183
+					$error_msg = esc_html__(
184
+						'An unknown error occurred and the file could not be uploaded',
185
+						"event_espresso"
186
+					);
187
+					break;
188
+			}
189
+
190
+			if (! $error_msg) {
191
+				$filename = $files['file']['name'][0];
192
+				$file_ext = substr(strrchr($filename, '.'), 1);
193
+				$file_type = $files['file']['type'][0];
194
+				$temp_file = $files['file']['tmp_name'][0];
195
+				$filesize = $files['file']['size'][0] / 1024;// convert from bytes to KB
196
+
197
+				if ($file_ext == 'csv') {
198
+					$max_upload = $this->EE_CSV->get_max_upload_size();// max upload size in KB
199
+					if ($filesize < $max_upload || true) {
200
+						$wp_upload_dir = str_replace(array('\\', '/'), '/', wp_upload_dir());
201
+						$path_to_file = $wp_upload_dir['basedir'] . '/espresso/' . $filename;
202
+
203
+						if (move_uploaded_file($temp_file, $path_to_file)) {
204
+							// convert csv to array
205
+							$this->csv_array = $this->EE_CSV->import_csv_to_model_data_array($path_to_file);
206
+
207
+							$action = $request->getRequestParam('action');
208
+
209
+							// was data successfully stored in an array?
210
+							if (is_array($this->csv_array)) {
211
+								$import_what = str_replace('csv_import_', '', $action);
212
+								$import_what = str_replace('_', ' ', ucwords($import_what));
213
+								$processed_data = $this->csv_array;
214
+								$this->columns_to_save = false;
215
+
216
+								// if any imports require funky processing, we'll catch them in the switch
217
+								switch ($action) {
218
+									case "import_events":
219
+									case "event_list":
220
+										$import_what = 'Event Details';
221
+										break;
222
+
223
+									case 'groupon_import_csv':
224
+										$import_what = 'Groupon Codes';
225
+										$processed_data = $this->process_groupon_codes();
226
+										break;
227
+								}
228
+								// save processed codes to db
229
+								if ($this->save_csv_data_array_to_db($processed_data, $this->columns_to_save)) {
230
+									return true;
231
+								}
232
+							} else {
233
+								// no array? must be an error
234
+								EE_Error::add_error(
235
+									sprintf(esc_html__("No file seems to have been uploaded", "event_espresso")),
236
+									__FILE__,
237
+									__FUNCTION__,
238
+									__LINE__
239
+								);
240
+								return false;
241
+							}
242
+						} else {
243
+							EE_Error::add_error(
244
+								sprintf(esc_html__("%s was not successfully uploaded", "event_espresso"), $filename),
245
+								__FILE__,
246
+								__FUNCTION__,
247
+								__LINE__
248
+							);
249
+							return false;
250
+						}
251
+					} else {
252
+						EE_Error::add_error(
253
+							sprintf(
254
+								esc_html__(
255
+									"%s was too large of a file and could not be uploaded. The max filesize is %s' KB.",
256
+									"event_espresso"
257
+								),
258
+								$filename,
259
+								$max_upload
260
+							),
261
+							__FILE__,
262
+							__FUNCTION__,
263
+							__LINE__
264
+						);
265
+						return false;
266
+					}
267
+				} else {
268
+					EE_Error::add_error(
269
+						sprintf(esc_html__("%s  had an invalid file extension, not uploaded", "event_espresso"), $filename),
270
+						__FILE__,
271
+						__FUNCTION__,
272
+						__LINE__
273
+					);
274
+					return false;
275
+				}
276
+			} else {
277
+				EE_Error::add_error($error_msg, __FILE__, __FUNCTION__, __LINE__);
278
+				return false;
279
+			}
280
+		}
281
+		return false;
282
+	}
283
+
284
+
285
+	/**
286
+	 *    Given an array of data (usually from a CSV import) attempts to save that data to the db.
287
+	 *    If $model_name ISN'T provided, assumes that this is a 3d array, with toplevel keys being model names,
288
+	 *    next level being numeric indexes adn each value representing a model object, and the last layer down
289
+	 *    being keys of model fields and their proposed values.
290
+	 *    If $model_name IS provided, assumes a 2d array of the bottom two layers previously mentioned.
291
+	 *    If the CSV data says (in the metadata row) that it's from the SAME database,
292
+	 *    we treat the IDs in the CSV as the normal IDs, and try to update those records. However, if those
293
+	 *    IDs DON'T exist in the database, they're treated as temporary IDs,
294
+	 *    which can used elsewhere to refer to the same object. Once an item
295
+	 *    with a temporary ID gets inserted, we record its mapping from temporary
296
+	 *    ID to real ID, and use the real ID in place of the temporary ID
297
+	 *    when that temporary ID was used as a foreign key.
298
+	 *    If the CSV data says (in the metadata again) that it's from a DIFFERENT database,
299
+	 *    we treat all the IDs in the CSV as temporary ID- eg, if the CSV specifies an event with
300
+	 *    ID 1, and the database already has an event with ID 1, we assume that's just a coincidence,
301
+	 *    and insert a new event, and map it's temporary ID of 1 over to its new real ID.
302
+	 *    An important exception are non-auto-increment primary keys. If one entry in the
303
+	 *    CSV file has the same ID as one in the DB, we assume they are meant to be
304
+	 *    the same item, and instead update the item in the DB with that same ID.
305
+	 *    Also note, we remember the mappings permanently. So the 2nd, 3rd, and 10000th
306
+	 *    time you import a CSV from a different site, we remember their mappings, and
307
+	 * will try to update the item in the DB instead of inserting another item (eg
308
+	 * if we previously imported an event with temporary ID 1, and then it got a
309
+	 * real ID of 123, we remember that. So the next time we import an event with
310
+	 * temporary ID, from the same site, we know that it's real ID is 123, and will
311
+	 * update that event, instead of adding a new event).
312
+	 *
313
+	 * @access public
314
+	 * @param array $csv_data_array - the array containing the csv data produced from
315
+	 *                              EE_CSV::import_csv_to_model_data_array()
316
+	 * @param array $fields_to_save - an array containing the csv column names as keys with the corresponding db table
317
+	 *                              fields they will be saved to
318
+	 * @return TRUE on success, FALSE on fail
319
+	 * @throws \EE_Error
320
+	 */
321
+	public function save_csv_data_array_to_db($csv_data_array, $model_name = false)
322
+	{
323
+		$success = false;
324
+		$error = false;
325
+		// whther to treat this import as if it's data froma different database or not
326
+		// ie, if it IS from a different database, ignore foreign keys whihf
327
+		$export_from_site_a_to_b = true;
328
+		// first level of array is not table information but a table name was passed to the function
329
+		// array is only two levels deep, so let's fix that by adding a level, else the next steps will fail
330
+		if ($model_name) {
331
+			$csv_data_array = array($csv_data_array);
332
+		}
333
+		// begin looking through the $csv_data_array, expecting the toplevel key to be the model's name...
334
+		$old_site_url = 'none-specified';
335
+		// hanlde metadata
336
+		if (isset($csv_data_array[ EE_CSV::metadata_header ])) {
337
+			$csv_metadata = array_shift($csv_data_array[ EE_CSV::metadata_header ]);
338
+			// ok so its metadata, dont try to save it to ehte db obviously...
339
+			if (isset($csv_metadata['site_url']) && $csv_metadata['site_url'] == site_url()) {
340
+				EE_Error::add_attention(
341
+					sprintf(
342
+						esc_html__(
343
+							"CSV Data appears to be from the same database, so attempting to update data",
344
+							"event_espresso"
345
+						)
346
+					)
347
+				);
348
+				$export_from_site_a_to_b = false;
349
+			} else {
350
+				$old_site_url = isset($csv_metadata['site_url']) ? $csv_metadata['site_url'] : $old_site_url;
351
+				EE_Error::add_attention(
352
+					sprintf(
353
+						esc_html__(
354
+							"CSV Data appears to be from a different database (%s instead of %s), so we assume IDs in the CSV data DO NOT correspond to IDs in this database",
355
+							"event_espresso"
356
+						),
357
+						$old_site_url,
358
+						site_url()
359
+					)
360
+				);
361
+			};
362
+			unset($csv_data_array[ EE_CSV::metadata_header ]);
363
+		}
364
+		/**
365
+		 * @var $old_db_to_new_db_mapping 2d array: toplevel keys being model names, bottom-level keys being the original key, and
366
+		 * the value will be the newly-inserted ID.
367
+		 * If we have already imported data from the same website via CSV, it shoudl be kept in this wp option
368
+		 */
369
+		$old_db_to_new_db_mapping = get_option('ee_id_mapping_from' . sanitize_title($old_site_url), array());
370
+		if ($old_db_to_new_db_mapping) {
371
+			EE_Error::add_attention(
372
+				sprintf(
373
+					esc_html__(
374
+						"We noticed you have imported data via CSV from %s before. Because of this, IDs in your CSV have been mapped to their new IDs in %s",
375
+						"event_espresso"
376
+					),
377
+					$old_site_url,
378
+					site_url()
379
+				)
380
+			);
381
+		}
382
+		$old_db_to_new_db_mapping = $this->save_data_rows_to_db(
383
+			$csv_data_array,
384
+			$export_from_site_a_to_b,
385
+			$old_db_to_new_db_mapping
386
+		);
387
+
388
+		// save the mapping from old db to new db in case they try re-importing the same data from the same website again
389
+		update_option('ee_id_mapping_from' . sanitize_title($old_site_url), $old_db_to_new_db_mapping);
390
+
391
+		if ($this->_total_updates > 0) {
392
+			EE_Error::add_success(
393
+				sprintf(
394
+					esc_html__("%s existing records in the database were updated.", "event_espresso"),
395
+					$this->_total_updates
396
+				)
397
+			);
398
+			$success = true;
399
+		}
400
+		if ($this->_total_inserts > 0) {
401
+			EE_Error::add_success(
402
+				sprintf(esc_html__("%s new records were added to the database.", "event_espresso"), $this->_total_inserts)
403
+			);
404
+			$success = true;
405
+		}
406
+
407
+		if ($this->_total_update_errors > 0) {
408
+			EE_Error::add_error(
409
+				sprintf(
410
+					esc_html__(
411
+						"'One or more errors occurred, and a total of %s existing records in the database were <strong>not</strong> updated.'",
412
+						"event_espresso"
413
+					),
414
+					$this->_total_update_errors
415
+				),
416
+				__FILE__,
417
+				__FUNCTION__,
418
+				__LINE__
419
+			);
420
+			$error = true;
421
+		}
422
+		if ($this->_total_insert_errors > 0) {
423
+			EE_Error::add_error(
424
+				sprintf(
425
+					esc_html__(
426
+						"One or more errors occurred, and a total of %s new records were <strong>not</strong> added to the database.'",
427
+						"event_espresso"
428
+					),
429
+					$this->_total_insert_errors
430
+				),
431
+				__FILE__,
432
+				__FUNCTION__,
433
+				__LINE__
434
+			);
435
+			$error = true;
436
+		}
437
+
438
+		// lastly, we need to update the datetime and ticket sold amounts
439
+		// as those may have been affected by this
440
+		EEM_Ticket::instance()->update_tickets_sold(EEM_Ticket::instance()->get_all());
441
+
442
+		// if there was at least one success and absolutely no errors
443
+		if ($success && ! $error) {
444
+			return true;
445
+		} else {
446
+			return false;
447
+		}
448
+	}
449
+
450
+
451
+	/**
452
+	 * Processes the array of data, given the knowledge that it's from the same database or a different one,
453
+	 * and the mapping from temporary IDs to real IDs.
454
+	 * If the data is from a different database, we treat the primary keys and their corresponding
455
+	 * foreign keys as "temp Ids", basically identifiers that get mapped to real primary keys
456
+	 * in the real target database. As items are inserted, their temporary primary keys
457
+	 * are mapped to the real IDs in the target database. Also, before doing any update or
458
+	 * insert, we replace all the temp ID which are foreign keys with their mapped real IDs.
459
+	 * An exception: string primary keys are treated as real IDs, or else we'd need to
460
+	 * dynamically generate new string primary keys which would be very awkard for the country table etc.
461
+	 * Also, models with no primary key are strange too. We combine use their primar key INDEX (a
462
+	 * combination of fields) to create a unique string identifying the row and store
463
+	 * those in the mapping.
464
+	 *
465
+	 * If the data is from the same database, we usually treat primary keys as real IDs.
466
+	 * An exception is if there is nothing in the database for that ID. If that's the case,
467
+	 * we need to insert a new row for that ID, and then map from the non-existent ID
468
+	 * to the newly-inserted real ID.
469
+	 *
470
+	 * @param type $csv_data_array
471
+	 * @param type $export_from_site_a_to_b
472
+	 * @param type $old_db_to_new_db_mapping
473
+	 * @return array updated $old_db_to_new_db_mapping
474
+	 */
475
+	public function save_data_rows_to_db($csv_data_array, $export_from_site_a_to_b, $old_db_to_new_db_mapping)
476
+	{
477
+		foreach ($csv_data_array as $model_name_in_csv_data => $model_data_from_import) {
478
+			// now check that assumption was correct. If
479
+			if (EE_Registry::instance()->is_model_name($model_name_in_csv_data)) {
480
+				$model_name = $model_name_in_csv_data;
481
+			} else {
482
+				// no table info in the array and no table name passed to the function?? FAIL
483
+				EE_Error::add_error(
484
+					esc_html__(
485
+						'No table information was specified and/or found, therefore the import could not be completed',
486
+						'event_espresso'
487
+					),
488
+					__FILE__,
489
+					__FUNCTION__,
490
+					__LINE__
491
+				);
492
+				return false;
493
+			}
494
+			/* @var $model EEM_Base */
495
+			$model = EE_Registry::instance()->load_model($model_name);
496
+
497
+			// so without further ado, scanning all the data provided for primary keys and their inital values
498
+			foreach ($model_data_from_import as $model_object_data) {
499
+				// before we do ANYTHING, make sure the csv row wasn't just completely blank
500
+				$row_is_completely_empty = true;
501
+				foreach ($model_object_data as $field) {
502
+					if ($field) {
503
+						$row_is_completely_empty = false;
504
+					}
505
+				}
506
+				if ($row_is_completely_empty) {
507
+					continue;
508
+				}
509
+				// find the PK in the row of data (or a combined key if
510
+				// there is no primary key)
511
+				if ($model->has_primary_key_field()) {
512
+					$id_in_csv = $model_object_data[ $model->primary_key_name() ];
513
+				} else {
514
+					$id_in_csv = $model->get_index_primary_key_string($model_object_data);
515
+				}
516
+
517
+
518
+				$model_object_data = $this->_replace_temp_ids_with_mappings(
519
+					$model_object_data,
520
+					$model,
521
+					$old_db_to_new_db_mapping,
522
+					$export_from_site_a_to_b
523
+				);
524
+				// now we need to decide if we're going to add a new model object given the $model_object_data,
525
+				// or just update.
526
+				if ($export_from_site_a_to_b) {
527
+					$what_to_do = $this->_decide_whether_to_insert_or_update_given_data_from_other_db(
528
+						$id_in_csv,
529
+						$model_object_data,
530
+						$model,
531
+						$old_db_to_new_db_mapping
532
+					);
533
+				} else {// this is just a re-import
534
+					$what_to_do = $this->_decide_whether_to_insert_or_update_given_data_from_same_db(
535
+						$id_in_csv,
536
+						$model_object_data,
537
+						$model,
538
+						$old_db_to_new_db_mapping
539
+					);
540
+				}
541
+				if ($what_to_do == self::do_nothing) {
542
+					continue;
543
+				}
544
+
545
+				// double-check we actually want to insert, if that's what we're planning
546
+				// based on whether this item would be unique in the DB or not
547
+				if ($what_to_do == self::do_insert) {
548
+					// we're supposed to be inserting. But wait, will this thing
549
+					// be acceptable if inserted?
550
+					$conflicting = $model->get_one_conflicting($model_object_data, false);
551
+					if ($conflicting) {
552
+						// ok, this item would conflict if inserted. Just update the item that it conflicts with.
553
+						$what_to_do = self::do_update;
554
+						// and if this model has a primary key, remember its mapping
555
+						if ($model->has_primary_key_field()) {
556
+							$old_db_to_new_db_mapping[ $model_name ][ $id_in_csv ] = $conflicting->ID();
557
+							$model_object_data[ $model->primary_key_name() ] = $conflicting->ID();
558
+						} else {
559
+							// we want to update this conflicting item, instead of inserting a conflicting item
560
+							// so we need to make sure they match entirely (its possible that they only conflicted on one field, but we need them to match on other fields
561
+							// for the WHERE conditions in the update). At the time of this comment, there were no models like this
562
+							foreach ($model->get_combined_primary_key_fields() as $key_field) {
563
+								$model_object_data[ $key_field->get_name() ] = $conflicting->get(
564
+									$key_field->get_name()
565
+								);
566
+							}
567
+						}
568
+					}
569
+				}
570
+				if ($what_to_do == self::do_insert) {
571
+					$old_db_to_new_db_mapping = $this->_insert_from_data_array(
572
+						$id_in_csv,
573
+						$model_object_data,
574
+						$model,
575
+						$old_db_to_new_db_mapping
576
+					);
577
+				} elseif ($what_to_do == self::do_update) {
578
+					$old_db_to_new_db_mapping = $this->_update_from_data_array(
579
+						$id_in_csv,
580
+						$model_object_data,
581
+						$model,
582
+						$old_db_to_new_db_mapping
583
+					);
584
+				} else {
585
+					throw new EE_Error(
586
+						sprintf(
587
+							esc_html__(
588
+								'Programming error. We shoudl be inserting or updating, but instead we are being told to "%s", whifh is invalid',
589
+								'event_espresso'
590
+							),
591
+							$what_to_do
592
+						)
593
+					);
594
+				}
595
+			}
596
+		}
597
+		return $old_db_to_new_db_mapping;
598
+	}
599
+
600
+
601
+	/**
602
+	 * Decides whether or not to insert, given that this data is from another database.
603
+	 * So, if the primary key of this $model_object_data already exists in the database,
604
+	 * it's just a coincidence and we should still insert. The only time we should
605
+	 * update is when we know what it maps to, or there's something that would
606
+	 * conflict (and we should instead just update that conflicting thing)
607
+	 *
608
+	 * @param string   $id_in_csv
609
+	 * @param array    $model_object_data        by reference so it can be modified
610
+	 * @param EEM_Base $model
611
+	 * @param array    $old_db_to_new_db_mapping by reference so it can be modified
612
+	 * @return string one of the consts on this class that starts with do_*
613
+	 */
614
+	protected function _decide_whether_to_insert_or_update_given_data_from_other_db(
615
+		$id_in_csv,
616
+		$model_object_data,
617
+		$model,
618
+		$old_db_to_new_db_mapping
619
+	) {
620
+		$model_name = $model->get_this_model_name();
621
+		// if it's a site-to-site export-and-import, see if this modelobject's id
622
+		// in the old data that we know of
623
+		if (isset($old_db_to_new_db_mapping[ $model_name ][ $id_in_csv ])) {
624
+			return self::do_update;
625
+		} else {
626
+			return self::do_insert;
627
+		}
628
+	}
629
+
630
+	/**
631
+	 * If this thing basically already exists in the database, we want to update it;
632
+	 * otherwise insert it (ie, someone tweaked the CSV file, or the item was
633
+	 * deleted in the database so it should be re-inserted)
634
+	 *
635
+	 * @param type     $id_in_csv
636
+	 * @param type     $model_object_data
637
+	 * @param EEM_Base $model
638
+	 * @param type     $old_db_to_new_db_mapping
639
+	 * @return
640
+	 */
641
+	protected function _decide_whether_to_insert_or_update_given_data_from_same_db(
642
+		$id_in_csv,
643
+		$model_object_data,
644
+		$model
645
+	) {
646
+		// in this case, check if this thing ACTUALLY exists in the database
647
+		if ($model->get_one_conflicting($model_object_data)) {
648
+			return self::do_update;
649
+		} else {
650
+			return self::do_insert;
651
+		}
652
+	}
653
+
654
+	/**
655
+	 * Using the $old_db_to_new_db_mapping array, replaces all the temporary IDs
656
+	 * with their mapped real IDs. Eg, if importing from site A to B, the mapping
657
+	 * file may indicate that the ID "my_event_id" maps to an actual event ID of 123.
658
+	 * So this function searches for any event temp Ids called "my_event_id" and
659
+	 * replaces them with 123.
660
+	 * Also, if there is no temp ID for the INT foreign keys from another database,
661
+	 * replaces them with 0 or the field's default.
662
+	 *
663
+	 * @param type     $model_object_data
664
+	 * @param EEM_Base $model
665
+	 * @param type     $old_db_to_new_db_mapping
666
+	 * @param boolean  $export_from_site_a_to_b
667
+	 * @return array updated model object data with temp IDs removed
668
+	 */
669
+	protected function _replace_temp_ids_with_mappings(
670
+		$model_object_data,
671
+		$model,
672
+		$old_db_to_new_db_mapping,
673
+		$export_from_site_a_to_b
674
+	) {
675
+		// if this model object's primary key is in the mapping, replace it
676
+		if (
677
+			$model->has_primary_key_field() &&
678
+			$model->get_primary_key_field()->is_auto_increment() &&
679
+			isset($old_db_to_new_db_mapping[ $model->get_this_model_name() ]) &&
680
+			isset(
681
+				$old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $model_object_data[ $model->primary_key_name() ] ]
682
+			)
683
+		) {
684
+			$model_object_data[ $model->primary_key_name() ] = $old_db_to_new_db_mapping[ $model->get_this_model_name(
685
+			) ][ $model_object_data[ $model->primary_key_name() ] ];
686
+		}
687
+
688
+		try {
689
+			$model_name_field = $model->get_field_containing_related_model_name();
690
+			$models_pointed_to_by_model_name_field = $model_name_field->get_model_names_pointed_to();
691
+		} catch (EE_Error $e) {
692
+			$model_name_field = null;
693
+			$models_pointed_to_by_model_name_field = array();
694
+		}
695
+		foreach ($model->field_settings(true) as $field_obj) {
696
+			if ($field_obj instanceof EE_Foreign_Key_Int_Field) {
697
+				$models_pointed_to = $field_obj->get_model_names_pointed_to();
698
+				$found_a_mapping = false;
699
+				foreach ($models_pointed_to as $model_pointed_to_by_fk) {
700
+					if ($model_name_field) {
701
+						$value_of_model_name_field = $model_object_data[ $model_name_field->get_name() ];
702
+						if ($value_of_model_name_field == $model_pointed_to_by_fk) {
703
+							$model_object_data[ $field_obj->get_name() ] = $this->_find_mapping_in(
704
+								$model_object_data[ $field_obj->get_name() ],
705
+								$model_pointed_to_by_fk,
706
+								$old_db_to_new_db_mapping,
707
+								$export_from_site_a_to_b
708
+							);
709
+							$found_a_mapping = true;
710
+							break;
711
+						}
712
+					} else {
713
+						$model_object_data[ $field_obj->get_name() ] = $this->_find_mapping_in(
714
+							$model_object_data[ $field_obj->get_name() ],
715
+							$model_pointed_to_by_fk,
716
+							$old_db_to_new_db_mapping,
717
+							$export_from_site_a_to_b
718
+						);
719
+						$found_a_mapping = true;
720
+					}
721
+					// once we've found a mapping for this field no need to continue
722
+					if ($found_a_mapping) {
723
+						break;
724
+					}
725
+				}
726
+			} else {
727
+				// it's a string foreign key (which we leave alone, because those are things
728
+				// like country names, which we'd really rather not make 2 USAs etc (we'd actually
729
+				// prefer to just update one)
730
+				// or it's just a regular value that ought to be replaced
731
+			}
732
+		}
733
+		//
734
+		if ($model instanceof EEM_Term_Taxonomy) {
735
+			$model_object_data = $this->_handle_split_term_ids($model_object_data);
736
+		}
737
+		return $model_object_data;
738
+	}
739
+
740
+	/**
741
+	 * If the data was exported PRE-4.2, but then imported POST-4.2, then the term_id
742
+	 * this term-taxonomy refers to may be out-of-date so we need to update it.
743
+	 * see https://make.wordpress.org/core/2015/02/16/taxonomy-term-splitting-in-4-2-a-developer-guide/
744
+	 *
745
+	 * @param type $model_object_data
746
+	 * @return array new model object data
747
+	 */
748
+	protected function _handle_split_term_ids($model_object_data)
749
+	{
750
+		if (
751
+			isset($model_object_data['term_id'])
752
+			&& isset($model_object_data['taxonomy'])
753
+			&& apply_filters(
754
+				'FHEE__EE_Import__handle_split_term_ids__function_exists',
755
+				function_exists('wp_get_split_term'),
756
+				$model_object_data
757
+			)
758
+		) {
759
+			$new_term_id = wp_get_split_term($model_object_data['term_id'], $model_object_data['taxonomy']);
760
+			if ($new_term_id) {
761
+				$model_object_data['term_id'] = $new_term_id;
762
+			}
763
+		}
764
+		return $model_object_data;
765
+	}
766
+
767
+	/**
768
+	 * Given the object's ID and its model's name, find it int he mapping data,
769
+	 * bearing in mind where it came from
770
+	 *
771
+	 * @param type   $object_id
772
+	 * @param string $model_name
773
+	 * @param array  $old_db_to_new_db_mapping
774
+	 * @param type   $export_from_site_a_to_b
775
+	 * @return int
776
+	 */
777
+	protected function _find_mapping_in($object_id, $model_name, $old_db_to_new_db_mapping, $export_from_site_a_to_b)
778
+	{
779
+		if (isset($old_db_to_new_db_mapping[ $model_name ][ $object_id ])) {
780
+			return $old_db_to_new_db_mapping[ $model_name ][ $object_id ];
781
+		} elseif ($object_id == '0' || $object_id == '') {
782
+			// leave as-is
783
+			return $object_id;
784
+		} elseif ($export_from_site_a_to_b) {
785
+			// we couldn't find a mapping for this, and it's from a different site,
786
+			// so blank it out
787
+			return null;
788
+		} elseif (! $export_from_site_a_to_b) {
789
+			// we coudln't find a mapping for this, but it's from thsi DB anyway
790
+			// so let's just leave it as-is
791
+			return $object_id;
792
+		}
793
+	}
794
+
795
+	/**
796
+	 *
797
+	 * @param type     $id_in_csv
798
+	 * @param type     $model_object_data
799
+	 * @param EEM_Base $model
800
+	 * @param type     $old_db_to_new_db_mapping
801
+	 * @return array updated $old_db_to_new_db_mapping
802
+	 */
803
+	protected function _insert_from_data_array($id_in_csv, $model_object_data, $model, $old_db_to_new_db_mapping)
804
+	{
805
+		// remove the primary key, if there is one (we don't want it for inserts OR updates)
806
+		// we'll put it back in if we need it
807
+		if ($model->has_primary_key_field() && $model->get_primary_key_field()->is_auto_increment()) {
808
+			$effective_id = $model_object_data[ $model->primary_key_name() ];
809
+			unset($model_object_data[ $model->primary_key_name() ]);
810
+		} else {
811
+			$effective_id = $model->get_index_primary_key_string($model_object_data);
812
+		}
813
+		// the model takes care of validating the CSV's input
814
+		try {
815
+			$new_id = $model->insert($model_object_data);
816
+			if ($new_id) {
817
+				$old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $id_in_csv ] = $new_id;
818
+				$this->_total_inserts++;
819
+				EE_Error::add_success(
820
+					sprintf(
821
+						esc_html__("Successfully added new %s (with id %s) with csv data %s", "event_espresso"),
822
+						$model->get_this_model_name(),
823
+						$new_id,
824
+						implode(",", $model_object_data)
825
+					)
826
+				);
827
+			} else {
828
+				$this->_total_insert_errors++;
829
+				// put the ID used back in there for the error message
830
+				if ($model->has_primary_key_field()) {
831
+					$model_object_data[ $model->primary_key_name() ] = $effective_id;
832
+				}
833
+				EE_Error::add_error(
834
+					sprintf(
835
+						esc_html__("Could not insert new %s with the csv data: %s", "event_espresso"),
836
+						$model->get_this_model_name(),
837
+						http_build_query($model_object_data)
838
+					),
839
+					__FILE__,
840
+					__FUNCTION__,
841
+					__LINE__
842
+				);
843
+			}
844
+		} catch (EE_Error $e) {
845
+			$this->_total_insert_errors++;
846
+			if ($model->has_primary_key_field()) {
847
+				$model_object_data[ $model->primary_key_name() ] = $effective_id;
848
+			}
849
+			EE_Error::add_error(
850
+				sprintf(
851
+					esc_html__("Could not insert new %s with the csv data: %s because %s", "event_espresso"),
852
+					$model->get_this_model_name(),
853
+					implode(",", $model_object_data),
854
+					$e->getMessage()
855
+				),
856
+				__FILE__,
857
+				__FUNCTION__,
858
+				__LINE__
859
+			);
860
+		}
861
+		return $old_db_to_new_db_mapping;
862
+	}
863
+
864
+	/**
865
+	 * Given the model object data, finds the row to update and updates it
866
+	 *
867
+	 * @param string|int $id_in_csv
868
+	 * @param array      $model_object_data
869
+	 * @param EEM_Base   $model
870
+	 * @param array      $old_db_to_new_db_mapping
871
+	 * @return array updated $old_db_to_new_db_mapping
872
+	 */
873
+	protected function _update_from_data_array($id_in_csv, $model_object_data, $model, $old_db_to_new_db_mapping)
874
+	{
875
+		try {
876
+			// let's keep two copies of the model object data:
877
+			// one for performing an update, one for everthing else
878
+			$model_object_data_for_update = $model_object_data;
879
+			if ($model->has_primary_key_field()) {
880
+				$conditions = array($model->primary_key_name() => $model_object_data[ $model->primary_key_name() ]);
881
+				// remove the primary key because we shouldn't use it for updating
882
+				unset($model_object_data_for_update[ $model->primary_key_name() ]);
883
+			} elseif ($model->get_combined_primary_key_fields() > 1) {
884
+				$conditions = array();
885
+				foreach ($model->get_combined_primary_key_fields() as $key_field) {
886
+					$conditions[ $key_field->get_name() ] = $model_object_data[ $key_field->get_name() ];
887
+				}
888
+			} else {
889
+				$model->primary_key_name(
890
+				);// this shoudl just throw an exception, explaining that we dont have a primary key (or a combine dkey)
891
+			}
892
+
893
+			$success = $model->update($model_object_data_for_update, array($conditions));
894
+			if ($success) {
895
+				$this->_total_updates++;
896
+				EE_Error::add_success(
897
+					sprintf(
898
+						esc_html__("Successfully updated %s with csv data %s", "event_espresso"),
899
+						$model->get_this_model_name(),
900
+						implode(",", $model_object_data_for_update)
901
+					)
902
+				);
903
+				// we should still record the mapping even though it was an update
904
+				// because if we were going to insert somethign but it was going to conflict
905
+				// we would have last-minute decided to update. So we'd like to know what we updated
906
+				// and so we record what record ended up being updated using the mapping
907
+				if ($model->has_primary_key_field()) {
908
+					$new_key_for_mapping = $model_object_data[ $model->primary_key_name() ];
909
+				} else {
910
+					// no primary key just a combined key
911
+					$new_key_for_mapping = $model->get_index_primary_key_string($model_object_data);
912
+				}
913
+				$old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $id_in_csv ] = $new_key_for_mapping;
914
+			} else {
915
+				$matched_items = $model->get_all(array($conditions));
916
+				if (! $matched_items) {
917
+					// no items were matched (so we shouldn't have updated)... but then we should have inserted? what the heck?
918
+					$this->_total_update_errors++;
919
+					EE_Error::add_error(
920
+						sprintf(
921
+							esc_html__(
922
+								"Could not update %s with the csv data: '%s' for an unknown reason (using WHERE conditions %s)",
923
+								"event_espresso"
924
+							),
925
+							$model->get_this_model_name(),
926
+							http_build_query($model_object_data),
927
+							http_build_query($conditions)
928
+						),
929
+						__FILE__,
930
+						__FUNCTION__,
931
+						__LINE__
932
+					);
933
+				} else {
934
+					$this->_total_updates++;
935
+					EE_Error::add_success(
936
+						sprintf(
937
+							esc_html__(
938
+								"%s with csv data '%s' was found in the database and didn't need updating because all the data is identical.",
939
+								"event_espresso"
940
+							),
941
+							$model->get_this_model_name(),
942
+							implode(",", $model_object_data)
943
+						)
944
+					);
945
+				}
946
+			}
947
+		} catch (EE_Error $e) {
948
+			$this->_total_update_errors++;
949
+			$basic_message = sprintf(
950
+				esc_html__("Could not update %s with the csv data: %s because %s", "event_espresso"),
951
+				$model->get_this_model_name(),
952
+				implode(",", $model_object_data),
953
+				$e->getMessage()
954
+			);
955
+			$debug_message = $basic_message . ' Stack trace: ' . $e->getTraceAsString();
956
+			EE_Error::add_error("$basic_message | $debug_message", __FILE__, __FUNCTION__, __LINE__);
957
+		}
958
+		return $old_db_to_new_db_mapping;
959
+	}
960
+
961
+	/**
962
+	 * Gets the number of inserts performed since importer was instantiated or reset
963
+	 *
964
+	 * @return int
965
+	 */
966
+	public function get_total_inserts()
967
+	{
968
+		return $this->_total_inserts;
969
+	}
970
+
971
+	/**
972
+	 *  Gets the number of insert errors since importer was instantiated or reset
973
+	 *
974
+	 * @return int
975
+	 */
976
+	public function get_total_insert_errors()
977
+	{
978
+		return $this->_total_insert_errors;
979
+	}
980
+
981
+	/**
982
+	 *  Gets the number of updates performed since importer was instantiated or reset
983
+	 *
984
+	 * @return int
985
+	 */
986
+	public function get_total_updates()
987
+	{
988
+		return $this->_total_updates;
989
+	}
990
+
991
+	/**
992
+	 *  Gets the number of update errors since importer was instantiated or reset
993
+	 *
994
+	 * @return int
995
+	 */
996
+	public function get_total_update_errors()
997
+	{
998
+		return $this->_total_update_errors;
999
+	}
1000 1000
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Question_Form_Input.class.php 2 patches
Spacing   +32 added lines, -32 removed lines patch added patch discarded remove patch
@@ -114,7 +114,7 @@  discard block
 block discarded – undo
114 114
      */
115 115
     public function set_question_form_input_meta($q_meta = [])
116 116
     {
117
-        $default_q_meta  = [
117
+        $default_q_meta = [
118 118
             'att_nmbr'       => 1,
119 119
             'ticket_id'      => '',
120 120
             'date'           => '',
@@ -160,13 +160,13 @@  discard block
 block discarded – undo
160 160
      */
161 161
     private function _set_input_name($qstn_id)
162 162
     {
163
-        if (! empty($qstn_id)) {
163
+        if ( ! empty($qstn_id)) {
164 164
             $ANS_ID  = $this->get('ANS_ID');
165
-            $qstn_id = ! empty($ANS_ID) ? '[' . $qstn_id . '][' . $ANS_ID . ']' : '[' . $qstn_id . ']';
165
+            $qstn_id = ! empty($ANS_ID) ? '['.$qstn_id.']['.$ANS_ID.']' : '['.$qstn_id.']';
166 166
         }
167 167
         $this->QST_input_name = $this->_QST_meta['append_qstn_id'] && ! empty($qstn_id)
168
-            ? $this->_QST_meta['input_prefix'] . $this->_QST_meta['input_name'] . $qstn_id
169
-            : $this->_QST_meta['input_prefix'] . $this->_QST_meta['input_name'];
168
+            ? $this->_QST_meta['input_prefix'].$this->_QST_meta['input_name'].$qstn_id
169
+            : $this->_QST_meta['input_prefix'].$this->_QST_meta['input_name'];
170 170
     }
171 171
 
172 172
 
@@ -181,7 +181,7 @@  discard block
 block discarded – undo
181 181
      */
182 182
     public function get($property = null)
183 183
     {
184
-        if (! empty($property)) {
184
+        if ( ! empty($property)) {
185 185
             if (EEM_Question::instance()->has_field($property)) {
186 186
                 return $this->_QST->get($property);
187 187
             } elseif (EEM_Answer::instance()->has_field($property)) {
@@ -207,7 +207,7 @@  discard block
 block discarded – undo
207 207
     {
208 208
         // first try regular property exists method which works as expected in PHP 5.3+
209 209
         $prop = EEH_Class_Tools::has_property($classname, $property);
210
-        if (! $prop) {
210
+        if ( ! $prop) {
211 211
             // use reflection for < PHP 5.3 as a double check when property is not found, possible due to access restriction
212 212
             $reflector = new ReflectionClass($classname);
213 213
             $prop      = $reflector->hasProperty($property);
@@ -231,7 +231,7 @@  discard block
 block discarded – undo
231 231
             ? $this->_QST_meta['input_id']
232 232
             : sanitize_key(strip_tags($this->_QST->get('QST_display_text')));
233 233
         $this->QST_input_id = $this->_QST_meta['append_qstn_id'] && ! empty($qstn_id)
234
-            ? $input_id . '-' . $qstn_id
234
+            ? $input_id.'-'.$qstn_id
235 235
             : $input_id;
236 236
     }
237 237
 
@@ -272,8 +272,8 @@  discard block
 block discarded – undo
272 272
             $date = $this->_QST_meta['date'];
273 273
             $time = $this->_QST_meta['time'];
274 274
             $price_id = $this->_QST_meta['price_id'];
275
-            if (isset($this->form_data['qstn'][ $EVT_ID ][ $att_nmbr ][ $date ][ $time ][ $price_id ][ $qstn_id ])) {
276
-                $answer = $this->form_data['qstn'][ $EVT_ID ][ $att_nmbr ][ $date ][ $time ][ $price_id ][ $qstn_id ];
275
+            if (isset($this->form_data['qstn'][$EVT_ID][$att_nmbr][$date][$time][$price_id][$qstn_id])) {
276
+                $answer = $this->form_data['qstn'][$EVT_ID][$att_nmbr][$date][$time][$price_id][$qstn_id];
277 277
                 $this->_ANS->set('ANS_value', $answer);
278 278
             }
279 279
         }
@@ -292,42 +292,42 @@  discard block
 block discarded – undo
292 292
      */
293 293
     public static function generate_question_form_inputs_for_object($object = false, $input_types = [])
294 294
     {
295
-        if (! is_object($object)) {
295
+        if ( ! is_object($object)) {
296 296
             return [];
297 297
         }
298 298
         $inputs = [];
299 299
         $fields = $object->get_model()->field_settings(false);
300 300
         foreach ($fields as $field_ID => $field) {
301 301
             if ($field instanceof EE_Model_Field_Base) {
302
-                if (isset($input_types[ $field_ID ])) {
302
+                if (isset($input_types[$field_ID])) {
303 303
                     // get saved value for field
304 304
                     $value = $object->get($field_ID);
305 305
                     // if no saved value, then use default
306 306
                     $value = $value !== null ? $value : $field->get_default_value();
307 307
                     // determine question type
308
-                    $type = isset($input_types[ $field_ID ]) ? $input_types[ $field_ID ]['type'] : 'TEXT';
308
+                    $type = isset($input_types[$field_ID]) ? $input_types[$field_ID]['type'] : 'TEXT';
309 309
                     // input name
310
-                    $input_name = isset($input_types[ $field_ID ]) && isset($input_types[ $field_ID ]['input_name'])
311
-                        ? $input_types[ $field_ID ]['input_name'] . '[' . $field_ID . ']'
310
+                    $input_name = isset($input_types[$field_ID]) && isset($input_types[$field_ID]['input_name'])
311
+                        ? $input_types[$field_ID]['input_name'].'['.$field_ID.']'
312 312
                         : $field_ID;
313 313
                     // css class for input
314
-                    $class = isset($input_types[ $field_ID ]['class']) && ! empty($input_types[ $field_ID ]['class'])
315
-                        ? ' ' . $input_types[ $field_ID ]['class']
314
+                    $class = isset($input_types[$field_ID]['class']) && ! empty($input_types[$field_ID]['class'])
315
+                        ? ' '.$input_types[$field_ID]['class']
316 316
                         : '';
317 317
                     // whether to apply htmlentities to answer
318
-                    $htmlentities = isset($input_types[ $field_ID ]['htmlentities'])
319
-                        ? $input_types[ $field_ID ]['htmlentities']
318
+                    $htmlentities = isset($input_types[$field_ID]['htmlentities'])
319
+                        ? $input_types[$field_ID]['htmlentities']
320 320
                         : true;
321 321
                     // whether to apply htmlentities to answer
322
-                    $label_b4 = isset($input_types[ $field_ID ]['label_b4'])
323
-                        ? $input_types[ $field_ID ]['label_b4']
322
+                    $label_b4 = isset($input_types[$field_ID]['label_b4'])
323
+                        ? $input_types[$field_ID]['label_b4']
324 324
                         : false;
325 325
                     // whether to apply htmlentities to answer
326
-                    $use_desc_4_label = isset($input_types[ $field_ID ]['use_desc_4_label'])
327
-                        ? $input_types[ $field_ID ]['use_desc_4_label']
326
+                    $use_desc_4_label = isset($input_types[$field_ID]['use_desc_4_label'])
327
+                        ? $input_types[$field_ID]['use_desc_4_label']
328 328
                         : false;
329 329
                     // whether input is disabled
330
-                    $disabled = isset($input_types[ $field_ID ]['disabled']) && $input_types[ $field_ID ]['disabled'];
330
+                    $disabled = isset($input_types[$field_ID]['disabled']) && $input_types[$field_ID]['disabled'];
331 331
 
332 332
                     // create EE_Question_Form_Input object
333 333
                     $QFI = new EE_Question_Form_Input(
@@ -347,9 +347,9 @@  discard block
 block discarded – undo
347 347
                             ]
348 348
                         ),
349 349
                         [
350
-                            'input_id'         => $field_ID . '-' . $object->ID(),
350
+                            'input_id'         => $field_ID.'-'.$object->ID(),
351 351
                             'input_name'       => $input_name,
352
-                            'input_class'      => $field_ID . $class,
352
+                            'input_class'      => $field_ID.$class,
353 353
                             'input_prefix'     => '',
354 354
                             'append_qstn_id'   => false,
355 355
                             'htmlentities'     => $htmlentities,
@@ -360,10 +360,10 @@  discard block
 block discarded – undo
360 360
                     // does question type have options ?
361 361
                     if (
362 362
                         in_array($type, ['DROPDOWN', 'RADIO_BTN', 'CHECKBOX'])
363
-                        && isset($input_types[ $field_ID ])
364
-                        && isset($input_types[ $field_ID ]['options'])
363
+                        && isset($input_types[$field_ID])
364
+                        && isset($input_types[$field_ID]['options'])
365 365
                     ) {
366
-                        foreach ($input_types[ $field_ID ]['options'] as $option) {
366
+                        foreach ($input_types[$field_ID]['options'] as $option) {
367 367
                             $option    = stripslashes_deep($option);
368 368
                             $option_id = ! empty($option['id']) ? $option['id'] : 0;
369 369
                             $QSO       = EE_Question_Option::new_instance(
@@ -381,7 +381,7 @@  discard block
 block discarded – undo
381 381
                     if ($disabled || $field_ID == $object->get_model()->primary_key_name()) {
382 382
                         $QFI->set('QST_disabled', true);
383 383
                     }
384
-                    $inputs[ $field_ID ] = $QFI;
384
+                    $inputs[$field_ID] = $QFI;
385 385
                 }
386 386
             }
387 387
         }
@@ -414,7 +414,7 @@  discard block
 block discarded – undo
414 414
      */
415 415
     public function set($property = null, $value = null)
416 416
     {
417
-        if (! empty($property)) {
417
+        if ( ! empty($property)) {
418 418
             if (EEM_Question::instance()->has_field($property)) {
419 419
                 $this->_QST->set($property, $value);
420 420
             } elseif (EEM_Answer::instance()->has_field($property)) {
@@ -460,6 +460,6 @@  discard block
 block discarded – undo
460 460
      */
461 461
     public function get_meta($key = false)
462 462
     {
463
-        return $key && isset($this->_QST_meta[ $key ]) ? $this->_QST_meta[ $key ] : false;
463
+        return $key && isset($this->_QST_meta[$key]) ? $this->_QST_meta[$key] : false;
464 464
     }
465 465
 }
Please login to merge, or discard this patch.
Indentation   +446 added lines, -446 removed lines patch added patch discarded remove patch
@@ -15,450 +15,450 @@
 block discarded – undo
15 15
  */
16 16
 class EE_Question_Form_Input
17 17
 {
18
-    /**
19
-     *    EE_Question object
20
-     *
21
-     * @access private
22
-     * @var object
23
-     */
24
-    private $_QST = null;
25
-
26
-    /**
27
-     *    EE_Answer object
28
-     *
29
-     * @access private
30
-     * @var object
31
-     */
32
-    private $_ANS = null;
33
-
34
-    /**
35
-     *    $_QST_meta
36
-     * @access private
37
-     * @var array
38
-     */
39
-    private $_QST_meta = [];
40
-
41
-    /**
42
-     *    $QST_input_name
43
-     * @access private
44
-     * @var string
45
-     */
46
-    private $QST_input_name = '';
47
-
48
-    /**
49
-     *    $QST_input_id
50
-     * @access private
51
-     * @var string
52
-     */
53
-    private $QST_input_id = '';
54
-
55
-    /**
56
-     *    $QST_input_class
57
-     * @access private
58
-     * @var string
59
-     */
60
-    private $QST_input_class = '';
61
-
62
-    /**
63
-     * @var bool $QST_disabled
64
-     */
65
-    private $QST_disabled = false;
66
-
67
-    /**
68
-     * @var RequestInterface
69
-     */
70
-    protected $request;
71
-
72
-    /**
73
-     * @var array
74
-     */
75
-    protected $form_data;
76
-
77
-
78
-    /**
79
-     * constructor for questions
80
-     *
81
-     * @param EE_Question $QST EE_Question object
82
-     * @param EE_Answer   $ANS EE_Answer object
83
-     * @param array       $q_meta
84
-     * @throws EE_Error
85
-     * @throws ReflectionException
86
-     */
87
-    public function __construct(EE_Question $QST = null, EE_Answer $ANS = null, $q_meta = [])
88
-    {
89
-        $this->request   = LoaderFactory::getLoader()->getShared(RequestInterface::class);
90
-        $this->form_data = $this->request->requestParams();
91
-        if (empty($QST) || empty($ANS)) {
92
-            EE_Error::add_error(
93
-                esc_html__('An error occurred. A valid EE_Question or EE_Answer object was not received.', 'event_espresso'),
94
-                __FILE__,
95
-                __FUNCTION__,
96
-                __LINE__
97
-            );
98
-            return null;
99
-        }
100
-        $this->_QST = $QST;
101
-        $this->_ANS = $ANS;
102
-        $this->set_question_form_input_meta($q_meta);
103
-        $this->set_question_form_input_init();
104
-    }
105
-
106
-
107
-    /**
108
-     * sets meta data for the question form input
109
-     *
110
-     * @access public
111
-     * @param array $q_meta
112
-     * @return void
113
-     */
114
-    public function set_question_form_input_meta($q_meta = [])
115
-    {
116
-        $default_q_meta  = [
117
-            'att_nmbr'       => 1,
118
-            'ticket_id'      => '',
119
-            'date'           => '',
120
-            'time'           => '',
121
-            'input_name'     => '',
122
-            'input_id'       => '',
123
-            'input_class'    => '',
124
-            'input_prefix'   => 'qstn',
125
-            'append_qstn_id' => true,
126
-            'htmlentities'   => true,
127
-            'allow_null'     => false,
128
-        ];
129
-        $this->_QST_meta = array_merge($default_q_meta, $q_meta);
130
-    }
131
-
132
-
133
-    /**
134
-     * set_question_form_input_init
135
-     *
136
-     * @access public
137
-     * @return void
138
-     * @throws EE_Error
139
-     * @throws ReflectionException
140
-     */
141
-    public function set_question_form_input_init()
142
-    {
143
-        $qstn_id = $this->_QST->system_ID() ? $this->_QST->system_ID() : $this->_QST->ID();
144
-        $this->_set_input_name($qstn_id);
145
-        $this->_set_input_id($qstn_id);
146
-        $this->_set_input_class();
147
-        $this->set_question_form_input_answer($qstn_id);
148
-    }
149
-
150
-
151
-    /**
152
-     * set_input_name
153
-     *
154
-     * @access private
155
-     * @param $qstn_id
156
-     * @return void
157
-     * @throws EE_Error
158
-     * @throws ReflectionException
159
-     */
160
-    private function _set_input_name($qstn_id)
161
-    {
162
-        if (! empty($qstn_id)) {
163
-            $ANS_ID  = $this->get('ANS_ID');
164
-            $qstn_id = ! empty($ANS_ID) ? '[' . $qstn_id . '][' . $ANS_ID . ']' : '[' . $qstn_id . ']';
165
-        }
166
-        $this->QST_input_name = $this->_QST_meta['append_qstn_id'] && ! empty($qstn_id)
167
-            ? $this->_QST_meta['input_prefix'] . $this->_QST_meta['input_name'] . $qstn_id
168
-            : $this->_QST_meta['input_prefix'] . $this->_QST_meta['input_name'];
169
-    }
170
-
171
-
172
-    /**
173
-     * get property values for question form input
174
-     *
175
-     * @access public
176
-     * @param string $property
177
-     * @return mixed
178
-     * @throws EE_Error
179
-     * @throws ReflectionException
180
-     */
181
-    public function get($property = null)
182
-    {
183
-        if (! empty($property)) {
184
-            if (EEM_Question::instance()->has_field($property)) {
185
-                return $this->_QST->get($property);
186
-            } elseif (EEM_Answer::instance()->has_field($property)) {
187
-                return $this->_ANS->get($property);
188
-            } elseif ($this->_question_form_input_property_exists(__CLASS__, $property)) {
189
-                return $this->{$property};
190
-            }
191
-        }
192
-        return null;
193
-    }
194
-
195
-
196
-    /**
197
-     *    _question_form_input_property_exists
198
-     *
199
-     * @access private
200
-     * @param string $classname
201
-     * @param string $property
202
-     * @return boolean
203
-     * @throws ReflectionException
204
-     */
205
-    private function _question_form_input_property_exists($classname, $property)
206
-    {
207
-        // first try regular property exists method which works as expected in PHP 5.3+
208
-        $prop = EEH_Class_Tools::has_property($classname, $property);
209
-        if (! $prop) {
210
-            // use reflection for < PHP 5.3 as a double check when property is not found, possible due to access restriction
211
-            $reflector = new ReflectionClass($classname);
212
-            $prop      = $reflector->hasProperty($property);
213
-        }
214
-        return $prop;
215
-    }
216
-
217
-
218
-    /**
219
-     * set_input_id
220
-     *
221
-     * @access private
222
-     * @param $qstn_id
223
-     * @return void
224
-     * @throws EE_Error
225
-     * @throws ReflectionException
226
-     */
227
-    private function _set_input_id($qstn_id)
228
-    {
229
-        $input_id           = isset($this->_QST_meta['input_id']) && ! empty($this->_QST_meta['input_id'])
230
-            ? $this->_QST_meta['input_id']
231
-            : sanitize_key(strip_tags($this->_QST->get('QST_display_text')));
232
-        $this->QST_input_id = $this->_QST_meta['append_qstn_id'] && ! empty($qstn_id)
233
-            ? $input_id . '-' . $qstn_id
234
-            : $input_id;
235
-    }
236
-
237
-
238
-    /**
239
-     * set_input_class
240
-     *
241
-     * @access private
242
-     * @return void
243
-     */
244
-    private function _set_input_class()
245
-    {
246
-        $this->QST_input_class = isset($this->_QST_meta['input_class']) ? $this->_QST_meta['input_class'] : '';
247
-    }
248
-
249
-
250
-    /**
251
-     * set_question_form_input_answer
252
-     *
253
-     * @access public
254
-     * @param mixed    int | string    $qstn_id
255
-     * @return void
256
-     * @throws EE_Error
257
-     * @throws ReflectionException
258
-     */
259
-    public function set_question_form_input_answer($qstn_id)
260
-    {
261
-        // check for answer in $this->form_data in case we are reprocessing a form after an error
262
-        if (
263
-            isset($this->_QST_meta['EVT_ID'])
264
-            && isset($this->_QST_meta['att_nmbr'])
265
-            && isset($this->_QST_meta['date'])
266
-            && isset($this->_QST_meta['time'])
267
-            && isset($this->_QST_meta['price_id'])
268
-        ) {
269
-            $EVT_ID = $this->_QST_meta['EVT_ID'];
270
-            $att_nmbr = $this->_QST_meta['att_nmbr'];
271
-            $date = $this->_QST_meta['date'];
272
-            $time = $this->_QST_meta['time'];
273
-            $price_id = $this->_QST_meta['price_id'];
274
-            if (isset($this->form_data['qstn'][ $EVT_ID ][ $att_nmbr ][ $date ][ $time ][ $price_id ][ $qstn_id ])) {
275
-                $answer = $this->form_data['qstn'][ $EVT_ID ][ $att_nmbr ][ $date ][ $time ][ $price_id ][ $qstn_id ];
276
-                $this->_ANS->set('ANS_value', $answer);
277
-            }
278
-        }
279
-    }
280
-
281
-
282
-    /**
283
-     *        generate_question_form_inputs_for_object
284
-     *
285
-     * @access    protected
286
-     * @param bool|object $object $object
287
-     * @param array       $input_types
288
-     * @return        array
289
-     * @throws EE_Error
290
-     * @throws ReflectionException
291
-     */
292
-    public static function generate_question_form_inputs_for_object($object = false, $input_types = [])
293
-    {
294
-        if (! is_object($object)) {
295
-            return [];
296
-        }
297
-        $inputs = [];
298
-        $fields = $object->get_model()->field_settings(false);
299
-        foreach ($fields as $field_ID => $field) {
300
-            if ($field instanceof EE_Model_Field_Base) {
301
-                if (isset($input_types[ $field_ID ])) {
302
-                    // get saved value for field
303
-                    $value = $object->get($field_ID);
304
-                    // if no saved value, then use default
305
-                    $value = $value !== null ? $value : $field->get_default_value();
306
-                    // determine question type
307
-                    $type = isset($input_types[ $field_ID ]) ? $input_types[ $field_ID ]['type'] : 'TEXT';
308
-                    // input name
309
-                    $input_name = isset($input_types[ $field_ID ]) && isset($input_types[ $field_ID ]['input_name'])
310
-                        ? $input_types[ $field_ID ]['input_name'] . '[' . $field_ID . ']'
311
-                        : $field_ID;
312
-                    // css class for input
313
-                    $class = isset($input_types[ $field_ID ]['class']) && ! empty($input_types[ $field_ID ]['class'])
314
-                        ? ' ' . $input_types[ $field_ID ]['class']
315
-                        : '';
316
-                    // whether to apply htmlentities to answer
317
-                    $htmlentities = isset($input_types[ $field_ID ]['htmlentities'])
318
-                        ? $input_types[ $field_ID ]['htmlentities']
319
-                        : true;
320
-                    // whether to apply htmlentities to answer
321
-                    $label_b4 = isset($input_types[ $field_ID ]['label_b4'])
322
-                        ? $input_types[ $field_ID ]['label_b4']
323
-                        : false;
324
-                    // whether to apply htmlentities to answer
325
-                    $use_desc_4_label = isset($input_types[ $field_ID ]['use_desc_4_label'])
326
-                        ? $input_types[ $field_ID ]['use_desc_4_label']
327
-                        : false;
328
-                    // whether input is disabled
329
-                    $disabled = isset($input_types[ $field_ID ]['disabled']) && $input_types[ $field_ID ]['disabled'];
330
-
331
-                    // create EE_Question_Form_Input object
332
-                    $QFI = new EE_Question_Form_Input(
333
-                        EE_Question::new_instance(
334
-                            [
335
-                                'QST_ID'           => 0,
336
-                                'QST_display_text' => $field->get_nicename(),
337
-                                'QST_type'         => $type,
338
-                            ]
339
-                        ),
340
-                        EE_Answer::new_instance(
341
-                            [
342
-                                'ANS_ID'    => 0,
343
-                                'QST_ID'    => 0,
344
-                                'REG_ID'    => 0,
345
-                                'ANS_value' => $value,
346
-                            ]
347
-                        ),
348
-                        [
349
-                            'input_id'         => $field_ID . '-' . $object->ID(),
350
-                            'input_name'       => $input_name,
351
-                            'input_class'      => $field_ID . $class,
352
-                            'input_prefix'     => '',
353
-                            'append_qstn_id'   => false,
354
-                            'htmlentities'     => $htmlentities,
355
-                            'label_b4'         => $label_b4,
356
-                            'use_desc_4_label' => $use_desc_4_label,
357
-                        ]
358
-                    );
359
-                    // does question type have options ?
360
-                    if (
361
-                        in_array($type, ['DROPDOWN', 'RADIO_BTN', 'CHECKBOX'])
362
-                        && isset($input_types[ $field_ID ])
363
-                        && isset($input_types[ $field_ID ]['options'])
364
-                    ) {
365
-                        foreach ($input_types[ $field_ID ]['options'] as $option) {
366
-                            $option    = stripslashes_deep($option);
367
-                            $option_id = ! empty($option['id']) ? $option['id'] : 0;
368
-                            $QSO       = EE_Question_Option::new_instance(
369
-                                [
370
-                                    'QSO_value'   => (string) $option_id,
371
-                                    'QSO_desc'    => $option['text'],
372
-                                    'QSO_deleted' => false,
373
-                                ]
374
-                            );
375
-                            // all QST (and ANS) properties can be accessed indirectly thru QFI
376
-                            $QFI->add_temp_option($QSO);
377
-                        }
378
-                    }
379
-                    // we don't want ppl manually changing primary keys cuz that would just lead to total craziness man
380
-                    if ($disabled || $field_ID == $object->get_model()->primary_key_name()) {
381
-                        $QFI->set('QST_disabled', true);
382
-                    }
383
-                    $inputs[ $field_ID ] = $QFI;
384
-                }
385
-            }
386
-        }
387
-        return $inputs;
388
-    }
389
-
390
-
391
-    /**
392
-     *    add_temp_option
393
-     *
394
-     * @access public
395
-     * @param EE_Question_Option $QSO EE_Question_Option
396
-     * @return void
397
-     */
398
-    public function add_temp_option(EE_Question_Option $QSO)
399
-    {
400
-        $this->_QST->add_temp_option($QSO);
401
-    }
402
-
403
-
404
-    /**
405
-     * set property values for question form input
406
-     *
407
-     * @access public
408
-     * @param string $property
409
-     * @param mixed  $value
410
-     * @return void
411
-     * @throws EE_Error
412
-     * @throws ReflectionException
413
-     */
414
-    public function set($property = null, $value = null)
415
-    {
416
-        if (! empty($property)) {
417
-            if (EEM_Question::instance()->has_field($property)) {
418
-                $this->_QST->set($property, $value);
419
-            } elseif (EEM_Answer::instance()->has_field($property)) {
420
-                $this->_ANS->set($property, $value);
421
-            } elseif ($this->_question_form_input_property_exists(__CLASS__, $property)) {
422
-                $this->{$property} = $value;
423
-            }
424
-        }
425
-    }
426
-
427
-
428
-    /**
429
-     *    _question_form_input_property_exists
430
-     *
431
-     * @access public
432
-     * @param boolean      $notDeletedOptionsOnly            1
433
-     *                                                       whether to return ALL options, or only the ones which have
434
-     *                                                       not yet been deleted
435
-     * @param string|array $selected_value_to_always_include , when retrieving options to an ANSWERED question,
436
-     *                                                       we want to usually only show non-deleted options AND the
437
-     *                                                       value that was selected for the answer, whether it was
438
-     *                                                       trashed or not.
439
-     * @return EE_Question_Option
440
-     */
441
-    public function options($notDeletedOptionsOnly = true, $selected_value_to_always_include = null)
442
-    {
443
-        $temp_options = $this->_QST->temp_options();
444
-        return ! empty($temp_options)
445
-            ? $temp_options
446
-            : $this->_QST->options(
447
-                $notDeletedOptionsOnly,
448
-                $selected_value_to_always_include
449
-            );
450
-    }
451
-
452
-
453
-    /**
454
-     *    get_meta
455
-     *
456
-     * @access public
457
-     * @param mixed $key
458
-     * @return mixed
459
-     */
460
-    public function get_meta($key = false)
461
-    {
462
-        return $key && isset($this->_QST_meta[ $key ]) ? $this->_QST_meta[ $key ] : false;
463
-    }
18
+	/**
19
+	 *    EE_Question object
20
+	 *
21
+	 * @access private
22
+	 * @var object
23
+	 */
24
+	private $_QST = null;
25
+
26
+	/**
27
+	 *    EE_Answer object
28
+	 *
29
+	 * @access private
30
+	 * @var object
31
+	 */
32
+	private $_ANS = null;
33
+
34
+	/**
35
+	 *    $_QST_meta
36
+	 * @access private
37
+	 * @var array
38
+	 */
39
+	private $_QST_meta = [];
40
+
41
+	/**
42
+	 *    $QST_input_name
43
+	 * @access private
44
+	 * @var string
45
+	 */
46
+	private $QST_input_name = '';
47
+
48
+	/**
49
+	 *    $QST_input_id
50
+	 * @access private
51
+	 * @var string
52
+	 */
53
+	private $QST_input_id = '';
54
+
55
+	/**
56
+	 *    $QST_input_class
57
+	 * @access private
58
+	 * @var string
59
+	 */
60
+	private $QST_input_class = '';
61
+
62
+	/**
63
+	 * @var bool $QST_disabled
64
+	 */
65
+	private $QST_disabled = false;
66
+
67
+	/**
68
+	 * @var RequestInterface
69
+	 */
70
+	protected $request;
71
+
72
+	/**
73
+	 * @var array
74
+	 */
75
+	protected $form_data;
76
+
77
+
78
+	/**
79
+	 * constructor for questions
80
+	 *
81
+	 * @param EE_Question $QST EE_Question object
82
+	 * @param EE_Answer   $ANS EE_Answer object
83
+	 * @param array       $q_meta
84
+	 * @throws EE_Error
85
+	 * @throws ReflectionException
86
+	 */
87
+	public function __construct(EE_Question $QST = null, EE_Answer $ANS = null, $q_meta = [])
88
+	{
89
+		$this->request   = LoaderFactory::getLoader()->getShared(RequestInterface::class);
90
+		$this->form_data = $this->request->requestParams();
91
+		if (empty($QST) || empty($ANS)) {
92
+			EE_Error::add_error(
93
+				esc_html__('An error occurred. A valid EE_Question or EE_Answer object was not received.', 'event_espresso'),
94
+				__FILE__,
95
+				__FUNCTION__,
96
+				__LINE__
97
+			);
98
+			return null;
99
+		}
100
+		$this->_QST = $QST;
101
+		$this->_ANS = $ANS;
102
+		$this->set_question_form_input_meta($q_meta);
103
+		$this->set_question_form_input_init();
104
+	}
105
+
106
+
107
+	/**
108
+	 * sets meta data for the question form input
109
+	 *
110
+	 * @access public
111
+	 * @param array $q_meta
112
+	 * @return void
113
+	 */
114
+	public function set_question_form_input_meta($q_meta = [])
115
+	{
116
+		$default_q_meta  = [
117
+			'att_nmbr'       => 1,
118
+			'ticket_id'      => '',
119
+			'date'           => '',
120
+			'time'           => '',
121
+			'input_name'     => '',
122
+			'input_id'       => '',
123
+			'input_class'    => '',
124
+			'input_prefix'   => 'qstn',
125
+			'append_qstn_id' => true,
126
+			'htmlentities'   => true,
127
+			'allow_null'     => false,
128
+		];
129
+		$this->_QST_meta = array_merge($default_q_meta, $q_meta);
130
+	}
131
+
132
+
133
+	/**
134
+	 * set_question_form_input_init
135
+	 *
136
+	 * @access public
137
+	 * @return void
138
+	 * @throws EE_Error
139
+	 * @throws ReflectionException
140
+	 */
141
+	public function set_question_form_input_init()
142
+	{
143
+		$qstn_id = $this->_QST->system_ID() ? $this->_QST->system_ID() : $this->_QST->ID();
144
+		$this->_set_input_name($qstn_id);
145
+		$this->_set_input_id($qstn_id);
146
+		$this->_set_input_class();
147
+		$this->set_question_form_input_answer($qstn_id);
148
+	}
149
+
150
+
151
+	/**
152
+	 * set_input_name
153
+	 *
154
+	 * @access private
155
+	 * @param $qstn_id
156
+	 * @return void
157
+	 * @throws EE_Error
158
+	 * @throws ReflectionException
159
+	 */
160
+	private function _set_input_name($qstn_id)
161
+	{
162
+		if (! empty($qstn_id)) {
163
+			$ANS_ID  = $this->get('ANS_ID');
164
+			$qstn_id = ! empty($ANS_ID) ? '[' . $qstn_id . '][' . $ANS_ID . ']' : '[' . $qstn_id . ']';
165
+		}
166
+		$this->QST_input_name = $this->_QST_meta['append_qstn_id'] && ! empty($qstn_id)
167
+			? $this->_QST_meta['input_prefix'] . $this->_QST_meta['input_name'] . $qstn_id
168
+			: $this->_QST_meta['input_prefix'] . $this->_QST_meta['input_name'];
169
+	}
170
+
171
+
172
+	/**
173
+	 * get property values for question form input
174
+	 *
175
+	 * @access public
176
+	 * @param string $property
177
+	 * @return mixed
178
+	 * @throws EE_Error
179
+	 * @throws ReflectionException
180
+	 */
181
+	public function get($property = null)
182
+	{
183
+		if (! empty($property)) {
184
+			if (EEM_Question::instance()->has_field($property)) {
185
+				return $this->_QST->get($property);
186
+			} elseif (EEM_Answer::instance()->has_field($property)) {
187
+				return $this->_ANS->get($property);
188
+			} elseif ($this->_question_form_input_property_exists(__CLASS__, $property)) {
189
+				return $this->{$property};
190
+			}
191
+		}
192
+		return null;
193
+	}
194
+
195
+
196
+	/**
197
+	 *    _question_form_input_property_exists
198
+	 *
199
+	 * @access private
200
+	 * @param string $classname
201
+	 * @param string $property
202
+	 * @return boolean
203
+	 * @throws ReflectionException
204
+	 */
205
+	private function _question_form_input_property_exists($classname, $property)
206
+	{
207
+		// first try regular property exists method which works as expected in PHP 5.3+
208
+		$prop = EEH_Class_Tools::has_property($classname, $property);
209
+		if (! $prop) {
210
+			// use reflection for < PHP 5.3 as a double check when property is not found, possible due to access restriction
211
+			$reflector = new ReflectionClass($classname);
212
+			$prop      = $reflector->hasProperty($property);
213
+		}
214
+		return $prop;
215
+	}
216
+
217
+
218
+	/**
219
+	 * set_input_id
220
+	 *
221
+	 * @access private
222
+	 * @param $qstn_id
223
+	 * @return void
224
+	 * @throws EE_Error
225
+	 * @throws ReflectionException
226
+	 */
227
+	private function _set_input_id($qstn_id)
228
+	{
229
+		$input_id           = isset($this->_QST_meta['input_id']) && ! empty($this->_QST_meta['input_id'])
230
+			? $this->_QST_meta['input_id']
231
+			: sanitize_key(strip_tags($this->_QST->get('QST_display_text')));
232
+		$this->QST_input_id = $this->_QST_meta['append_qstn_id'] && ! empty($qstn_id)
233
+			? $input_id . '-' . $qstn_id
234
+			: $input_id;
235
+	}
236
+
237
+
238
+	/**
239
+	 * set_input_class
240
+	 *
241
+	 * @access private
242
+	 * @return void
243
+	 */
244
+	private function _set_input_class()
245
+	{
246
+		$this->QST_input_class = isset($this->_QST_meta['input_class']) ? $this->_QST_meta['input_class'] : '';
247
+	}
248
+
249
+
250
+	/**
251
+	 * set_question_form_input_answer
252
+	 *
253
+	 * @access public
254
+	 * @param mixed    int | string    $qstn_id
255
+	 * @return void
256
+	 * @throws EE_Error
257
+	 * @throws ReflectionException
258
+	 */
259
+	public function set_question_form_input_answer($qstn_id)
260
+	{
261
+		// check for answer in $this->form_data in case we are reprocessing a form after an error
262
+		if (
263
+			isset($this->_QST_meta['EVT_ID'])
264
+			&& isset($this->_QST_meta['att_nmbr'])
265
+			&& isset($this->_QST_meta['date'])
266
+			&& isset($this->_QST_meta['time'])
267
+			&& isset($this->_QST_meta['price_id'])
268
+		) {
269
+			$EVT_ID = $this->_QST_meta['EVT_ID'];
270
+			$att_nmbr = $this->_QST_meta['att_nmbr'];
271
+			$date = $this->_QST_meta['date'];
272
+			$time = $this->_QST_meta['time'];
273
+			$price_id = $this->_QST_meta['price_id'];
274
+			if (isset($this->form_data['qstn'][ $EVT_ID ][ $att_nmbr ][ $date ][ $time ][ $price_id ][ $qstn_id ])) {
275
+				$answer = $this->form_data['qstn'][ $EVT_ID ][ $att_nmbr ][ $date ][ $time ][ $price_id ][ $qstn_id ];
276
+				$this->_ANS->set('ANS_value', $answer);
277
+			}
278
+		}
279
+	}
280
+
281
+
282
+	/**
283
+	 *        generate_question_form_inputs_for_object
284
+	 *
285
+	 * @access    protected
286
+	 * @param bool|object $object $object
287
+	 * @param array       $input_types
288
+	 * @return        array
289
+	 * @throws EE_Error
290
+	 * @throws ReflectionException
291
+	 */
292
+	public static function generate_question_form_inputs_for_object($object = false, $input_types = [])
293
+	{
294
+		if (! is_object($object)) {
295
+			return [];
296
+		}
297
+		$inputs = [];
298
+		$fields = $object->get_model()->field_settings(false);
299
+		foreach ($fields as $field_ID => $field) {
300
+			if ($field instanceof EE_Model_Field_Base) {
301
+				if (isset($input_types[ $field_ID ])) {
302
+					// get saved value for field
303
+					$value = $object->get($field_ID);
304
+					// if no saved value, then use default
305
+					$value = $value !== null ? $value : $field->get_default_value();
306
+					// determine question type
307
+					$type = isset($input_types[ $field_ID ]) ? $input_types[ $field_ID ]['type'] : 'TEXT';
308
+					// input name
309
+					$input_name = isset($input_types[ $field_ID ]) && isset($input_types[ $field_ID ]['input_name'])
310
+						? $input_types[ $field_ID ]['input_name'] . '[' . $field_ID . ']'
311
+						: $field_ID;
312
+					// css class for input
313
+					$class = isset($input_types[ $field_ID ]['class']) && ! empty($input_types[ $field_ID ]['class'])
314
+						? ' ' . $input_types[ $field_ID ]['class']
315
+						: '';
316
+					// whether to apply htmlentities to answer
317
+					$htmlentities = isset($input_types[ $field_ID ]['htmlentities'])
318
+						? $input_types[ $field_ID ]['htmlentities']
319
+						: true;
320
+					// whether to apply htmlentities to answer
321
+					$label_b4 = isset($input_types[ $field_ID ]['label_b4'])
322
+						? $input_types[ $field_ID ]['label_b4']
323
+						: false;
324
+					// whether to apply htmlentities to answer
325
+					$use_desc_4_label = isset($input_types[ $field_ID ]['use_desc_4_label'])
326
+						? $input_types[ $field_ID ]['use_desc_4_label']
327
+						: false;
328
+					// whether input is disabled
329
+					$disabled = isset($input_types[ $field_ID ]['disabled']) && $input_types[ $field_ID ]['disabled'];
330
+
331
+					// create EE_Question_Form_Input object
332
+					$QFI = new EE_Question_Form_Input(
333
+						EE_Question::new_instance(
334
+							[
335
+								'QST_ID'           => 0,
336
+								'QST_display_text' => $field->get_nicename(),
337
+								'QST_type'         => $type,
338
+							]
339
+						),
340
+						EE_Answer::new_instance(
341
+							[
342
+								'ANS_ID'    => 0,
343
+								'QST_ID'    => 0,
344
+								'REG_ID'    => 0,
345
+								'ANS_value' => $value,
346
+							]
347
+						),
348
+						[
349
+							'input_id'         => $field_ID . '-' . $object->ID(),
350
+							'input_name'       => $input_name,
351
+							'input_class'      => $field_ID . $class,
352
+							'input_prefix'     => '',
353
+							'append_qstn_id'   => false,
354
+							'htmlentities'     => $htmlentities,
355
+							'label_b4'         => $label_b4,
356
+							'use_desc_4_label' => $use_desc_4_label,
357
+						]
358
+					);
359
+					// does question type have options ?
360
+					if (
361
+						in_array($type, ['DROPDOWN', 'RADIO_BTN', 'CHECKBOX'])
362
+						&& isset($input_types[ $field_ID ])
363
+						&& isset($input_types[ $field_ID ]['options'])
364
+					) {
365
+						foreach ($input_types[ $field_ID ]['options'] as $option) {
366
+							$option    = stripslashes_deep($option);
367
+							$option_id = ! empty($option['id']) ? $option['id'] : 0;
368
+							$QSO       = EE_Question_Option::new_instance(
369
+								[
370
+									'QSO_value'   => (string) $option_id,
371
+									'QSO_desc'    => $option['text'],
372
+									'QSO_deleted' => false,
373
+								]
374
+							);
375
+							// all QST (and ANS) properties can be accessed indirectly thru QFI
376
+							$QFI->add_temp_option($QSO);
377
+						}
378
+					}
379
+					// we don't want ppl manually changing primary keys cuz that would just lead to total craziness man
380
+					if ($disabled || $field_ID == $object->get_model()->primary_key_name()) {
381
+						$QFI->set('QST_disabled', true);
382
+					}
383
+					$inputs[ $field_ID ] = $QFI;
384
+				}
385
+			}
386
+		}
387
+		return $inputs;
388
+	}
389
+
390
+
391
+	/**
392
+	 *    add_temp_option
393
+	 *
394
+	 * @access public
395
+	 * @param EE_Question_Option $QSO EE_Question_Option
396
+	 * @return void
397
+	 */
398
+	public function add_temp_option(EE_Question_Option $QSO)
399
+	{
400
+		$this->_QST->add_temp_option($QSO);
401
+	}
402
+
403
+
404
+	/**
405
+	 * set property values for question form input
406
+	 *
407
+	 * @access public
408
+	 * @param string $property
409
+	 * @param mixed  $value
410
+	 * @return void
411
+	 * @throws EE_Error
412
+	 * @throws ReflectionException
413
+	 */
414
+	public function set($property = null, $value = null)
415
+	{
416
+		if (! empty($property)) {
417
+			if (EEM_Question::instance()->has_field($property)) {
418
+				$this->_QST->set($property, $value);
419
+			} elseif (EEM_Answer::instance()->has_field($property)) {
420
+				$this->_ANS->set($property, $value);
421
+			} elseif ($this->_question_form_input_property_exists(__CLASS__, $property)) {
422
+				$this->{$property} = $value;
423
+			}
424
+		}
425
+	}
426
+
427
+
428
+	/**
429
+	 *    _question_form_input_property_exists
430
+	 *
431
+	 * @access public
432
+	 * @param boolean      $notDeletedOptionsOnly            1
433
+	 *                                                       whether to return ALL options, or only the ones which have
434
+	 *                                                       not yet been deleted
435
+	 * @param string|array $selected_value_to_always_include , when retrieving options to an ANSWERED question,
436
+	 *                                                       we want to usually only show non-deleted options AND the
437
+	 *                                                       value that was selected for the answer, whether it was
438
+	 *                                                       trashed or not.
439
+	 * @return EE_Question_Option
440
+	 */
441
+	public function options($notDeletedOptionsOnly = true, $selected_value_to_always_include = null)
442
+	{
443
+		$temp_options = $this->_QST->temp_options();
444
+		return ! empty($temp_options)
445
+			? $temp_options
446
+			: $this->_QST->options(
447
+				$notDeletedOptionsOnly,
448
+				$selected_value_to_always_include
449
+			);
450
+	}
451
+
452
+
453
+	/**
454
+	 *    get_meta
455
+	 *
456
+	 * @access public
457
+	 * @param mixed $key
458
+	 * @return mixed
459
+	 */
460
+	public function get_meta($key = false)
461
+	{
462
+		return $key && isset($this->_QST_meta[ $key ]) ? $this->_QST_meta[ $key ] : false;
463
+	}
464 464
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Line_Item.class.php 2 patches
Spacing   +14 added lines, -14 removed lines patch added patch discarded remove patch
@@ -88,7 +88,7 @@  discard block
 block discarded – undo
88 88
     protected function __construct($fieldValues = array(), $bydb = false, $timezone = '')
89 89
     {
90 90
         parent::__construct($fieldValues, $bydb, $timezone);
91
-        if (! $this->get('LIN_code')) {
91
+        if ( ! $this->get('LIN_code')) {
92 92
             $this->set_code($this->generate_code());
93 93
         }
94 94
     }
@@ -155,7 +155,7 @@  discard block
 block discarded – undo
155 155
     public function name()
156 156
     {
157 157
         $name = $this->get('LIN_name');
158
-        if (! $name) {
158
+        if ( ! $name) {
159 159
             $name = ucwords(str_replace('-', ' ', $this->type()));
160 160
         }
161 161
         return $name;
@@ -615,7 +615,7 @@  discard block
 block discarded – undo
615 615
                 )
616 616
             );
617 617
         }
618
-        if (! is_array($this->_children)) {
618
+        if ( ! is_array($this->_children)) {
619 619
             $this->_children = array();
620 620
         }
621 621
         return $this->_children;
@@ -856,7 +856,7 @@  discard block
 block discarded – undo
856 856
             }
857 857
             return $line_item->save();
858 858
         }
859
-        $this->_children[ $line_item->code() ] = $line_item;
859
+        $this->_children[$line_item->code()] = $line_item;
860 860
         if ($line_item->parent() !== $this) {
861 861
             $line_item->set_parent($this);
862 862
         }
@@ -880,7 +880,7 @@  discard block
 block discarded – undo
880 880
     public function set_parent($line_item)
881 881
     {
882 882
         if ($this->ID()) {
883
-            if (! $line_item->ID()) {
883
+            if ( ! $line_item->ID()) {
884 884
                 $line_item->save();
885 885
             }
886 886
             $this->set_parent_ID($line_item->ID());
@@ -912,8 +912,8 @@  discard block
 block discarded – undo
912 912
                 array(array('LIN_parent' => $this->ID(), 'LIN_code' => $code))
913 913
             );
914 914
         }
915
-        return isset($this->_children[ $code ])
916
-            ? $this->_children[ $code ]
915
+        return isset($this->_children[$code])
916
+            ? $this->_children[$code]
917 917
             : null;
918 918
     }
919 919
 
@@ -973,8 +973,8 @@  discard block
 block discarded – undo
973 973
             }
974 974
             return $items_deleted;
975 975
         }
976
-        if (isset($this->_children[ $code ])) {
977
-            unset($this->_children[ $code ]);
976
+        if (isset($this->_children[$code])) {
977
+            unset($this->_children[$code]);
978 978
             return 1;
979 979
         }
980 980
         return 0;
@@ -1015,7 +1015,7 @@  discard block
 block discarded – undo
1015 1015
     public function generate_code()
1016 1016
     {
1017 1017
         // each line item in the cart requires a unique identifier
1018
-        return md5($this->get('OBJ_type') . $this->get('OBJ_ID') . microtime());
1018
+        return md5($this->get('OBJ_type').$this->get('OBJ_ID').microtime());
1019 1019
     }
1020 1020
 
1021 1021
 
@@ -1228,7 +1228,7 @@  discard block
 block discarded – undo
1228 1228
         $has_children = ! empty($my_children);
1229 1229
         if ($has_children && $this->is_line_item()) {
1230 1230
             $total = $this->_recalculate_pretax_total_for_line_item($total, $my_children);
1231
-        } elseif (! $has_children && ($this->is_sub_line_item() || $this->is_line_item())) {
1231
+        } elseif ( ! $has_children && ($this->is_sub_line_item() || $this->is_line_item())) {
1232 1232
             $total = $this->unit_price() * $this->quantity();
1233 1233
         } elseif ($this->is_sub_total() || $this->is_total()) {
1234 1234
             $total = $this->_recalculate_pretax_total_for_subtotal($total, $my_children);
@@ -1243,13 +1243,13 @@  discard block
 block discarded – undo
1243 1243
             if ($this->OBJ_type() !== EEM_Line_Item::OBJ_TYPE_EVENT) {
1244 1244
                 $this->set_quantity(1);
1245 1245
             }
1246
-            if (! $this->is_percent()) {
1246
+            if ( ! $this->is_percent()) {
1247 1247
                 $this->set_unit_price($total);
1248 1248
             }
1249 1249
         }
1250 1250
         // we don't want to bother saving grand totals, because that needs to factor in taxes anyways
1251 1251
         // so it ought to be
1252
-        if (! $this->is_total()) {
1252
+        if ( ! $this->is_total()) {
1253 1253
             $this->set_total($total);
1254 1254
             // if not a percent line item, make sure we keep the unit price in sync
1255 1255
             if (
@@ -1597,7 +1597,7 @@  discard block
 block discarded – undo
1597 1597
     public function save_this_and_descendants_to_txn($txn_id = null)
1598 1598
     {
1599 1599
         $count = 0;
1600
-        if (! $txn_id) {
1600
+        if ( ! $txn_id) {
1601 1601
             $txn_id = $this->TXN_ID();
1602 1602
         }
1603 1603
         $this->set_TXN_ID($txn_id);
Please login to merge, or discard this patch.
Indentation   +1738 added lines, -1738 removed lines patch added patch discarded remove patch
@@ -13,1742 +13,1742 @@
 block discarded – undo
13 13
  */
14 14
 class EE_Line_Item extends EE_Base_Class implements EEI_Line_Item
15 15
 {
16
-    /**
17
-     * for children line items (currently not a normal relation)
18
-     *
19
-     * @type EE_Line_Item[]
20
-     */
21
-    protected $_children = array();
22
-
23
-    /**
24
-     * for the parent line item
25
-     *
26
-     * @var EE_Line_Item
27
-     */
28
-    protected $_parent;
29
-
30
-
31
-    /**
32
-     * @param array  $props_n_values          incoming values
33
-     * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
34
-     *                                        used.)
35
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
36
-     *                                        date_format and the second value is the time format
37
-     * @return EE_Line_Item
38
-     * @throws EE_Error
39
-     * @throws InvalidArgumentException
40
-     * @throws InvalidDataTypeException
41
-     * @throws InvalidInterfaceException
42
-     * @throws ReflectionException
43
-     */
44
-    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
45
-    {
46
-        $has_object = parent::_check_for_object(
47
-            $props_n_values,
48
-            __CLASS__,
49
-            $timezone,
50
-            $date_formats
51
-        );
52
-        return $has_object
53
-            ? $has_object
54
-            : new self($props_n_values, false, $timezone);
55
-    }
56
-
57
-
58
-    /**
59
-     * @param array  $props_n_values  incoming values from the database
60
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
61
-     *                                the website will be used.
62
-     * @return EE_Line_Item
63
-     * @throws EE_Error
64
-     * @throws InvalidArgumentException
65
-     * @throws InvalidDataTypeException
66
-     * @throws InvalidInterfaceException
67
-     * @throws ReflectionException
68
-     */
69
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
70
-    {
71
-        return new self($props_n_values, true, $timezone);
72
-    }
73
-
74
-
75
-    /**
76
-     * Adds some defaults if they're not specified
77
-     *
78
-     * @param array  $fieldValues
79
-     * @param bool   $bydb
80
-     * @param string $timezone
81
-     * @throws EE_Error
82
-     * @throws InvalidArgumentException
83
-     * @throws InvalidDataTypeException
84
-     * @throws InvalidInterfaceException
85
-     * @throws ReflectionException
86
-     */
87
-    protected function __construct($fieldValues = array(), $bydb = false, $timezone = '')
88
-    {
89
-        parent::__construct($fieldValues, $bydb, $timezone);
90
-        if (! $this->get('LIN_code')) {
91
-            $this->set_code($this->generate_code());
92
-        }
93
-    }
94
-
95
-
96
-    /**
97
-     * Gets ID
98
-     *
99
-     * @return int
100
-     * @throws EE_Error
101
-     * @throws InvalidArgumentException
102
-     * @throws InvalidDataTypeException
103
-     * @throws InvalidInterfaceException
104
-     * @throws ReflectionException
105
-     */
106
-    public function ID()
107
-    {
108
-        return $this->get('LIN_ID');
109
-    }
110
-
111
-
112
-    /**
113
-     * Gets TXN_ID
114
-     *
115
-     * @return int
116
-     * @throws EE_Error
117
-     * @throws InvalidArgumentException
118
-     * @throws InvalidDataTypeException
119
-     * @throws InvalidInterfaceException
120
-     * @throws ReflectionException
121
-     */
122
-    public function TXN_ID()
123
-    {
124
-        return $this->get('TXN_ID');
125
-    }
126
-
127
-
128
-    /**
129
-     * Sets TXN_ID
130
-     *
131
-     * @param int $TXN_ID
132
-     * @throws EE_Error
133
-     * @throws InvalidArgumentException
134
-     * @throws InvalidDataTypeException
135
-     * @throws InvalidInterfaceException
136
-     * @throws ReflectionException
137
-     */
138
-    public function set_TXN_ID($TXN_ID)
139
-    {
140
-        $this->set('TXN_ID', $TXN_ID);
141
-    }
142
-
143
-
144
-    /**
145
-     * Gets name
146
-     *
147
-     * @return string
148
-     * @throws EE_Error
149
-     * @throws InvalidArgumentException
150
-     * @throws InvalidDataTypeException
151
-     * @throws InvalidInterfaceException
152
-     * @throws ReflectionException
153
-     */
154
-    public function name()
155
-    {
156
-        $name = $this->get('LIN_name');
157
-        if (! $name) {
158
-            $name = ucwords(str_replace('-', ' ', $this->type()));
159
-        }
160
-        return $name;
161
-    }
162
-
163
-
164
-    /**
165
-     * Sets name
166
-     *
167
-     * @param string $name
168
-     * @throws EE_Error
169
-     * @throws InvalidArgumentException
170
-     * @throws InvalidDataTypeException
171
-     * @throws InvalidInterfaceException
172
-     * @throws ReflectionException
173
-     */
174
-    public function set_name($name)
175
-    {
176
-        $this->set('LIN_name', $name);
177
-    }
178
-
179
-
180
-    /**
181
-     * Gets desc
182
-     *
183
-     * @return string
184
-     * @throws EE_Error
185
-     * @throws InvalidArgumentException
186
-     * @throws InvalidDataTypeException
187
-     * @throws InvalidInterfaceException
188
-     * @throws ReflectionException
189
-     */
190
-    public function desc()
191
-    {
192
-        return $this->get('LIN_desc');
193
-    }
194
-
195
-
196
-    /**
197
-     * Sets desc
198
-     *
199
-     * @param string $desc
200
-     * @throws EE_Error
201
-     * @throws InvalidArgumentException
202
-     * @throws InvalidDataTypeException
203
-     * @throws InvalidInterfaceException
204
-     * @throws ReflectionException
205
-     */
206
-    public function set_desc($desc)
207
-    {
208
-        $this->set('LIN_desc', $desc);
209
-    }
210
-
211
-
212
-    /**
213
-     * Gets quantity
214
-     *
215
-     * @return int
216
-     * @throws EE_Error
217
-     * @throws InvalidArgumentException
218
-     * @throws InvalidDataTypeException
219
-     * @throws InvalidInterfaceException
220
-     * @throws ReflectionException
221
-     */
222
-    public function quantity()
223
-    {
224
-        return $this->get('LIN_quantity');
225
-    }
226
-
227
-
228
-    /**
229
-     * Sets quantity
230
-     *
231
-     * @param int $quantity
232
-     * @throws EE_Error
233
-     * @throws InvalidArgumentException
234
-     * @throws InvalidDataTypeException
235
-     * @throws InvalidInterfaceException
236
-     * @throws ReflectionException
237
-     */
238
-    public function set_quantity($quantity)
239
-    {
240
-        $this->set('LIN_quantity', max($quantity, 0));
241
-    }
242
-
243
-
244
-    /**
245
-     * Gets item_id
246
-     *
247
-     * @return string
248
-     * @throws EE_Error
249
-     * @throws InvalidArgumentException
250
-     * @throws InvalidDataTypeException
251
-     * @throws InvalidInterfaceException
252
-     * @throws ReflectionException
253
-     */
254
-    public function OBJ_ID()
255
-    {
256
-        return $this->get('OBJ_ID');
257
-    }
258
-
259
-
260
-    /**
261
-     * Sets item_id
262
-     *
263
-     * @param string $item_id
264
-     * @throws EE_Error
265
-     * @throws InvalidArgumentException
266
-     * @throws InvalidDataTypeException
267
-     * @throws InvalidInterfaceException
268
-     * @throws ReflectionException
269
-     */
270
-    public function set_OBJ_ID($item_id)
271
-    {
272
-        $this->set('OBJ_ID', $item_id);
273
-    }
274
-
275
-
276
-    /**
277
-     * Gets item_type
278
-     *
279
-     * @return string
280
-     * @throws EE_Error
281
-     * @throws InvalidArgumentException
282
-     * @throws InvalidDataTypeException
283
-     * @throws InvalidInterfaceException
284
-     * @throws ReflectionException
285
-     */
286
-    public function OBJ_type()
287
-    {
288
-        return $this->get('OBJ_type');
289
-    }
290
-
291
-
292
-    /**
293
-     * Gets item_type
294
-     *
295
-     * @return string
296
-     * @throws EE_Error
297
-     * @throws InvalidArgumentException
298
-     * @throws InvalidDataTypeException
299
-     * @throws InvalidInterfaceException
300
-     * @throws ReflectionException
301
-     */
302
-    public function OBJ_type_i18n()
303
-    {
304
-        $obj_type = $this->OBJ_type();
305
-        switch ($obj_type) {
306
-            case EEM_Line_Item::OBJ_TYPE_EVENT:
307
-                $obj_type = esc_html__('Event', 'event_espresso');
308
-                break;
309
-            case EEM_Line_Item::OBJ_TYPE_PRICE:
310
-                $obj_type = esc_html__('Price', 'event_espresso');
311
-                break;
312
-            case EEM_Line_Item::OBJ_TYPE_PROMOTION:
313
-                $obj_type = esc_html__('Promotion', 'event_espresso');
314
-                break;
315
-            case EEM_Line_Item::OBJ_TYPE_TICKET:
316
-                $obj_type = esc_html__('Ticket', 'event_espresso');
317
-                break;
318
-            case EEM_Line_Item::OBJ_TYPE_TRANSACTION:
319
-                $obj_type = esc_html__('Transaction', 'event_espresso');
320
-                break;
321
-        }
322
-        return apply_filters('FHEE__EE_Line_Item__OBJ_type_i18n', $obj_type, $this);
323
-    }
324
-
325
-
326
-    /**
327
-     * Sets item_type
328
-     *
329
-     * @param string $OBJ_type
330
-     * @throws EE_Error
331
-     * @throws InvalidArgumentException
332
-     * @throws InvalidDataTypeException
333
-     * @throws InvalidInterfaceException
334
-     * @throws ReflectionException
335
-     */
336
-    public function set_OBJ_type($OBJ_type)
337
-    {
338
-        $this->set('OBJ_type', $OBJ_type);
339
-    }
340
-
341
-
342
-    /**
343
-     * Gets unit_price
344
-     *
345
-     * @return float
346
-     * @throws EE_Error
347
-     * @throws InvalidArgumentException
348
-     * @throws InvalidDataTypeException
349
-     * @throws InvalidInterfaceException
350
-     * @throws ReflectionException
351
-     */
352
-    public function unit_price()
353
-    {
354
-        return $this->get('LIN_unit_price');
355
-    }
356
-
357
-
358
-    /**
359
-     * Sets unit_price
360
-     *
361
-     * @param float $unit_price
362
-     * @throws EE_Error
363
-     * @throws InvalidArgumentException
364
-     * @throws InvalidDataTypeException
365
-     * @throws InvalidInterfaceException
366
-     * @throws ReflectionException
367
-     */
368
-    public function set_unit_price($unit_price)
369
-    {
370
-        $this->set('LIN_unit_price', $unit_price);
371
-    }
372
-
373
-
374
-    /**
375
-     * Checks if this item is a percentage modifier or not
376
-     *
377
-     * @return boolean
378
-     * @throws EE_Error
379
-     * @throws InvalidArgumentException
380
-     * @throws InvalidDataTypeException
381
-     * @throws InvalidInterfaceException
382
-     * @throws ReflectionException
383
-     */
384
-    public function is_percent()
385
-    {
386
-        if ($this->is_tax_sub_total()) {
387
-            // tax subtotals HAVE a percent on them, that percentage only applies
388
-            // to taxable items, so its' an exception. Treat it like a flat line item
389
-            return false;
390
-        }
391
-        $unit_price = abs($this->get('LIN_unit_price'));
392
-        $percent = abs($this->get('LIN_percent'));
393
-        if ($unit_price < .001 && $percent) {
394
-            return true;
395
-        }
396
-        if ($unit_price >= .001 && ! $percent) {
397
-            return false;
398
-        }
399
-        if ($unit_price >= .001 && $percent) {
400
-            throw new EE_Error(
401
-                sprintf(
402
-                    esc_html__(
403
-                        'A Line Item can not have a unit price of (%s) AND a percent (%s)!',
404
-                        'event_espresso'
405
-                    ),
406
-                    $unit_price,
407
-                    $percent
408
-                )
409
-            );
410
-        }
411
-        // if they're both 0, assume its not a percent item
412
-        return false;
413
-    }
414
-
415
-
416
-    /**
417
-     * Gets percent (between 100-.001)
418
-     *
419
-     * @return float
420
-     * @throws EE_Error
421
-     * @throws InvalidArgumentException
422
-     * @throws InvalidDataTypeException
423
-     * @throws InvalidInterfaceException
424
-     * @throws ReflectionException
425
-     */
426
-    public function percent()
427
-    {
428
-        return $this->get('LIN_percent');
429
-    }
430
-
431
-
432
-    /**
433
-     * Sets percent (between 100-0.01)
434
-     *
435
-     * @param float $percent
436
-     * @throws EE_Error
437
-     * @throws InvalidArgumentException
438
-     * @throws InvalidDataTypeException
439
-     * @throws InvalidInterfaceException
440
-     * @throws ReflectionException
441
-     */
442
-    public function set_percent($percent)
443
-    {
444
-        $this->set('LIN_percent', $percent);
445
-    }
446
-
447
-
448
-    /**
449
-     * Gets total
450
-     *
451
-     * @return float
452
-     * @throws EE_Error
453
-     * @throws InvalidArgumentException
454
-     * @throws InvalidDataTypeException
455
-     * @throws InvalidInterfaceException
456
-     * @throws ReflectionException
457
-     */
458
-    public function total()
459
-    {
460
-        return $this->get('LIN_total');
461
-    }
462
-
463
-
464
-    /**
465
-     * Sets total
466
-     *
467
-     * @param float $total
468
-     * @throws EE_Error
469
-     * @throws InvalidArgumentException
470
-     * @throws InvalidDataTypeException
471
-     * @throws InvalidInterfaceException
472
-     * @throws ReflectionException
473
-     */
474
-    public function set_total($total)
475
-    {
476
-        $this->set('LIN_total', $total);
477
-    }
478
-
479
-
480
-    /**
481
-     * Gets order
482
-     *
483
-     * @return int
484
-     * @throws EE_Error
485
-     * @throws InvalidArgumentException
486
-     * @throws InvalidDataTypeException
487
-     * @throws InvalidInterfaceException
488
-     * @throws ReflectionException
489
-     */
490
-    public function order()
491
-    {
492
-        return $this->get('LIN_order');
493
-    }
494
-
495
-
496
-    /**
497
-     * Sets order
498
-     *
499
-     * @param int $order
500
-     * @throws EE_Error
501
-     * @throws InvalidArgumentException
502
-     * @throws InvalidDataTypeException
503
-     * @throws InvalidInterfaceException
504
-     * @throws ReflectionException
505
-     */
506
-    public function set_order($order)
507
-    {
508
-        $this->set('LIN_order', $order);
509
-    }
510
-
511
-
512
-    /**
513
-     * Gets parent
514
-     *
515
-     * @return int
516
-     * @throws EE_Error
517
-     * @throws InvalidArgumentException
518
-     * @throws InvalidDataTypeException
519
-     * @throws InvalidInterfaceException
520
-     * @throws ReflectionException
521
-     */
522
-    public function parent_ID()
523
-    {
524
-        return $this->get('LIN_parent');
525
-    }
526
-
527
-
528
-    /**
529
-     * Sets parent
530
-     *
531
-     * @param int $parent
532
-     * @throws EE_Error
533
-     * @throws InvalidArgumentException
534
-     * @throws InvalidDataTypeException
535
-     * @throws InvalidInterfaceException
536
-     * @throws ReflectionException
537
-     */
538
-    public function set_parent_ID($parent)
539
-    {
540
-        $this->set('LIN_parent', $parent);
541
-    }
542
-
543
-
544
-    /**
545
-     * Gets type
546
-     *
547
-     * @return string
548
-     * @throws EE_Error
549
-     * @throws InvalidArgumentException
550
-     * @throws InvalidDataTypeException
551
-     * @throws InvalidInterfaceException
552
-     * @throws ReflectionException
553
-     */
554
-    public function type()
555
-    {
556
-        return $this->get('LIN_type');
557
-    }
558
-
559
-
560
-    /**
561
-     * Sets type
562
-     *
563
-     * @param string $type
564
-     * @throws EE_Error
565
-     * @throws InvalidArgumentException
566
-     * @throws InvalidDataTypeException
567
-     * @throws InvalidInterfaceException
568
-     * @throws ReflectionException
569
-     */
570
-    public function set_type($type)
571
-    {
572
-        $this->set('LIN_type', $type);
573
-    }
574
-
575
-
576
-    /**
577
-     * Gets the line item of which this item is a composite. Eg, if this is a subtotal, the parent might be a total\
578
-     * If this line item is saved to the DB, fetches the parent from the DB. However, if this line item isn't in the DB
579
-     * it uses its cached reference to its parent line item (which would have been set by `EE_Line_Item::set_parent()`
580
-     * or indirectly by `EE_Line_item::add_child_line_item()`)
581
-     *
582
-     * @return EE_Base_Class|EE_Line_Item
583
-     * @throws EE_Error
584
-     * @throws InvalidArgumentException
585
-     * @throws InvalidDataTypeException
586
-     * @throws InvalidInterfaceException
587
-     * @throws ReflectionException
588
-     */
589
-    public function parent()
590
-    {
591
-        return $this->ID()
592
-            ? $this->get_model()->get_one_by_ID($this->parent_ID())
593
-            : $this->_parent;
594
-    }
595
-
596
-
597
-    /**
598
-     * Gets ALL the children of this line item (ie, all the parts that contribute towards this total).
599
-     *
600
-     * @return EE_Base_Class[]|EE_Line_Item[]
601
-     * @throws EE_Error
602
-     * @throws InvalidArgumentException
603
-     * @throws InvalidDataTypeException
604
-     * @throws InvalidInterfaceException
605
-     * @throws ReflectionException
606
-     */
607
-    public function children()
608
-    {
609
-        if ($this->ID()) {
610
-            return $this->get_model()->get_all(
611
-                array(
612
-                    array('LIN_parent' => $this->ID()),
613
-                    'order_by' => array('LIN_order' => 'ASC'),
614
-                )
615
-            );
616
-        }
617
-        if (! is_array($this->_children)) {
618
-            $this->_children = array();
619
-        }
620
-        return $this->_children;
621
-    }
622
-
623
-
624
-    /**
625
-     * Gets code
626
-     *
627
-     * @return string
628
-     * @throws EE_Error
629
-     * @throws InvalidArgumentException
630
-     * @throws InvalidDataTypeException
631
-     * @throws InvalidInterfaceException
632
-     * @throws ReflectionException
633
-     */
634
-    public function code()
635
-    {
636
-        return $this->get('LIN_code');
637
-    }
638
-
639
-
640
-    /**
641
-     * Sets code
642
-     *
643
-     * @param string $code
644
-     * @throws EE_Error
645
-     * @throws InvalidArgumentException
646
-     * @throws InvalidDataTypeException
647
-     * @throws InvalidInterfaceException
648
-     * @throws ReflectionException
649
-     */
650
-    public function set_code($code)
651
-    {
652
-        $this->set('LIN_code', $code);
653
-    }
654
-
655
-
656
-    /**
657
-     * Gets is_taxable
658
-     *
659
-     * @return boolean
660
-     * @throws EE_Error
661
-     * @throws InvalidArgumentException
662
-     * @throws InvalidDataTypeException
663
-     * @throws InvalidInterfaceException
664
-     * @throws ReflectionException
665
-     */
666
-    public function is_taxable()
667
-    {
668
-        return $this->get('LIN_is_taxable');
669
-    }
670
-
671
-
672
-    /**
673
-     * Sets is_taxable
674
-     *
675
-     * @param boolean $is_taxable
676
-     * @throws EE_Error
677
-     * @throws InvalidArgumentException
678
-     * @throws InvalidDataTypeException
679
-     * @throws InvalidInterfaceException
680
-     * @throws ReflectionException
681
-     */
682
-    public function set_is_taxable($is_taxable)
683
-    {
684
-        $this->set('LIN_is_taxable', $is_taxable);
685
-    }
686
-
687
-
688
-    /**
689
-     * Gets the object that this model-joins-to.
690
-     * returns one of the model objects that the field OBJ_ID can point to... see the 'OBJ_ID' field on
691
-     * EEM_Promotion_Object
692
-     *        Eg, if this line item join model object is for a ticket, this will return the EE_Ticket object
693
-     *
694
-     * @return EE_Base_Class | NULL
695
-     * @throws EE_Error
696
-     * @throws InvalidArgumentException
697
-     * @throws InvalidDataTypeException
698
-     * @throws InvalidInterfaceException
699
-     * @throws ReflectionException
700
-     */
701
-    public function get_object()
702
-    {
703
-        $model_name_of_related_obj = $this->OBJ_type();
704
-        return $this->get_model()->has_relation($model_name_of_related_obj)
705
-            ? $this->get_first_related($model_name_of_related_obj)
706
-            : null;
707
-    }
708
-
709
-
710
-    /**
711
-     * Like EE_Line_Item::get_object(), but can only ever actually return an EE_Ticket.
712
-     * (IE, if this line item is for a price or something else, will return NULL)
713
-     *
714
-     * @param array $query_params
715
-     * @return EE_Base_Class|EE_Ticket
716
-     * @throws EE_Error
717
-     * @throws InvalidArgumentException
718
-     * @throws InvalidDataTypeException
719
-     * @throws InvalidInterfaceException
720
-     * @throws ReflectionException
721
-     */
722
-    public function ticket($query_params = array())
723
-    {
724
-        // we're going to assume that when this method is called
725
-        // we always want to receive the attached ticket EVEN if that ticket is archived.
726
-        // This can be overridden via the incoming $query_params argument
727
-        $remove_defaults = array('default_where_conditions' => 'none');
728
-        $query_params = array_merge($remove_defaults, $query_params);
729
-        return $this->get_first_related(EEM_Line_Item::OBJ_TYPE_TICKET, $query_params);
730
-    }
731
-
732
-
733
-    /**
734
-     * Gets the EE_Datetime that's related to the ticket, IF this is for a ticket
735
-     *
736
-     * @return EE_Datetime | NULL
737
-     * @throws EE_Error
738
-     * @throws InvalidArgumentException
739
-     * @throws InvalidDataTypeException
740
-     * @throws InvalidInterfaceException
741
-     * @throws ReflectionException
742
-     */
743
-    public function get_ticket_datetime()
744
-    {
745
-        if ($this->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET) {
746
-            $ticket = $this->ticket();
747
-            if ($ticket instanceof EE_Ticket) {
748
-                $datetime = $ticket->first_datetime();
749
-                if ($datetime instanceof EE_Datetime) {
750
-                    return $datetime;
751
-                }
752
-            }
753
-        }
754
-        return null;
755
-    }
756
-
757
-
758
-    /**
759
-     * Gets the event's name that's related to the ticket, if this is for
760
-     * a ticket
761
-     *
762
-     * @return string
763
-     * @throws EE_Error
764
-     * @throws InvalidArgumentException
765
-     * @throws InvalidDataTypeException
766
-     * @throws InvalidInterfaceException
767
-     * @throws ReflectionException
768
-     */
769
-    public function ticket_event_name()
770
-    {
771
-        $event_name = esc_html__('Unknown', 'event_espresso');
772
-        $event = $this->ticket_event();
773
-        if ($event instanceof EE_Event) {
774
-            $event_name = $event->name();
775
-        }
776
-        return $event_name;
777
-    }
778
-
779
-
780
-    /**
781
-     * Gets the event that's related to the ticket, if this line item represents a ticket.
782
-     *
783
-     * @return EE_Event|null
784
-     * @throws EE_Error
785
-     * @throws InvalidArgumentException
786
-     * @throws InvalidDataTypeException
787
-     * @throws InvalidInterfaceException
788
-     * @throws ReflectionException
789
-     */
790
-    public function ticket_event()
791
-    {
792
-        $event = null;
793
-        $ticket = $this->ticket();
794
-        if ($ticket instanceof EE_Ticket) {
795
-            $datetime = $ticket->first_datetime();
796
-            if ($datetime instanceof EE_Datetime) {
797
-                $event = $datetime->event();
798
-            }
799
-        }
800
-        return $event;
801
-    }
802
-
803
-
804
-    /**
805
-     * Gets the first datetime for this lien item, assuming it's for a ticket
806
-     *
807
-     * @param string $date_format
808
-     * @param string $time_format
809
-     * @return string
810
-     * @throws EE_Error
811
-     * @throws InvalidArgumentException
812
-     * @throws InvalidDataTypeException
813
-     * @throws InvalidInterfaceException
814
-     * @throws ReflectionException
815
-     */
816
-    public function ticket_datetime_start($date_format = '', $time_format = '')
817
-    {
818
-        $first_datetime_string = esc_html__('Unknown', 'event_espresso');
819
-        $datetime = $this->get_ticket_datetime();
820
-        if ($datetime) {
821
-            $first_datetime_string = $datetime->start_date_and_time($date_format, $time_format);
822
-        }
823
-        return $first_datetime_string;
824
-    }
825
-
826
-
827
-    /**
828
-     * Adds the line item as a child to this line item. If there is another child line
829
-     * item with the same LIN_code, it is overwritten by this new one
830
-     *
831
-     * @param EEI_Line_Item $line_item
832
-     * @param bool          $set_order
833
-     * @return bool success
834
-     * @throws EE_Error
835
-     * @throws InvalidArgumentException
836
-     * @throws InvalidDataTypeException
837
-     * @throws InvalidInterfaceException
838
-     * @throws ReflectionException
839
-     */
840
-    public function add_child_line_item(EEI_Line_Item $line_item, $set_order = true)
841
-    {
842
-        // should we calculate the LIN_order for this line item ?
843
-        if ($set_order || $line_item->order() === null) {
844
-            $line_item->set_order(count($this->children()));
845
-        }
846
-        if ($this->ID()) {
847
-            // check for any duplicate line items (with the same code), if so, this replaces it
848
-            $line_item_with_same_code = $this->get_child_line_item($line_item->code());
849
-            if ($line_item_with_same_code instanceof EE_Line_Item && $line_item_with_same_code !== $line_item) {
850
-                $this->delete_child_line_item($line_item_with_same_code->code());
851
-            }
852
-            $line_item->set_parent_ID($this->ID());
853
-            if ($this->TXN_ID()) {
854
-                $line_item->set_TXN_ID($this->TXN_ID());
855
-            }
856
-            return $line_item->save();
857
-        }
858
-        $this->_children[ $line_item->code() ] = $line_item;
859
-        if ($line_item->parent() !== $this) {
860
-            $line_item->set_parent($this);
861
-        }
862
-        return true;
863
-    }
864
-
865
-
866
-    /**
867
-     * Similar to EE_Base_Class::_add_relation_to, except this isn't a normal relation.
868
-     * If this line item is saved to the DB, this is just a wrapper for set_parent_ID() and save()
869
-     * However, if this line item is NOT saved to the DB, this just caches the parent on
870
-     * the EE_Line_Item::_parent property.
871
-     *
872
-     * @param EE_Line_Item $line_item
873
-     * @throws EE_Error
874
-     * @throws InvalidArgumentException
875
-     * @throws InvalidDataTypeException
876
-     * @throws InvalidInterfaceException
877
-     * @throws ReflectionException
878
-     */
879
-    public function set_parent($line_item)
880
-    {
881
-        if ($this->ID()) {
882
-            if (! $line_item->ID()) {
883
-                $line_item->save();
884
-            }
885
-            $this->set_parent_ID($line_item->ID());
886
-            $this->save();
887
-        } else {
888
-            $this->_parent = $line_item;
889
-            $this->set_parent_ID($line_item->ID());
890
-        }
891
-    }
892
-
893
-
894
-    /**
895
-     * Gets the child line item as specified by its code. Because this returns an object (by reference)
896
-     * you can modify this child line item and the parent (this object) can know about them
897
-     * because it also has a reference to that line item
898
-     *
899
-     * @param string $code
900
-     * @return EE_Base_Class|EE_Line_Item|EE_Soft_Delete_Base_Class|NULL
901
-     * @throws EE_Error
902
-     * @throws InvalidArgumentException
903
-     * @throws InvalidDataTypeException
904
-     * @throws InvalidInterfaceException
905
-     * @throws ReflectionException
906
-     */
907
-    public function get_child_line_item($code)
908
-    {
909
-        if ($this->ID()) {
910
-            return $this->get_model()->get_one(
911
-                array(array('LIN_parent' => $this->ID(), 'LIN_code' => $code))
912
-            );
913
-        }
914
-        return isset($this->_children[ $code ])
915
-            ? $this->_children[ $code ]
916
-            : null;
917
-    }
918
-
919
-
920
-    /**
921
-     * Returns how many items are deleted (or, if this item has not been saved ot the DB yet, just how many it HAD
922
-     * cached on it)
923
-     *
924
-     * @return int
925
-     * @throws EE_Error
926
-     * @throws InvalidArgumentException
927
-     * @throws InvalidDataTypeException
928
-     * @throws InvalidInterfaceException
929
-     * @throws ReflectionException
930
-     */
931
-    public function delete_children_line_items()
932
-    {
933
-        if ($this->ID()) {
934
-            return $this->get_model()->delete(array(array('LIN_parent' => $this->ID())));
935
-        }
936
-        $count = count($this->_children);
937
-        $this->_children = array();
938
-        return $count;
939
-    }
940
-
941
-
942
-    /**
943
-     * If this line item has been saved to the DB, deletes its child with LIN_code == $code. If this line
944
-     * HAS NOT been saved to the DB, removes the child line item with index $code.
945
-     * Also searches through the child's children for a matching line item. However, once a line item has been found
946
-     * and deleted, stops searching (so if there are line items with duplicate codes, only the first one found will be
947
-     * deleted)
948
-     *
949
-     * @param string $code
950
-     * @param bool   $stop_search_once_found
951
-     * @return int count of items deleted (or simply removed from the line item's cache, if not has not been saved to
952
-     *             the DB yet)
953
-     * @throws EE_Error
954
-     * @throws InvalidArgumentException
955
-     * @throws InvalidDataTypeException
956
-     * @throws InvalidInterfaceException
957
-     * @throws ReflectionException
958
-     */
959
-    public function delete_child_line_item($code, $stop_search_once_found = true)
960
-    {
961
-        if ($this->ID()) {
962
-            $items_deleted = 0;
963
-            if ($this->code() === $code) {
964
-                $items_deleted += EEH_Line_Item::delete_all_child_items($this);
965
-                $items_deleted += (int) $this->delete();
966
-                if ($stop_search_once_found) {
967
-                    return $items_deleted;
968
-                }
969
-            }
970
-            foreach ($this->children() as $child_line_item) {
971
-                $items_deleted += $child_line_item->delete_child_line_item($code, $stop_search_once_found);
972
-            }
973
-            return $items_deleted;
974
-        }
975
-        if (isset($this->_children[ $code ])) {
976
-            unset($this->_children[ $code ]);
977
-            return 1;
978
-        }
979
-        return 0;
980
-    }
981
-
982
-
983
-    /**
984
-     * If this line item is in the database, is of the type subtotal, and
985
-     * has no children, why do we have it? It should be deleted so this function
986
-     * does that
987
-     *
988
-     * @return boolean
989
-     * @throws EE_Error
990
-     * @throws InvalidArgumentException
991
-     * @throws InvalidDataTypeException
992
-     * @throws InvalidInterfaceException
993
-     * @throws ReflectionException
994
-     */
995
-    public function delete_if_childless_subtotal()
996
-    {
997
-        if ($this->ID() && $this->type() === EEM_Line_Item::type_sub_total && ! $this->children()) {
998
-            return $this->delete();
999
-        }
1000
-        return false;
1001
-    }
1002
-
1003
-
1004
-    /**
1005
-     * Creates a code and returns a string. doesn't assign the code to this model object
1006
-     *
1007
-     * @return string
1008
-     * @throws EE_Error
1009
-     * @throws InvalidArgumentException
1010
-     * @throws InvalidDataTypeException
1011
-     * @throws InvalidInterfaceException
1012
-     * @throws ReflectionException
1013
-     */
1014
-    public function generate_code()
1015
-    {
1016
-        // each line item in the cart requires a unique identifier
1017
-        return md5($this->get('OBJ_type') . $this->get('OBJ_ID') . microtime());
1018
-    }
1019
-
1020
-
1021
-    /**
1022
-     * @return bool
1023
-     * @throws EE_Error
1024
-     * @throws InvalidArgumentException
1025
-     * @throws InvalidDataTypeException
1026
-     * @throws InvalidInterfaceException
1027
-     * @throws ReflectionException
1028
-     */
1029
-    public function is_tax()
1030
-    {
1031
-        return $this->type() === EEM_Line_Item::type_tax;
1032
-    }
1033
-
1034
-
1035
-    /**
1036
-     * @return bool
1037
-     * @throws EE_Error
1038
-     * @throws InvalidArgumentException
1039
-     * @throws InvalidDataTypeException
1040
-     * @throws InvalidInterfaceException
1041
-     * @throws ReflectionException
1042
-     */
1043
-    public function is_tax_sub_total()
1044
-    {
1045
-        return $this->type() === EEM_Line_Item::type_tax_sub_total;
1046
-    }
1047
-
1048
-
1049
-    /**
1050
-     * @return bool
1051
-     * @throws EE_Error
1052
-     * @throws InvalidArgumentException
1053
-     * @throws InvalidDataTypeException
1054
-     * @throws InvalidInterfaceException
1055
-     * @throws ReflectionException
1056
-     */
1057
-    public function is_line_item()
1058
-    {
1059
-        return $this->type() === EEM_Line_Item::type_line_item;
1060
-    }
1061
-
1062
-
1063
-    /**
1064
-     * @return bool
1065
-     * @throws EE_Error
1066
-     * @throws InvalidArgumentException
1067
-     * @throws InvalidDataTypeException
1068
-     * @throws InvalidInterfaceException
1069
-     * @throws ReflectionException
1070
-     */
1071
-    public function is_sub_line_item()
1072
-    {
1073
-        return $this->type() === EEM_Line_Item::type_sub_line_item;
1074
-    }
1075
-
1076
-
1077
-    /**
1078
-     * @return bool
1079
-     * @throws EE_Error
1080
-     * @throws InvalidArgumentException
1081
-     * @throws InvalidDataTypeException
1082
-     * @throws InvalidInterfaceException
1083
-     * @throws ReflectionException
1084
-     */
1085
-    public function is_sub_total()
1086
-    {
1087
-        return $this->type() === EEM_Line_Item::type_sub_total;
1088
-    }
1089
-
1090
-
1091
-    /**
1092
-     * Whether or not this line item is a cancellation line item
1093
-     *
1094
-     * @return boolean
1095
-     * @throws EE_Error
1096
-     * @throws InvalidArgumentException
1097
-     * @throws InvalidDataTypeException
1098
-     * @throws InvalidInterfaceException
1099
-     * @throws ReflectionException
1100
-     */
1101
-    public function is_cancellation()
1102
-    {
1103
-        return EEM_Line_Item::type_cancellation === $this->type();
1104
-    }
1105
-
1106
-
1107
-    /**
1108
-     * @return bool
1109
-     * @throws EE_Error
1110
-     * @throws InvalidArgumentException
1111
-     * @throws InvalidDataTypeException
1112
-     * @throws InvalidInterfaceException
1113
-     * @throws ReflectionException
1114
-     */
1115
-    public function is_total()
1116
-    {
1117
-        return $this->type() === EEM_Line_Item::type_total;
1118
-    }
1119
-
1120
-
1121
-    /**
1122
-     * @return bool
1123
-     * @throws EE_Error
1124
-     * @throws InvalidArgumentException
1125
-     * @throws InvalidDataTypeException
1126
-     * @throws InvalidInterfaceException
1127
-     * @throws ReflectionException
1128
-     */
1129
-    public function is_cancelled()
1130
-    {
1131
-        return $this->type() === EEM_Line_Item::type_cancellation;
1132
-    }
1133
-
1134
-
1135
-    /**
1136
-     * @return string like '2, 004.00', formatted according to the localized currency
1137
-     * @throws EE_Error
1138
-     * @throws InvalidArgumentException
1139
-     * @throws InvalidDataTypeException
1140
-     * @throws InvalidInterfaceException
1141
-     * @throws ReflectionException
1142
-     */
1143
-    public function unit_price_no_code()
1144
-    {
1145
-        return $this->get_pretty('LIN_unit_price', 'no_currency_code');
1146
-    }
1147
-
1148
-
1149
-    /**
1150
-     * @return string like '2, 004.00', formatted according to the localized currency
1151
-     * @throws EE_Error
1152
-     * @throws InvalidArgumentException
1153
-     * @throws InvalidDataTypeException
1154
-     * @throws InvalidInterfaceException
1155
-     * @throws ReflectionException
1156
-     */
1157
-    public function total_no_code()
1158
-    {
1159
-        return $this->get_pretty('LIN_total', 'no_currency_code');
1160
-    }
1161
-
1162
-
1163
-    /**
1164
-     * Gets the final total on this item, taking taxes into account.
1165
-     * Has the side-effect of setting the sub-total as it was just calculated.
1166
-     * If this is used on a grand-total line item, also updates the transaction's
1167
-     * TXN_total (provided this line item is allowed to persist, otherwise we don't
1168
-     * want to change a persistable transaction with info from a non-persistent line item)
1169
-     *
1170
-     * @param bool $update_txn_status
1171
-     * @return float
1172
-     * @throws EE_Error
1173
-     * @throws InvalidArgumentException
1174
-     * @throws InvalidDataTypeException
1175
-     * @throws InvalidInterfaceException
1176
-     * @throws ReflectionException
1177
-     * @throws RuntimeException
1178
-     */
1179
-    public function recalculate_total_including_taxes($update_txn_status = false)
1180
-    {
1181
-        $pre_tax_total = $this->recalculate_pre_tax_total();
1182
-        $tax_total = $this->recalculate_taxes_and_tax_total();
1183
-        $total = $pre_tax_total + $tax_total;
1184
-        // no negative totals plz
1185
-        $total = max($total, 0);
1186
-        $this->set_total($total);
1187
-        // only update the related transaction's total
1188
-        // if we intend to save this line item and its a grand total
1189
-        if (
1190
-            $this->allow_persist() && $this->type() === EEM_Line_Item::type_total
1191
-            && $this->transaction()
1192
-               instanceof
1193
-               EE_Transaction
1194
-        ) {
1195
-            $this->transaction()->set_total($total);
1196
-            if ($update_txn_status) {
1197
-                // don't save the TXN because that will be done below
1198
-                // and the following method only saves if the status changes
1199
-                $this->transaction()->update_status_based_on_total_paid(false);
1200
-            }
1201
-            if ($this->transaction()->ID()) {
1202
-                $this->transaction()->save();
1203
-            }
1204
-        }
1205
-        $this->maybe_save();
1206
-        return $total;
1207
-    }
1208
-
1209
-
1210
-    /**
1211
-     * Recursively goes through all the children and recalculates sub-totals EXCEPT for
1212
-     * tax-sub-totals (they're a an odd beast). Updates the 'total' on each line item according to either its
1213
-     * unit price * quantity or the total of all its children EXCEPT when we're only calculating the taxable total and
1214
-     * when this is called on the grand total
1215
-     *
1216
-     * @return float
1217
-     * @throws EE_Error
1218
-     * @throws InvalidArgumentException
1219
-     * @throws InvalidDataTypeException
1220
-     * @throws InvalidInterfaceException
1221
-     * @throws ReflectionException
1222
-     */
1223
-    public function recalculate_pre_tax_total()
1224
-    {
1225
-        $total = 0;
1226
-        $my_children = $this->children();
1227
-        $has_children = ! empty($my_children);
1228
-        if ($has_children && $this->is_line_item()) {
1229
-            $total = $this->_recalculate_pretax_total_for_line_item($total, $my_children);
1230
-        } elseif (! $has_children && ($this->is_sub_line_item() || $this->is_line_item())) {
1231
-            $total = $this->unit_price() * $this->quantity();
1232
-        } elseif ($this->is_sub_total() || $this->is_total()) {
1233
-            $total = $this->_recalculate_pretax_total_for_subtotal($total, $my_children);
1234
-        } elseif ($this->is_tax_sub_total() || $this->is_tax() || $this->is_cancelled()) {
1235
-            // completely ignore tax totals, tax sub-totals, and cancelled line items, when calculating the pre-tax-total
1236
-            return 0;
1237
-        }
1238
-        // ensure all non-line items and non-sub-line-items have a quantity of 1 (except for Events)
1239
-        if (
1240
-            ! $this->is_line_item() && ! $this->is_sub_line_item() && ! $this->is_cancellation()
1241
-        ) {
1242
-            if ($this->OBJ_type() !== EEM_Line_Item::OBJ_TYPE_EVENT) {
1243
-                $this->set_quantity(1);
1244
-            }
1245
-            if (! $this->is_percent()) {
1246
-                $this->set_unit_price($total);
1247
-            }
1248
-        }
1249
-        // we don't want to bother saving grand totals, because that needs to factor in taxes anyways
1250
-        // so it ought to be
1251
-        if (! $this->is_total()) {
1252
-            $this->set_total($total);
1253
-            // if not a percent line item, make sure we keep the unit price in sync
1254
-            if (
1255
-                $has_children
1256
-                && $this->is_line_item()
1257
-                && ! $this->is_percent()
1258
-            ) {
1259
-                if ($this->quantity() === 0) {
1260
-                    $new_unit_price = 0;
1261
-                } else {
1262
-                    $new_unit_price = $this->total() / $this->quantity();
1263
-                }
1264
-                $this->set_unit_price($new_unit_price);
1265
-            }
1266
-            $this->maybe_save();
1267
-        }
1268
-        return $total;
1269
-    }
1270
-
1271
-
1272
-    /**
1273
-     * Calculates the pretax total when this line item is a subtotal or total line item.
1274
-     * Basically does a sum-then-round approach (ie, any percent line item that are children
1275
-     * will calculate their total based on the un-rounded total we're working with so far, and
1276
-     * THEN round the result; instead of rounding as we go like with sub-line-items)
1277
-     *
1278
-     * @param float          $calculated_total_so_far
1279
-     * @param EE_Line_Item[] $my_children
1280
-     * @return float
1281
-     * @throws EE_Error
1282
-     * @throws InvalidArgumentException
1283
-     * @throws InvalidDataTypeException
1284
-     * @throws InvalidInterfaceException
1285
-     * @throws ReflectionException
1286
-     */
1287
-    protected function _recalculate_pretax_total_for_subtotal($calculated_total_so_far, $my_children = null)
1288
-    {
1289
-        if ($my_children === null) {
1290
-            $my_children = $this->children();
1291
-        }
1292
-        $subtotal_quantity = 0;
1293
-        // get the total of all its children
1294
-        foreach ($my_children as $child_line_item) {
1295
-            if ($child_line_item instanceof EE_Line_Item && ! $child_line_item->is_cancellation()) {
1296
-                // percentage line items are based on total so far
1297
-                if ($child_line_item->is_percent()) {
1298
-                    // round as we go so that the line items add up ok
1299
-                    $percent_total = round(
1300
-                        $calculated_total_so_far * $child_line_item->percent() / 100,
1301
-                        EE_Registry::instance()->CFG->currency->dec_plc
1302
-                    );
1303
-                    $child_line_item->set_total($percent_total);
1304
-                    // so far all percent line items should have a quantity of 1
1305
-                    // (ie, no double percent discounts. Although that might be requested someday)
1306
-                    $child_line_item->set_quantity(1);
1307
-                    $child_line_item->maybe_save();
1308
-                    $calculated_total_so_far += $percent_total;
1309
-                } else {
1310
-                    // verify flat sub-line-item quantities match their parent
1311
-                    if ($child_line_item->is_sub_line_item()) {
1312
-                        $child_line_item->set_quantity($this->quantity());
1313
-                    }
1314
-                    $calculated_total_so_far += $child_line_item->recalculate_pre_tax_total();
1315
-                    $subtotal_quantity += $child_line_item->quantity();
1316
-                }
1317
-            }
1318
-        }
1319
-        if ($this->is_sub_total()) {
1320
-            // no negative totals plz
1321
-            $calculated_total_so_far = max($calculated_total_so_far, 0);
1322
-            $subtotal_quantity = $subtotal_quantity > 0 ? 1 : 0;
1323
-            $this->set_quantity($subtotal_quantity);
1324
-            $this->maybe_save();
1325
-        }
1326
-        return $calculated_total_so_far;
1327
-    }
1328
-
1329
-
1330
-    /**
1331
-     * Calculates the pretax total for a normal line item, in a round-then-sum approach
1332
-     * (where each sub-line-item is applied to the base price for the line item
1333
-     * and the result is immediately rounded, rather than summing all the sub-line-items
1334
-     * then rounding, like we do when recalculating pretax totals on totals and subtotals).
1335
-     *
1336
-     * @param float          $calculated_total_so_far
1337
-     * @param EE_Line_Item[] $my_children
1338
-     * @return float
1339
-     * @throws EE_Error
1340
-     * @throws InvalidArgumentException
1341
-     * @throws InvalidDataTypeException
1342
-     * @throws InvalidInterfaceException
1343
-     * @throws ReflectionException
1344
-     */
1345
-    protected function _recalculate_pretax_total_for_line_item($calculated_total_so_far, $my_children = null)
1346
-    {
1347
-        if ($my_children === null) {
1348
-            $my_children = $this->children();
1349
-        }
1350
-        // we need to keep track of the running total for a single item,
1351
-        // because we need to round as we go
1352
-        $unit_price_for_total = 0;
1353
-        $quantity_for_total = 1;
1354
-        // get the total of all its children
1355
-        foreach ($my_children as $child_line_item) {
1356
-            if ($child_line_item instanceof EE_Line_Item && ! $child_line_item->is_cancellation()) {
1357
-                if ($child_line_item->is_percent()) {
1358
-                    // it should be the unit-price-so-far multiplied by teh percent multiplied by the quantity
1359
-                    // not total multiplied by percent, because that ignores rounding along-the-way
1360
-                    $percent_unit_price = round(
1361
-                        $unit_price_for_total * $child_line_item->percent() / 100,
1362
-                        EE_Registry::instance()->CFG->currency->dec_plc
1363
-                    );
1364
-                    $percent_total = $percent_unit_price * $quantity_for_total;
1365
-                    $child_line_item->set_total($percent_total);
1366
-                    // so far all percent line items should have a quantity of 1
1367
-                    // (ie, no double percent discounts. Although that might be requested someday)
1368
-                    $child_line_item->set_quantity(1);
1369
-                    $child_line_item->maybe_save();
1370
-                    $calculated_total_so_far += $percent_total;
1371
-                    $unit_price_for_total += $percent_unit_price;
1372
-                } else {
1373
-                    // verify flat sub-line-item quantities match their parent
1374
-                    if ($child_line_item->is_sub_line_item()) {
1375
-                        $child_line_item->set_quantity($this->quantity());
1376
-                    }
1377
-                    $quantity_for_total = $child_line_item->quantity();
1378
-                    $calculated_total_so_far += $child_line_item->recalculate_pre_tax_total();
1379
-                    $unit_price_for_total += $child_line_item->unit_price();
1380
-                }
1381
-            }
1382
-        }
1383
-        return $calculated_total_so_far;
1384
-    }
1385
-
1386
-
1387
-    /**
1388
-     * Recalculates the total on each individual tax (based on a recalculation of the pre-tax total), sets
1389
-     * the totals on each tax calculated, and returns the final tax total. Re-saves tax line items
1390
-     * and tax sub-total if already in the DB
1391
-     *
1392
-     * @return float
1393
-     * @throws EE_Error
1394
-     * @throws InvalidArgumentException
1395
-     * @throws InvalidDataTypeException
1396
-     * @throws InvalidInterfaceException
1397
-     * @throws ReflectionException
1398
-     */
1399
-    public function recalculate_taxes_and_tax_total()
1400
-    {
1401
-        // get all taxes
1402
-        $taxes = $this->tax_descendants();
1403
-        // calculate the pretax total
1404
-        $taxable_total = $this->taxable_total();
1405
-        $tax_total = 0;
1406
-        foreach ($taxes as $tax) {
1407
-            $total_on_this_tax = $taxable_total * $tax->percent() / 100;
1408
-            // remember the total on this line item
1409
-            $tax->set_total($total_on_this_tax);
1410
-            $tax->maybe_save();
1411
-            $tax_total += $tax->total();
1412
-        }
1413
-        $this->_recalculate_tax_sub_total();
1414
-        return $tax_total;
1415
-    }
1416
-
1417
-
1418
-    /**
1419
-     * Simply forces all the tax-sub-totals to recalculate. Assumes the taxes have been calculated
1420
-     *
1421
-     * @return void
1422
-     * @throws EE_Error
1423
-     * @throws InvalidArgumentException
1424
-     * @throws InvalidDataTypeException
1425
-     * @throws InvalidInterfaceException
1426
-     * @throws ReflectionException
1427
-     */
1428
-    private function _recalculate_tax_sub_total()
1429
-    {
1430
-        if ($this->is_tax_sub_total()) {
1431
-            $total = 0;
1432
-            $total_percent = 0;
1433
-            // simply loop through all its children (which should be taxes) and sum their total
1434
-            foreach ($this->children() as $child_tax) {
1435
-                if ($child_tax instanceof EE_Line_Item) {
1436
-                    $total += $child_tax->total();
1437
-                    $total_percent += $child_tax->percent();
1438
-                }
1439
-            }
1440
-            $this->set_total($total);
1441
-            $this->set_percent($total_percent);
1442
-            $this->maybe_save();
1443
-        } elseif ($this->is_total()) {
1444
-            foreach ($this->children() as $maybe_tax_subtotal) {
1445
-                if ($maybe_tax_subtotal instanceof EE_Line_Item) {
1446
-                    $maybe_tax_subtotal->_recalculate_tax_sub_total();
1447
-                }
1448
-            }
1449
-        }
1450
-    }
1451
-
1452
-
1453
-    /**
1454
-     * Gets the total tax on this line item. Assumes taxes have already been calculated using
1455
-     * recalculate_taxes_and_total
1456
-     *
1457
-     * @return float
1458
-     * @throws EE_Error
1459
-     * @throws InvalidArgumentException
1460
-     * @throws InvalidDataTypeException
1461
-     * @throws InvalidInterfaceException
1462
-     * @throws ReflectionException
1463
-     */
1464
-    public function get_total_tax()
1465
-    {
1466
-        $this->_recalculate_tax_sub_total();
1467
-        $total = 0;
1468
-        foreach ($this->tax_descendants() as $tax_line_item) {
1469
-            if ($tax_line_item instanceof EE_Line_Item) {
1470
-                $total += $tax_line_item->total();
1471
-            }
1472
-        }
1473
-        return $total;
1474
-    }
1475
-
1476
-
1477
-    /**
1478
-     * Gets the total for all the items purchased only
1479
-     *
1480
-     * @return float
1481
-     * @throws EE_Error
1482
-     * @throws InvalidArgumentException
1483
-     * @throws InvalidDataTypeException
1484
-     * @throws InvalidInterfaceException
1485
-     * @throws ReflectionException
1486
-     */
1487
-    public function get_items_total()
1488
-    {
1489
-        // by default, let's make sure we're consistent with the existing line item
1490
-        if ($this->is_total()) {
1491
-            $pretax_subtotal_li = EEH_Line_Item::get_pre_tax_subtotal($this);
1492
-            if ($pretax_subtotal_li instanceof EE_Line_Item) {
1493
-                return $pretax_subtotal_li->total();
1494
-            }
1495
-        }
1496
-        $total = 0;
1497
-        foreach ($this->get_items() as $item) {
1498
-            if ($item instanceof EE_Line_Item) {
1499
-                $total += $item->total();
1500
-            }
1501
-        }
1502
-        return $total;
1503
-    }
1504
-
1505
-
1506
-    /**
1507
-     * Gets all the descendants (ie, children or children of children etc) that
1508
-     * are of the type 'tax'
1509
-     *
1510
-     * @return EE_Line_Item[]
1511
-     * @throws EE_Error
1512
-     */
1513
-    public function tax_descendants()
1514
-    {
1515
-        return EEH_Line_Item::get_tax_descendants($this);
1516
-    }
1517
-
1518
-
1519
-    /**
1520
-     * Gets all the real items purchased which are children of this item
1521
-     *
1522
-     * @return EE_Line_Item[]
1523
-     * @throws EE_Error
1524
-     */
1525
-    public function get_items()
1526
-    {
1527
-        return EEH_Line_Item::get_line_item_descendants($this);
1528
-    }
1529
-
1530
-
1531
-    /**
1532
-     * Returns the amount taxable among this line item's children (or if it has no children,
1533
-     * how much of it is taxable). Does not recalculate totals or subtotals.
1534
-     * If the taxable total is negative, (eg, if none of the tickets were taxable,
1535
-     * but there is a "Taxable" discount), returns 0.
1536
-     *
1537
-     * @return float
1538
-     * @throws EE_Error
1539
-     * @throws InvalidArgumentException
1540
-     * @throws InvalidDataTypeException
1541
-     * @throws InvalidInterfaceException
1542
-     * @throws ReflectionException
1543
-     */
1544
-    public function taxable_total()
1545
-    {
1546
-        $total = 0;
1547
-        if ($this->children()) {
1548
-            foreach ($this->children() as $child_line_item) {
1549
-                if ($child_line_item->type() === EEM_Line_Item::type_line_item && $child_line_item->is_taxable()) {
1550
-                    // if it's a percent item, only take into account the percent
1551
-                    // that's taxable too (the taxable total so far)
1552
-                    if ($child_line_item->is_percent()) {
1553
-                        $total += ($total * $child_line_item->percent() / 100);
1554
-                    } else {
1555
-                        $total += $child_line_item->total();
1556
-                    }
1557
-                } elseif ($child_line_item->type() === EEM_Line_Item::type_sub_total) {
1558
-                    $total += $child_line_item->taxable_total();
1559
-                }
1560
-            }
1561
-        }
1562
-        return max($total, 0);
1563
-    }
1564
-
1565
-
1566
-    /**
1567
-     * Gets the transaction for this line item
1568
-     *
1569
-     * @return EE_Base_Class|EE_Transaction
1570
-     * @throws EE_Error
1571
-     * @throws InvalidArgumentException
1572
-     * @throws InvalidDataTypeException
1573
-     * @throws InvalidInterfaceException
1574
-     * @throws ReflectionException
1575
-     */
1576
-    public function transaction()
1577
-    {
1578
-        return $this->get_first_related(EEM_Line_Item::OBJ_TYPE_TRANSACTION);
1579
-    }
1580
-
1581
-
1582
-    /**
1583
-     * Saves this line item to the DB, and recursively saves its descendants.
1584
-     * Because there currently is no proper parent-child relation on the model,
1585
-     * save_this_and_cached() will NOT save the descendants.
1586
-     * Also sets the transaction on this line item and all its descendants before saving
1587
-     *
1588
-     * @param int $txn_id if none is provided, assumes $this->TXN_ID()
1589
-     * @return int count of items saved
1590
-     * @throws EE_Error
1591
-     * @throws InvalidArgumentException
1592
-     * @throws InvalidDataTypeException
1593
-     * @throws InvalidInterfaceException
1594
-     * @throws ReflectionException
1595
-     */
1596
-    public function save_this_and_descendants_to_txn($txn_id = null)
1597
-    {
1598
-        $count = 0;
1599
-        if (! $txn_id) {
1600
-            $txn_id = $this->TXN_ID();
1601
-        }
1602
-        $this->set_TXN_ID($txn_id);
1603
-        $children = $this->children();
1604
-        $count += $this->save()
1605
-            ? 1
1606
-            : 0;
1607
-        foreach ($children as $child_line_item) {
1608
-            if ($child_line_item instanceof EE_Line_Item) {
1609
-                $child_line_item->set_parent_ID($this->ID());
1610
-                $count += $child_line_item->save_this_and_descendants_to_txn($txn_id);
1611
-            }
1612
-        }
1613
-        return $count;
1614
-    }
1615
-
1616
-
1617
-    /**
1618
-     * Saves this line item to the DB, and recursively saves its descendants.
1619
-     *
1620
-     * @return int count of items saved
1621
-     * @throws EE_Error
1622
-     * @throws InvalidArgumentException
1623
-     * @throws InvalidDataTypeException
1624
-     * @throws InvalidInterfaceException
1625
-     * @throws ReflectionException
1626
-     */
1627
-    public function save_this_and_descendants()
1628
-    {
1629
-        $count = 0;
1630
-        $children = $this->children();
1631
-        $count += $this->save()
1632
-            ? 1
1633
-            : 0;
1634
-        foreach ($children as $child_line_item) {
1635
-            if ($child_line_item instanceof EE_Line_Item) {
1636
-                $child_line_item->set_parent_ID($this->ID());
1637
-                $count += $child_line_item->save_this_and_descendants();
1638
-            }
1639
-        }
1640
-        return $count;
1641
-    }
1642
-
1643
-
1644
-    /**
1645
-     * returns the cancellation line item if this item was cancelled
1646
-     *
1647
-     * @return EE_Line_Item[]
1648
-     * @throws InvalidArgumentException
1649
-     * @throws InvalidInterfaceException
1650
-     * @throws InvalidDataTypeException
1651
-     * @throws ReflectionException
1652
-     * @throws EE_Error
1653
-     */
1654
-    public function get_cancellations()
1655
-    {
1656
-        EE_Registry::instance()->load_helper('Line_Item');
1657
-        return EEH_Line_Item::get_descendants_of_type($this, EEM_Line_Item::type_cancellation);
1658
-    }
1659
-
1660
-
1661
-    /**
1662
-     * If this item has an ID, then this saves it again to update the db
1663
-     *
1664
-     * @return int count of items saved
1665
-     * @throws EE_Error
1666
-     * @throws InvalidArgumentException
1667
-     * @throws InvalidDataTypeException
1668
-     * @throws InvalidInterfaceException
1669
-     * @throws ReflectionException
1670
-     */
1671
-    public function maybe_save()
1672
-    {
1673
-        if ($this->ID()) {
1674
-            return $this->save();
1675
-        }
1676
-        return false;
1677
-    }
1678
-
1679
-
1680
-    /**
1681
-     * clears the cached children and parent from the line item
1682
-     *
1683
-     * @return void
1684
-     */
1685
-    public function clear_related_line_item_cache()
1686
-    {
1687
-        $this->_children = array();
1688
-        $this->_parent = null;
1689
-    }
1690
-
1691
-
1692
-    /**
1693
-     * @param bool $raw
1694
-     * @return int
1695
-     * @throws EE_Error
1696
-     * @throws InvalidArgumentException
1697
-     * @throws InvalidDataTypeException
1698
-     * @throws InvalidInterfaceException
1699
-     * @throws ReflectionException
1700
-     */
1701
-    public function timestamp($raw = false)
1702
-    {
1703
-        return $raw
1704
-            ? $this->get_raw('LIN_timestamp')
1705
-            : $this->get('LIN_timestamp');
1706
-    }
1707
-
1708
-
1709
-
1710
-
1711
-    /************************* DEPRECATED *************************/
1712
-    /**
1713
-     * @deprecated 4.6.0
1714
-     * @param string $type one of the constants on EEM_Line_Item
1715
-     * @return EE_Line_Item[]
1716
-     * @throws EE_Error
1717
-     */
1718
-    protected function _get_descendants_of_type($type)
1719
-    {
1720
-        EE_Error::doing_it_wrong(
1721
-            'EE_Line_Item::_get_descendants_of_type()',
1722
-            sprintf(
1723
-                esc_html__('Method replaced with %1$s', 'event_espresso'),
1724
-                'EEH_Line_Item::get_descendants_of_type()'
1725
-            ),
1726
-            '4.6.0'
1727
-        );
1728
-        return EEH_Line_Item::get_descendants_of_type($this, $type);
1729
-    }
1730
-
1731
-
1732
-    /**
1733
-     * @deprecated 4.6.0
1734
-     * @param string $type like one of the EEM_Line_Item::type_*
1735
-     * @return EE_Line_Item
1736
-     * @throws EE_Error
1737
-     * @throws InvalidArgumentException
1738
-     * @throws InvalidDataTypeException
1739
-     * @throws InvalidInterfaceException
1740
-     * @throws ReflectionException
1741
-     */
1742
-    public function get_nearest_descendant_of_type($type)
1743
-    {
1744
-        EE_Error::doing_it_wrong(
1745
-            'EE_Line_Item::get_nearest_descendant_of_type()',
1746
-            sprintf(
1747
-                esc_html__('Method replaced with %1$s', 'event_espresso'),
1748
-                'EEH_Line_Item::get_nearest_descendant_of_type()'
1749
-            ),
1750
-            '4.6.0'
1751
-        );
1752
-        return EEH_Line_Item::get_nearest_descendant_of_type($this, $type);
1753
-    }
16
+	/**
17
+	 * for children line items (currently not a normal relation)
18
+	 *
19
+	 * @type EE_Line_Item[]
20
+	 */
21
+	protected $_children = array();
22
+
23
+	/**
24
+	 * for the parent line item
25
+	 *
26
+	 * @var EE_Line_Item
27
+	 */
28
+	protected $_parent;
29
+
30
+
31
+	/**
32
+	 * @param array  $props_n_values          incoming values
33
+	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
34
+	 *                                        used.)
35
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
36
+	 *                                        date_format and the second value is the time format
37
+	 * @return EE_Line_Item
38
+	 * @throws EE_Error
39
+	 * @throws InvalidArgumentException
40
+	 * @throws InvalidDataTypeException
41
+	 * @throws InvalidInterfaceException
42
+	 * @throws ReflectionException
43
+	 */
44
+	public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
45
+	{
46
+		$has_object = parent::_check_for_object(
47
+			$props_n_values,
48
+			__CLASS__,
49
+			$timezone,
50
+			$date_formats
51
+		);
52
+		return $has_object
53
+			? $has_object
54
+			: new self($props_n_values, false, $timezone);
55
+	}
56
+
57
+
58
+	/**
59
+	 * @param array  $props_n_values  incoming values from the database
60
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
61
+	 *                                the website will be used.
62
+	 * @return EE_Line_Item
63
+	 * @throws EE_Error
64
+	 * @throws InvalidArgumentException
65
+	 * @throws InvalidDataTypeException
66
+	 * @throws InvalidInterfaceException
67
+	 * @throws ReflectionException
68
+	 */
69
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
70
+	{
71
+		return new self($props_n_values, true, $timezone);
72
+	}
73
+
74
+
75
+	/**
76
+	 * Adds some defaults if they're not specified
77
+	 *
78
+	 * @param array  $fieldValues
79
+	 * @param bool   $bydb
80
+	 * @param string $timezone
81
+	 * @throws EE_Error
82
+	 * @throws InvalidArgumentException
83
+	 * @throws InvalidDataTypeException
84
+	 * @throws InvalidInterfaceException
85
+	 * @throws ReflectionException
86
+	 */
87
+	protected function __construct($fieldValues = array(), $bydb = false, $timezone = '')
88
+	{
89
+		parent::__construct($fieldValues, $bydb, $timezone);
90
+		if (! $this->get('LIN_code')) {
91
+			$this->set_code($this->generate_code());
92
+		}
93
+	}
94
+
95
+
96
+	/**
97
+	 * Gets ID
98
+	 *
99
+	 * @return int
100
+	 * @throws EE_Error
101
+	 * @throws InvalidArgumentException
102
+	 * @throws InvalidDataTypeException
103
+	 * @throws InvalidInterfaceException
104
+	 * @throws ReflectionException
105
+	 */
106
+	public function ID()
107
+	{
108
+		return $this->get('LIN_ID');
109
+	}
110
+
111
+
112
+	/**
113
+	 * Gets TXN_ID
114
+	 *
115
+	 * @return int
116
+	 * @throws EE_Error
117
+	 * @throws InvalidArgumentException
118
+	 * @throws InvalidDataTypeException
119
+	 * @throws InvalidInterfaceException
120
+	 * @throws ReflectionException
121
+	 */
122
+	public function TXN_ID()
123
+	{
124
+		return $this->get('TXN_ID');
125
+	}
126
+
127
+
128
+	/**
129
+	 * Sets TXN_ID
130
+	 *
131
+	 * @param int $TXN_ID
132
+	 * @throws EE_Error
133
+	 * @throws InvalidArgumentException
134
+	 * @throws InvalidDataTypeException
135
+	 * @throws InvalidInterfaceException
136
+	 * @throws ReflectionException
137
+	 */
138
+	public function set_TXN_ID($TXN_ID)
139
+	{
140
+		$this->set('TXN_ID', $TXN_ID);
141
+	}
142
+
143
+
144
+	/**
145
+	 * Gets name
146
+	 *
147
+	 * @return string
148
+	 * @throws EE_Error
149
+	 * @throws InvalidArgumentException
150
+	 * @throws InvalidDataTypeException
151
+	 * @throws InvalidInterfaceException
152
+	 * @throws ReflectionException
153
+	 */
154
+	public function name()
155
+	{
156
+		$name = $this->get('LIN_name');
157
+		if (! $name) {
158
+			$name = ucwords(str_replace('-', ' ', $this->type()));
159
+		}
160
+		return $name;
161
+	}
162
+
163
+
164
+	/**
165
+	 * Sets name
166
+	 *
167
+	 * @param string $name
168
+	 * @throws EE_Error
169
+	 * @throws InvalidArgumentException
170
+	 * @throws InvalidDataTypeException
171
+	 * @throws InvalidInterfaceException
172
+	 * @throws ReflectionException
173
+	 */
174
+	public function set_name($name)
175
+	{
176
+		$this->set('LIN_name', $name);
177
+	}
178
+
179
+
180
+	/**
181
+	 * Gets desc
182
+	 *
183
+	 * @return string
184
+	 * @throws EE_Error
185
+	 * @throws InvalidArgumentException
186
+	 * @throws InvalidDataTypeException
187
+	 * @throws InvalidInterfaceException
188
+	 * @throws ReflectionException
189
+	 */
190
+	public function desc()
191
+	{
192
+		return $this->get('LIN_desc');
193
+	}
194
+
195
+
196
+	/**
197
+	 * Sets desc
198
+	 *
199
+	 * @param string $desc
200
+	 * @throws EE_Error
201
+	 * @throws InvalidArgumentException
202
+	 * @throws InvalidDataTypeException
203
+	 * @throws InvalidInterfaceException
204
+	 * @throws ReflectionException
205
+	 */
206
+	public function set_desc($desc)
207
+	{
208
+		$this->set('LIN_desc', $desc);
209
+	}
210
+
211
+
212
+	/**
213
+	 * Gets quantity
214
+	 *
215
+	 * @return int
216
+	 * @throws EE_Error
217
+	 * @throws InvalidArgumentException
218
+	 * @throws InvalidDataTypeException
219
+	 * @throws InvalidInterfaceException
220
+	 * @throws ReflectionException
221
+	 */
222
+	public function quantity()
223
+	{
224
+		return $this->get('LIN_quantity');
225
+	}
226
+
227
+
228
+	/**
229
+	 * Sets quantity
230
+	 *
231
+	 * @param int $quantity
232
+	 * @throws EE_Error
233
+	 * @throws InvalidArgumentException
234
+	 * @throws InvalidDataTypeException
235
+	 * @throws InvalidInterfaceException
236
+	 * @throws ReflectionException
237
+	 */
238
+	public function set_quantity($quantity)
239
+	{
240
+		$this->set('LIN_quantity', max($quantity, 0));
241
+	}
242
+
243
+
244
+	/**
245
+	 * Gets item_id
246
+	 *
247
+	 * @return string
248
+	 * @throws EE_Error
249
+	 * @throws InvalidArgumentException
250
+	 * @throws InvalidDataTypeException
251
+	 * @throws InvalidInterfaceException
252
+	 * @throws ReflectionException
253
+	 */
254
+	public function OBJ_ID()
255
+	{
256
+		return $this->get('OBJ_ID');
257
+	}
258
+
259
+
260
+	/**
261
+	 * Sets item_id
262
+	 *
263
+	 * @param string $item_id
264
+	 * @throws EE_Error
265
+	 * @throws InvalidArgumentException
266
+	 * @throws InvalidDataTypeException
267
+	 * @throws InvalidInterfaceException
268
+	 * @throws ReflectionException
269
+	 */
270
+	public function set_OBJ_ID($item_id)
271
+	{
272
+		$this->set('OBJ_ID', $item_id);
273
+	}
274
+
275
+
276
+	/**
277
+	 * Gets item_type
278
+	 *
279
+	 * @return string
280
+	 * @throws EE_Error
281
+	 * @throws InvalidArgumentException
282
+	 * @throws InvalidDataTypeException
283
+	 * @throws InvalidInterfaceException
284
+	 * @throws ReflectionException
285
+	 */
286
+	public function OBJ_type()
287
+	{
288
+		return $this->get('OBJ_type');
289
+	}
290
+
291
+
292
+	/**
293
+	 * Gets item_type
294
+	 *
295
+	 * @return string
296
+	 * @throws EE_Error
297
+	 * @throws InvalidArgumentException
298
+	 * @throws InvalidDataTypeException
299
+	 * @throws InvalidInterfaceException
300
+	 * @throws ReflectionException
301
+	 */
302
+	public function OBJ_type_i18n()
303
+	{
304
+		$obj_type = $this->OBJ_type();
305
+		switch ($obj_type) {
306
+			case EEM_Line_Item::OBJ_TYPE_EVENT:
307
+				$obj_type = esc_html__('Event', 'event_espresso');
308
+				break;
309
+			case EEM_Line_Item::OBJ_TYPE_PRICE:
310
+				$obj_type = esc_html__('Price', 'event_espresso');
311
+				break;
312
+			case EEM_Line_Item::OBJ_TYPE_PROMOTION:
313
+				$obj_type = esc_html__('Promotion', 'event_espresso');
314
+				break;
315
+			case EEM_Line_Item::OBJ_TYPE_TICKET:
316
+				$obj_type = esc_html__('Ticket', 'event_espresso');
317
+				break;
318
+			case EEM_Line_Item::OBJ_TYPE_TRANSACTION:
319
+				$obj_type = esc_html__('Transaction', 'event_espresso');
320
+				break;
321
+		}
322
+		return apply_filters('FHEE__EE_Line_Item__OBJ_type_i18n', $obj_type, $this);
323
+	}
324
+
325
+
326
+	/**
327
+	 * Sets item_type
328
+	 *
329
+	 * @param string $OBJ_type
330
+	 * @throws EE_Error
331
+	 * @throws InvalidArgumentException
332
+	 * @throws InvalidDataTypeException
333
+	 * @throws InvalidInterfaceException
334
+	 * @throws ReflectionException
335
+	 */
336
+	public function set_OBJ_type($OBJ_type)
337
+	{
338
+		$this->set('OBJ_type', $OBJ_type);
339
+	}
340
+
341
+
342
+	/**
343
+	 * Gets unit_price
344
+	 *
345
+	 * @return float
346
+	 * @throws EE_Error
347
+	 * @throws InvalidArgumentException
348
+	 * @throws InvalidDataTypeException
349
+	 * @throws InvalidInterfaceException
350
+	 * @throws ReflectionException
351
+	 */
352
+	public function unit_price()
353
+	{
354
+		return $this->get('LIN_unit_price');
355
+	}
356
+
357
+
358
+	/**
359
+	 * Sets unit_price
360
+	 *
361
+	 * @param float $unit_price
362
+	 * @throws EE_Error
363
+	 * @throws InvalidArgumentException
364
+	 * @throws InvalidDataTypeException
365
+	 * @throws InvalidInterfaceException
366
+	 * @throws ReflectionException
367
+	 */
368
+	public function set_unit_price($unit_price)
369
+	{
370
+		$this->set('LIN_unit_price', $unit_price);
371
+	}
372
+
373
+
374
+	/**
375
+	 * Checks if this item is a percentage modifier or not
376
+	 *
377
+	 * @return boolean
378
+	 * @throws EE_Error
379
+	 * @throws InvalidArgumentException
380
+	 * @throws InvalidDataTypeException
381
+	 * @throws InvalidInterfaceException
382
+	 * @throws ReflectionException
383
+	 */
384
+	public function is_percent()
385
+	{
386
+		if ($this->is_tax_sub_total()) {
387
+			// tax subtotals HAVE a percent on them, that percentage only applies
388
+			// to taxable items, so its' an exception. Treat it like a flat line item
389
+			return false;
390
+		}
391
+		$unit_price = abs($this->get('LIN_unit_price'));
392
+		$percent = abs($this->get('LIN_percent'));
393
+		if ($unit_price < .001 && $percent) {
394
+			return true;
395
+		}
396
+		if ($unit_price >= .001 && ! $percent) {
397
+			return false;
398
+		}
399
+		if ($unit_price >= .001 && $percent) {
400
+			throw new EE_Error(
401
+				sprintf(
402
+					esc_html__(
403
+						'A Line Item can not have a unit price of (%s) AND a percent (%s)!',
404
+						'event_espresso'
405
+					),
406
+					$unit_price,
407
+					$percent
408
+				)
409
+			);
410
+		}
411
+		// if they're both 0, assume its not a percent item
412
+		return false;
413
+	}
414
+
415
+
416
+	/**
417
+	 * Gets percent (between 100-.001)
418
+	 *
419
+	 * @return float
420
+	 * @throws EE_Error
421
+	 * @throws InvalidArgumentException
422
+	 * @throws InvalidDataTypeException
423
+	 * @throws InvalidInterfaceException
424
+	 * @throws ReflectionException
425
+	 */
426
+	public function percent()
427
+	{
428
+		return $this->get('LIN_percent');
429
+	}
430
+
431
+
432
+	/**
433
+	 * Sets percent (between 100-0.01)
434
+	 *
435
+	 * @param float $percent
436
+	 * @throws EE_Error
437
+	 * @throws InvalidArgumentException
438
+	 * @throws InvalidDataTypeException
439
+	 * @throws InvalidInterfaceException
440
+	 * @throws ReflectionException
441
+	 */
442
+	public function set_percent($percent)
443
+	{
444
+		$this->set('LIN_percent', $percent);
445
+	}
446
+
447
+
448
+	/**
449
+	 * Gets total
450
+	 *
451
+	 * @return float
452
+	 * @throws EE_Error
453
+	 * @throws InvalidArgumentException
454
+	 * @throws InvalidDataTypeException
455
+	 * @throws InvalidInterfaceException
456
+	 * @throws ReflectionException
457
+	 */
458
+	public function total()
459
+	{
460
+		return $this->get('LIN_total');
461
+	}
462
+
463
+
464
+	/**
465
+	 * Sets total
466
+	 *
467
+	 * @param float $total
468
+	 * @throws EE_Error
469
+	 * @throws InvalidArgumentException
470
+	 * @throws InvalidDataTypeException
471
+	 * @throws InvalidInterfaceException
472
+	 * @throws ReflectionException
473
+	 */
474
+	public function set_total($total)
475
+	{
476
+		$this->set('LIN_total', $total);
477
+	}
478
+
479
+
480
+	/**
481
+	 * Gets order
482
+	 *
483
+	 * @return int
484
+	 * @throws EE_Error
485
+	 * @throws InvalidArgumentException
486
+	 * @throws InvalidDataTypeException
487
+	 * @throws InvalidInterfaceException
488
+	 * @throws ReflectionException
489
+	 */
490
+	public function order()
491
+	{
492
+		return $this->get('LIN_order');
493
+	}
494
+
495
+
496
+	/**
497
+	 * Sets order
498
+	 *
499
+	 * @param int $order
500
+	 * @throws EE_Error
501
+	 * @throws InvalidArgumentException
502
+	 * @throws InvalidDataTypeException
503
+	 * @throws InvalidInterfaceException
504
+	 * @throws ReflectionException
505
+	 */
506
+	public function set_order($order)
507
+	{
508
+		$this->set('LIN_order', $order);
509
+	}
510
+
511
+
512
+	/**
513
+	 * Gets parent
514
+	 *
515
+	 * @return int
516
+	 * @throws EE_Error
517
+	 * @throws InvalidArgumentException
518
+	 * @throws InvalidDataTypeException
519
+	 * @throws InvalidInterfaceException
520
+	 * @throws ReflectionException
521
+	 */
522
+	public function parent_ID()
523
+	{
524
+		return $this->get('LIN_parent');
525
+	}
526
+
527
+
528
+	/**
529
+	 * Sets parent
530
+	 *
531
+	 * @param int $parent
532
+	 * @throws EE_Error
533
+	 * @throws InvalidArgumentException
534
+	 * @throws InvalidDataTypeException
535
+	 * @throws InvalidInterfaceException
536
+	 * @throws ReflectionException
537
+	 */
538
+	public function set_parent_ID($parent)
539
+	{
540
+		$this->set('LIN_parent', $parent);
541
+	}
542
+
543
+
544
+	/**
545
+	 * Gets type
546
+	 *
547
+	 * @return string
548
+	 * @throws EE_Error
549
+	 * @throws InvalidArgumentException
550
+	 * @throws InvalidDataTypeException
551
+	 * @throws InvalidInterfaceException
552
+	 * @throws ReflectionException
553
+	 */
554
+	public function type()
555
+	{
556
+		return $this->get('LIN_type');
557
+	}
558
+
559
+
560
+	/**
561
+	 * Sets type
562
+	 *
563
+	 * @param string $type
564
+	 * @throws EE_Error
565
+	 * @throws InvalidArgumentException
566
+	 * @throws InvalidDataTypeException
567
+	 * @throws InvalidInterfaceException
568
+	 * @throws ReflectionException
569
+	 */
570
+	public function set_type($type)
571
+	{
572
+		$this->set('LIN_type', $type);
573
+	}
574
+
575
+
576
+	/**
577
+	 * Gets the line item of which this item is a composite. Eg, if this is a subtotal, the parent might be a total\
578
+	 * If this line item is saved to the DB, fetches the parent from the DB. However, if this line item isn't in the DB
579
+	 * it uses its cached reference to its parent line item (which would have been set by `EE_Line_Item::set_parent()`
580
+	 * or indirectly by `EE_Line_item::add_child_line_item()`)
581
+	 *
582
+	 * @return EE_Base_Class|EE_Line_Item
583
+	 * @throws EE_Error
584
+	 * @throws InvalidArgumentException
585
+	 * @throws InvalidDataTypeException
586
+	 * @throws InvalidInterfaceException
587
+	 * @throws ReflectionException
588
+	 */
589
+	public function parent()
590
+	{
591
+		return $this->ID()
592
+			? $this->get_model()->get_one_by_ID($this->parent_ID())
593
+			: $this->_parent;
594
+	}
595
+
596
+
597
+	/**
598
+	 * Gets ALL the children of this line item (ie, all the parts that contribute towards this total).
599
+	 *
600
+	 * @return EE_Base_Class[]|EE_Line_Item[]
601
+	 * @throws EE_Error
602
+	 * @throws InvalidArgumentException
603
+	 * @throws InvalidDataTypeException
604
+	 * @throws InvalidInterfaceException
605
+	 * @throws ReflectionException
606
+	 */
607
+	public function children()
608
+	{
609
+		if ($this->ID()) {
610
+			return $this->get_model()->get_all(
611
+				array(
612
+					array('LIN_parent' => $this->ID()),
613
+					'order_by' => array('LIN_order' => 'ASC'),
614
+				)
615
+			);
616
+		}
617
+		if (! is_array($this->_children)) {
618
+			$this->_children = array();
619
+		}
620
+		return $this->_children;
621
+	}
622
+
623
+
624
+	/**
625
+	 * Gets code
626
+	 *
627
+	 * @return string
628
+	 * @throws EE_Error
629
+	 * @throws InvalidArgumentException
630
+	 * @throws InvalidDataTypeException
631
+	 * @throws InvalidInterfaceException
632
+	 * @throws ReflectionException
633
+	 */
634
+	public function code()
635
+	{
636
+		return $this->get('LIN_code');
637
+	}
638
+
639
+
640
+	/**
641
+	 * Sets code
642
+	 *
643
+	 * @param string $code
644
+	 * @throws EE_Error
645
+	 * @throws InvalidArgumentException
646
+	 * @throws InvalidDataTypeException
647
+	 * @throws InvalidInterfaceException
648
+	 * @throws ReflectionException
649
+	 */
650
+	public function set_code($code)
651
+	{
652
+		$this->set('LIN_code', $code);
653
+	}
654
+
655
+
656
+	/**
657
+	 * Gets is_taxable
658
+	 *
659
+	 * @return boolean
660
+	 * @throws EE_Error
661
+	 * @throws InvalidArgumentException
662
+	 * @throws InvalidDataTypeException
663
+	 * @throws InvalidInterfaceException
664
+	 * @throws ReflectionException
665
+	 */
666
+	public function is_taxable()
667
+	{
668
+		return $this->get('LIN_is_taxable');
669
+	}
670
+
671
+
672
+	/**
673
+	 * Sets is_taxable
674
+	 *
675
+	 * @param boolean $is_taxable
676
+	 * @throws EE_Error
677
+	 * @throws InvalidArgumentException
678
+	 * @throws InvalidDataTypeException
679
+	 * @throws InvalidInterfaceException
680
+	 * @throws ReflectionException
681
+	 */
682
+	public function set_is_taxable($is_taxable)
683
+	{
684
+		$this->set('LIN_is_taxable', $is_taxable);
685
+	}
686
+
687
+
688
+	/**
689
+	 * Gets the object that this model-joins-to.
690
+	 * returns one of the model objects that the field OBJ_ID can point to... see the 'OBJ_ID' field on
691
+	 * EEM_Promotion_Object
692
+	 *        Eg, if this line item join model object is for a ticket, this will return the EE_Ticket object
693
+	 *
694
+	 * @return EE_Base_Class | NULL
695
+	 * @throws EE_Error
696
+	 * @throws InvalidArgumentException
697
+	 * @throws InvalidDataTypeException
698
+	 * @throws InvalidInterfaceException
699
+	 * @throws ReflectionException
700
+	 */
701
+	public function get_object()
702
+	{
703
+		$model_name_of_related_obj = $this->OBJ_type();
704
+		return $this->get_model()->has_relation($model_name_of_related_obj)
705
+			? $this->get_first_related($model_name_of_related_obj)
706
+			: null;
707
+	}
708
+
709
+
710
+	/**
711
+	 * Like EE_Line_Item::get_object(), but can only ever actually return an EE_Ticket.
712
+	 * (IE, if this line item is for a price or something else, will return NULL)
713
+	 *
714
+	 * @param array $query_params
715
+	 * @return EE_Base_Class|EE_Ticket
716
+	 * @throws EE_Error
717
+	 * @throws InvalidArgumentException
718
+	 * @throws InvalidDataTypeException
719
+	 * @throws InvalidInterfaceException
720
+	 * @throws ReflectionException
721
+	 */
722
+	public function ticket($query_params = array())
723
+	{
724
+		// we're going to assume that when this method is called
725
+		// we always want to receive the attached ticket EVEN if that ticket is archived.
726
+		// This can be overridden via the incoming $query_params argument
727
+		$remove_defaults = array('default_where_conditions' => 'none');
728
+		$query_params = array_merge($remove_defaults, $query_params);
729
+		return $this->get_first_related(EEM_Line_Item::OBJ_TYPE_TICKET, $query_params);
730
+	}
731
+
732
+
733
+	/**
734
+	 * Gets the EE_Datetime that's related to the ticket, IF this is for a ticket
735
+	 *
736
+	 * @return EE_Datetime | NULL
737
+	 * @throws EE_Error
738
+	 * @throws InvalidArgumentException
739
+	 * @throws InvalidDataTypeException
740
+	 * @throws InvalidInterfaceException
741
+	 * @throws ReflectionException
742
+	 */
743
+	public function get_ticket_datetime()
744
+	{
745
+		if ($this->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET) {
746
+			$ticket = $this->ticket();
747
+			if ($ticket instanceof EE_Ticket) {
748
+				$datetime = $ticket->first_datetime();
749
+				if ($datetime instanceof EE_Datetime) {
750
+					return $datetime;
751
+				}
752
+			}
753
+		}
754
+		return null;
755
+	}
756
+
757
+
758
+	/**
759
+	 * Gets the event's name that's related to the ticket, if this is for
760
+	 * a ticket
761
+	 *
762
+	 * @return string
763
+	 * @throws EE_Error
764
+	 * @throws InvalidArgumentException
765
+	 * @throws InvalidDataTypeException
766
+	 * @throws InvalidInterfaceException
767
+	 * @throws ReflectionException
768
+	 */
769
+	public function ticket_event_name()
770
+	{
771
+		$event_name = esc_html__('Unknown', 'event_espresso');
772
+		$event = $this->ticket_event();
773
+		if ($event instanceof EE_Event) {
774
+			$event_name = $event->name();
775
+		}
776
+		return $event_name;
777
+	}
778
+
779
+
780
+	/**
781
+	 * Gets the event that's related to the ticket, if this line item represents a ticket.
782
+	 *
783
+	 * @return EE_Event|null
784
+	 * @throws EE_Error
785
+	 * @throws InvalidArgumentException
786
+	 * @throws InvalidDataTypeException
787
+	 * @throws InvalidInterfaceException
788
+	 * @throws ReflectionException
789
+	 */
790
+	public function ticket_event()
791
+	{
792
+		$event = null;
793
+		$ticket = $this->ticket();
794
+		if ($ticket instanceof EE_Ticket) {
795
+			$datetime = $ticket->first_datetime();
796
+			if ($datetime instanceof EE_Datetime) {
797
+				$event = $datetime->event();
798
+			}
799
+		}
800
+		return $event;
801
+	}
802
+
803
+
804
+	/**
805
+	 * Gets the first datetime for this lien item, assuming it's for a ticket
806
+	 *
807
+	 * @param string $date_format
808
+	 * @param string $time_format
809
+	 * @return string
810
+	 * @throws EE_Error
811
+	 * @throws InvalidArgumentException
812
+	 * @throws InvalidDataTypeException
813
+	 * @throws InvalidInterfaceException
814
+	 * @throws ReflectionException
815
+	 */
816
+	public function ticket_datetime_start($date_format = '', $time_format = '')
817
+	{
818
+		$first_datetime_string = esc_html__('Unknown', 'event_espresso');
819
+		$datetime = $this->get_ticket_datetime();
820
+		if ($datetime) {
821
+			$first_datetime_string = $datetime->start_date_and_time($date_format, $time_format);
822
+		}
823
+		return $first_datetime_string;
824
+	}
825
+
826
+
827
+	/**
828
+	 * Adds the line item as a child to this line item. If there is another child line
829
+	 * item with the same LIN_code, it is overwritten by this new one
830
+	 *
831
+	 * @param EEI_Line_Item $line_item
832
+	 * @param bool          $set_order
833
+	 * @return bool success
834
+	 * @throws EE_Error
835
+	 * @throws InvalidArgumentException
836
+	 * @throws InvalidDataTypeException
837
+	 * @throws InvalidInterfaceException
838
+	 * @throws ReflectionException
839
+	 */
840
+	public function add_child_line_item(EEI_Line_Item $line_item, $set_order = true)
841
+	{
842
+		// should we calculate the LIN_order for this line item ?
843
+		if ($set_order || $line_item->order() === null) {
844
+			$line_item->set_order(count($this->children()));
845
+		}
846
+		if ($this->ID()) {
847
+			// check for any duplicate line items (with the same code), if so, this replaces it
848
+			$line_item_with_same_code = $this->get_child_line_item($line_item->code());
849
+			if ($line_item_with_same_code instanceof EE_Line_Item && $line_item_with_same_code !== $line_item) {
850
+				$this->delete_child_line_item($line_item_with_same_code->code());
851
+			}
852
+			$line_item->set_parent_ID($this->ID());
853
+			if ($this->TXN_ID()) {
854
+				$line_item->set_TXN_ID($this->TXN_ID());
855
+			}
856
+			return $line_item->save();
857
+		}
858
+		$this->_children[ $line_item->code() ] = $line_item;
859
+		if ($line_item->parent() !== $this) {
860
+			$line_item->set_parent($this);
861
+		}
862
+		return true;
863
+	}
864
+
865
+
866
+	/**
867
+	 * Similar to EE_Base_Class::_add_relation_to, except this isn't a normal relation.
868
+	 * If this line item is saved to the DB, this is just a wrapper for set_parent_ID() and save()
869
+	 * However, if this line item is NOT saved to the DB, this just caches the parent on
870
+	 * the EE_Line_Item::_parent property.
871
+	 *
872
+	 * @param EE_Line_Item $line_item
873
+	 * @throws EE_Error
874
+	 * @throws InvalidArgumentException
875
+	 * @throws InvalidDataTypeException
876
+	 * @throws InvalidInterfaceException
877
+	 * @throws ReflectionException
878
+	 */
879
+	public function set_parent($line_item)
880
+	{
881
+		if ($this->ID()) {
882
+			if (! $line_item->ID()) {
883
+				$line_item->save();
884
+			}
885
+			$this->set_parent_ID($line_item->ID());
886
+			$this->save();
887
+		} else {
888
+			$this->_parent = $line_item;
889
+			$this->set_parent_ID($line_item->ID());
890
+		}
891
+	}
892
+
893
+
894
+	/**
895
+	 * Gets the child line item as specified by its code. Because this returns an object (by reference)
896
+	 * you can modify this child line item and the parent (this object) can know about them
897
+	 * because it also has a reference to that line item
898
+	 *
899
+	 * @param string $code
900
+	 * @return EE_Base_Class|EE_Line_Item|EE_Soft_Delete_Base_Class|NULL
901
+	 * @throws EE_Error
902
+	 * @throws InvalidArgumentException
903
+	 * @throws InvalidDataTypeException
904
+	 * @throws InvalidInterfaceException
905
+	 * @throws ReflectionException
906
+	 */
907
+	public function get_child_line_item($code)
908
+	{
909
+		if ($this->ID()) {
910
+			return $this->get_model()->get_one(
911
+				array(array('LIN_parent' => $this->ID(), 'LIN_code' => $code))
912
+			);
913
+		}
914
+		return isset($this->_children[ $code ])
915
+			? $this->_children[ $code ]
916
+			: null;
917
+	}
918
+
919
+
920
+	/**
921
+	 * Returns how many items are deleted (or, if this item has not been saved ot the DB yet, just how many it HAD
922
+	 * cached on it)
923
+	 *
924
+	 * @return int
925
+	 * @throws EE_Error
926
+	 * @throws InvalidArgumentException
927
+	 * @throws InvalidDataTypeException
928
+	 * @throws InvalidInterfaceException
929
+	 * @throws ReflectionException
930
+	 */
931
+	public function delete_children_line_items()
932
+	{
933
+		if ($this->ID()) {
934
+			return $this->get_model()->delete(array(array('LIN_parent' => $this->ID())));
935
+		}
936
+		$count = count($this->_children);
937
+		$this->_children = array();
938
+		return $count;
939
+	}
940
+
941
+
942
+	/**
943
+	 * If this line item has been saved to the DB, deletes its child with LIN_code == $code. If this line
944
+	 * HAS NOT been saved to the DB, removes the child line item with index $code.
945
+	 * Also searches through the child's children for a matching line item. However, once a line item has been found
946
+	 * and deleted, stops searching (so if there are line items with duplicate codes, only the first one found will be
947
+	 * deleted)
948
+	 *
949
+	 * @param string $code
950
+	 * @param bool   $stop_search_once_found
951
+	 * @return int count of items deleted (or simply removed from the line item's cache, if not has not been saved to
952
+	 *             the DB yet)
953
+	 * @throws EE_Error
954
+	 * @throws InvalidArgumentException
955
+	 * @throws InvalidDataTypeException
956
+	 * @throws InvalidInterfaceException
957
+	 * @throws ReflectionException
958
+	 */
959
+	public function delete_child_line_item($code, $stop_search_once_found = true)
960
+	{
961
+		if ($this->ID()) {
962
+			$items_deleted = 0;
963
+			if ($this->code() === $code) {
964
+				$items_deleted += EEH_Line_Item::delete_all_child_items($this);
965
+				$items_deleted += (int) $this->delete();
966
+				if ($stop_search_once_found) {
967
+					return $items_deleted;
968
+				}
969
+			}
970
+			foreach ($this->children() as $child_line_item) {
971
+				$items_deleted += $child_line_item->delete_child_line_item($code, $stop_search_once_found);
972
+			}
973
+			return $items_deleted;
974
+		}
975
+		if (isset($this->_children[ $code ])) {
976
+			unset($this->_children[ $code ]);
977
+			return 1;
978
+		}
979
+		return 0;
980
+	}
981
+
982
+
983
+	/**
984
+	 * If this line item is in the database, is of the type subtotal, and
985
+	 * has no children, why do we have it? It should be deleted so this function
986
+	 * does that
987
+	 *
988
+	 * @return boolean
989
+	 * @throws EE_Error
990
+	 * @throws InvalidArgumentException
991
+	 * @throws InvalidDataTypeException
992
+	 * @throws InvalidInterfaceException
993
+	 * @throws ReflectionException
994
+	 */
995
+	public function delete_if_childless_subtotal()
996
+	{
997
+		if ($this->ID() && $this->type() === EEM_Line_Item::type_sub_total && ! $this->children()) {
998
+			return $this->delete();
999
+		}
1000
+		return false;
1001
+	}
1002
+
1003
+
1004
+	/**
1005
+	 * Creates a code and returns a string. doesn't assign the code to this model object
1006
+	 *
1007
+	 * @return string
1008
+	 * @throws EE_Error
1009
+	 * @throws InvalidArgumentException
1010
+	 * @throws InvalidDataTypeException
1011
+	 * @throws InvalidInterfaceException
1012
+	 * @throws ReflectionException
1013
+	 */
1014
+	public function generate_code()
1015
+	{
1016
+		// each line item in the cart requires a unique identifier
1017
+		return md5($this->get('OBJ_type') . $this->get('OBJ_ID') . microtime());
1018
+	}
1019
+
1020
+
1021
+	/**
1022
+	 * @return bool
1023
+	 * @throws EE_Error
1024
+	 * @throws InvalidArgumentException
1025
+	 * @throws InvalidDataTypeException
1026
+	 * @throws InvalidInterfaceException
1027
+	 * @throws ReflectionException
1028
+	 */
1029
+	public function is_tax()
1030
+	{
1031
+		return $this->type() === EEM_Line_Item::type_tax;
1032
+	}
1033
+
1034
+
1035
+	/**
1036
+	 * @return bool
1037
+	 * @throws EE_Error
1038
+	 * @throws InvalidArgumentException
1039
+	 * @throws InvalidDataTypeException
1040
+	 * @throws InvalidInterfaceException
1041
+	 * @throws ReflectionException
1042
+	 */
1043
+	public function is_tax_sub_total()
1044
+	{
1045
+		return $this->type() === EEM_Line_Item::type_tax_sub_total;
1046
+	}
1047
+
1048
+
1049
+	/**
1050
+	 * @return bool
1051
+	 * @throws EE_Error
1052
+	 * @throws InvalidArgumentException
1053
+	 * @throws InvalidDataTypeException
1054
+	 * @throws InvalidInterfaceException
1055
+	 * @throws ReflectionException
1056
+	 */
1057
+	public function is_line_item()
1058
+	{
1059
+		return $this->type() === EEM_Line_Item::type_line_item;
1060
+	}
1061
+
1062
+
1063
+	/**
1064
+	 * @return bool
1065
+	 * @throws EE_Error
1066
+	 * @throws InvalidArgumentException
1067
+	 * @throws InvalidDataTypeException
1068
+	 * @throws InvalidInterfaceException
1069
+	 * @throws ReflectionException
1070
+	 */
1071
+	public function is_sub_line_item()
1072
+	{
1073
+		return $this->type() === EEM_Line_Item::type_sub_line_item;
1074
+	}
1075
+
1076
+
1077
+	/**
1078
+	 * @return bool
1079
+	 * @throws EE_Error
1080
+	 * @throws InvalidArgumentException
1081
+	 * @throws InvalidDataTypeException
1082
+	 * @throws InvalidInterfaceException
1083
+	 * @throws ReflectionException
1084
+	 */
1085
+	public function is_sub_total()
1086
+	{
1087
+		return $this->type() === EEM_Line_Item::type_sub_total;
1088
+	}
1089
+
1090
+
1091
+	/**
1092
+	 * Whether or not this line item is a cancellation line item
1093
+	 *
1094
+	 * @return boolean
1095
+	 * @throws EE_Error
1096
+	 * @throws InvalidArgumentException
1097
+	 * @throws InvalidDataTypeException
1098
+	 * @throws InvalidInterfaceException
1099
+	 * @throws ReflectionException
1100
+	 */
1101
+	public function is_cancellation()
1102
+	{
1103
+		return EEM_Line_Item::type_cancellation === $this->type();
1104
+	}
1105
+
1106
+
1107
+	/**
1108
+	 * @return bool
1109
+	 * @throws EE_Error
1110
+	 * @throws InvalidArgumentException
1111
+	 * @throws InvalidDataTypeException
1112
+	 * @throws InvalidInterfaceException
1113
+	 * @throws ReflectionException
1114
+	 */
1115
+	public function is_total()
1116
+	{
1117
+		return $this->type() === EEM_Line_Item::type_total;
1118
+	}
1119
+
1120
+
1121
+	/**
1122
+	 * @return bool
1123
+	 * @throws EE_Error
1124
+	 * @throws InvalidArgumentException
1125
+	 * @throws InvalidDataTypeException
1126
+	 * @throws InvalidInterfaceException
1127
+	 * @throws ReflectionException
1128
+	 */
1129
+	public function is_cancelled()
1130
+	{
1131
+		return $this->type() === EEM_Line_Item::type_cancellation;
1132
+	}
1133
+
1134
+
1135
+	/**
1136
+	 * @return string like '2, 004.00', formatted according to the localized currency
1137
+	 * @throws EE_Error
1138
+	 * @throws InvalidArgumentException
1139
+	 * @throws InvalidDataTypeException
1140
+	 * @throws InvalidInterfaceException
1141
+	 * @throws ReflectionException
1142
+	 */
1143
+	public function unit_price_no_code()
1144
+	{
1145
+		return $this->get_pretty('LIN_unit_price', 'no_currency_code');
1146
+	}
1147
+
1148
+
1149
+	/**
1150
+	 * @return string like '2, 004.00', formatted according to the localized currency
1151
+	 * @throws EE_Error
1152
+	 * @throws InvalidArgumentException
1153
+	 * @throws InvalidDataTypeException
1154
+	 * @throws InvalidInterfaceException
1155
+	 * @throws ReflectionException
1156
+	 */
1157
+	public function total_no_code()
1158
+	{
1159
+		return $this->get_pretty('LIN_total', 'no_currency_code');
1160
+	}
1161
+
1162
+
1163
+	/**
1164
+	 * Gets the final total on this item, taking taxes into account.
1165
+	 * Has the side-effect of setting the sub-total as it was just calculated.
1166
+	 * If this is used on a grand-total line item, also updates the transaction's
1167
+	 * TXN_total (provided this line item is allowed to persist, otherwise we don't
1168
+	 * want to change a persistable transaction with info from a non-persistent line item)
1169
+	 *
1170
+	 * @param bool $update_txn_status
1171
+	 * @return float
1172
+	 * @throws EE_Error
1173
+	 * @throws InvalidArgumentException
1174
+	 * @throws InvalidDataTypeException
1175
+	 * @throws InvalidInterfaceException
1176
+	 * @throws ReflectionException
1177
+	 * @throws RuntimeException
1178
+	 */
1179
+	public function recalculate_total_including_taxes($update_txn_status = false)
1180
+	{
1181
+		$pre_tax_total = $this->recalculate_pre_tax_total();
1182
+		$tax_total = $this->recalculate_taxes_and_tax_total();
1183
+		$total = $pre_tax_total + $tax_total;
1184
+		// no negative totals plz
1185
+		$total = max($total, 0);
1186
+		$this->set_total($total);
1187
+		// only update the related transaction's total
1188
+		// if we intend to save this line item and its a grand total
1189
+		if (
1190
+			$this->allow_persist() && $this->type() === EEM_Line_Item::type_total
1191
+			&& $this->transaction()
1192
+			   instanceof
1193
+			   EE_Transaction
1194
+		) {
1195
+			$this->transaction()->set_total($total);
1196
+			if ($update_txn_status) {
1197
+				// don't save the TXN because that will be done below
1198
+				// and the following method only saves if the status changes
1199
+				$this->transaction()->update_status_based_on_total_paid(false);
1200
+			}
1201
+			if ($this->transaction()->ID()) {
1202
+				$this->transaction()->save();
1203
+			}
1204
+		}
1205
+		$this->maybe_save();
1206
+		return $total;
1207
+	}
1208
+
1209
+
1210
+	/**
1211
+	 * Recursively goes through all the children and recalculates sub-totals EXCEPT for
1212
+	 * tax-sub-totals (they're a an odd beast). Updates the 'total' on each line item according to either its
1213
+	 * unit price * quantity or the total of all its children EXCEPT when we're only calculating the taxable total and
1214
+	 * when this is called on the grand total
1215
+	 *
1216
+	 * @return float
1217
+	 * @throws EE_Error
1218
+	 * @throws InvalidArgumentException
1219
+	 * @throws InvalidDataTypeException
1220
+	 * @throws InvalidInterfaceException
1221
+	 * @throws ReflectionException
1222
+	 */
1223
+	public function recalculate_pre_tax_total()
1224
+	{
1225
+		$total = 0;
1226
+		$my_children = $this->children();
1227
+		$has_children = ! empty($my_children);
1228
+		if ($has_children && $this->is_line_item()) {
1229
+			$total = $this->_recalculate_pretax_total_for_line_item($total, $my_children);
1230
+		} elseif (! $has_children && ($this->is_sub_line_item() || $this->is_line_item())) {
1231
+			$total = $this->unit_price() * $this->quantity();
1232
+		} elseif ($this->is_sub_total() || $this->is_total()) {
1233
+			$total = $this->_recalculate_pretax_total_for_subtotal($total, $my_children);
1234
+		} elseif ($this->is_tax_sub_total() || $this->is_tax() || $this->is_cancelled()) {
1235
+			// completely ignore tax totals, tax sub-totals, and cancelled line items, when calculating the pre-tax-total
1236
+			return 0;
1237
+		}
1238
+		// ensure all non-line items and non-sub-line-items have a quantity of 1 (except for Events)
1239
+		if (
1240
+			! $this->is_line_item() && ! $this->is_sub_line_item() && ! $this->is_cancellation()
1241
+		) {
1242
+			if ($this->OBJ_type() !== EEM_Line_Item::OBJ_TYPE_EVENT) {
1243
+				$this->set_quantity(1);
1244
+			}
1245
+			if (! $this->is_percent()) {
1246
+				$this->set_unit_price($total);
1247
+			}
1248
+		}
1249
+		// we don't want to bother saving grand totals, because that needs to factor in taxes anyways
1250
+		// so it ought to be
1251
+		if (! $this->is_total()) {
1252
+			$this->set_total($total);
1253
+			// if not a percent line item, make sure we keep the unit price in sync
1254
+			if (
1255
+				$has_children
1256
+				&& $this->is_line_item()
1257
+				&& ! $this->is_percent()
1258
+			) {
1259
+				if ($this->quantity() === 0) {
1260
+					$new_unit_price = 0;
1261
+				} else {
1262
+					$new_unit_price = $this->total() / $this->quantity();
1263
+				}
1264
+				$this->set_unit_price($new_unit_price);
1265
+			}
1266
+			$this->maybe_save();
1267
+		}
1268
+		return $total;
1269
+	}
1270
+
1271
+
1272
+	/**
1273
+	 * Calculates the pretax total when this line item is a subtotal or total line item.
1274
+	 * Basically does a sum-then-round approach (ie, any percent line item that are children
1275
+	 * will calculate their total based on the un-rounded total we're working with so far, and
1276
+	 * THEN round the result; instead of rounding as we go like with sub-line-items)
1277
+	 *
1278
+	 * @param float          $calculated_total_so_far
1279
+	 * @param EE_Line_Item[] $my_children
1280
+	 * @return float
1281
+	 * @throws EE_Error
1282
+	 * @throws InvalidArgumentException
1283
+	 * @throws InvalidDataTypeException
1284
+	 * @throws InvalidInterfaceException
1285
+	 * @throws ReflectionException
1286
+	 */
1287
+	protected function _recalculate_pretax_total_for_subtotal($calculated_total_so_far, $my_children = null)
1288
+	{
1289
+		if ($my_children === null) {
1290
+			$my_children = $this->children();
1291
+		}
1292
+		$subtotal_quantity = 0;
1293
+		// get the total of all its children
1294
+		foreach ($my_children as $child_line_item) {
1295
+			if ($child_line_item instanceof EE_Line_Item && ! $child_line_item->is_cancellation()) {
1296
+				// percentage line items are based on total so far
1297
+				if ($child_line_item->is_percent()) {
1298
+					// round as we go so that the line items add up ok
1299
+					$percent_total = round(
1300
+						$calculated_total_so_far * $child_line_item->percent() / 100,
1301
+						EE_Registry::instance()->CFG->currency->dec_plc
1302
+					);
1303
+					$child_line_item->set_total($percent_total);
1304
+					// so far all percent line items should have a quantity of 1
1305
+					// (ie, no double percent discounts. Although that might be requested someday)
1306
+					$child_line_item->set_quantity(1);
1307
+					$child_line_item->maybe_save();
1308
+					$calculated_total_so_far += $percent_total;
1309
+				} else {
1310
+					// verify flat sub-line-item quantities match their parent
1311
+					if ($child_line_item->is_sub_line_item()) {
1312
+						$child_line_item->set_quantity($this->quantity());
1313
+					}
1314
+					$calculated_total_so_far += $child_line_item->recalculate_pre_tax_total();
1315
+					$subtotal_quantity += $child_line_item->quantity();
1316
+				}
1317
+			}
1318
+		}
1319
+		if ($this->is_sub_total()) {
1320
+			// no negative totals plz
1321
+			$calculated_total_so_far = max($calculated_total_so_far, 0);
1322
+			$subtotal_quantity = $subtotal_quantity > 0 ? 1 : 0;
1323
+			$this->set_quantity($subtotal_quantity);
1324
+			$this->maybe_save();
1325
+		}
1326
+		return $calculated_total_so_far;
1327
+	}
1328
+
1329
+
1330
+	/**
1331
+	 * Calculates the pretax total for a normal line item, in a round-then-sum approach
1332
+	 * (where each sub-line-item is applied to the base price for the line item
1333
+	 * and the result is immediately rounded, rather than summing all the sub-line-items
1334
+	 * then rounding, like we do when recalculating pretax totals on totals and subtotals).
1335
+	 *
1336
+	 * @param float          $calculated_total_so_far
1337
+	 * @param EE_Line_Item[] $my_children
1338
+	 * @return float
1339
+	 * @throws EE_Error
1340
+	 * @throws InvalidArgumentException
1341
+	 * @throws InvalidDataTypeException
1342
+	 * @throws InvalidInterfaceException
1343
+	 * @throws ReflectionException
1344
+	 */
1345
+	protected function _recalculate_pretax_total_for_line_item($calculated_total_so_far, $my_children = null)
1346
+	{
1347
+		if ($my_children === null) {
1348
+			$my_children = $this->children();
1349
+		}
1350
+		// we need to keep track of the running total for a single item,
1351
+		// because we need to round as we go
1352
+		$unit_price_for_total = 0;
1353
+		$quantity_for_total = 1;
1354
+		// get the total of all its children
1355
+		foreach ($my_children as $child_line_item) {
1356
+			if ($child_line_item instanceof EE_Line_Item && ! $child_line_item->is_cancellation()) {
1357
+				if ($child_line_item->is_percent()) {
1358
+					// it should be the unit-price-so-far multiplied by teh percent multiplied by the quantity
1359
+					// not total multiplied by percent, because that ignores rounding along-the-way
1360
+					$percent_unit_price = round(
1361
+						$unit_price_for_total * $child_line_item->percent() / 100,
1362
+						EE_Registry::instance()->CFG->currency->dec_plc
1363
+					);
1364
+					$percent_total = $percent_unit_price * $quantity_for_total;
1365
+					$child_line_item->set_total($percent_total);
1366
+					// so far all percent line items should have a quantity of 1
1367
+					// (ie, no double percent discounts. Although that might be requested someday)
1368
+					$child_line_item->set_quantity(1);
1369
+					$child_line_item->maybe_save();
1370
+					$calculated_total_so_far += $percent_total;
1371
+					$unit_price_for_total += $percent_unit_price;
1372
+				} else {
1373
+					// verify flat sub-line-item quantities match their parent
1374
+					if ($child_line_item->is_sub_line_item()) {
1375
+						$child_line_item->set_quantity($this->quantity());
1376
+					}
1377
+					$quantity_for_total = $child_line_item->quantity();
1378
+					$calculated_total_so_far += $child_line_item->recalculate_pre_tax_total();
1379
+					$unit_price_for_total += $child_line_item->unit_price();
1380
+				}
1381
+			}
1382
+		}
1383
+		return $calculated_total_so_far;
1384
+	}
1385
+
1386
+
1387
+	/**
1388
+	 * Recalculates the total on each individual tax (based on a recalculation of the pre-tax total), sets
1389
+	 * the totals on each tax calculated, and returns the final tax total. Re-saves tax line items
1390
+	 * and tax sub-total if already in the DB
1391
+	 *
1392
+	 * @return float
1393
+	 * @throws EE_Error
1394
+	 * @throws InvalidArgumentException
1395
+	 * @throws InvalidDataTypeException
1396
+	 * @throws InvalidInterfaceException
1397
+	 * @throws ReflectionException
1398
+	 */
1399
+	public function recalculate_taxes_and_tax_total()
1400
+	{
1401
+		// get all taxes
1402
+		$taxes = $this->tax_descendants();
1403
+		// calculate the pretax total
1404
+		$taxable_total = $this->taxable_total();
1405
+		$tax_total = 0;
1406
+		foreach ($taxes as $tax) {
1407
+			$total_on_this_tax = $taxable_total * $tax->percent() / 100;
1408
+			// remember the total on this line item
1409
+			$tax->set_total($total_on_this_tax);
1410
+			$tax->maybe_save();
1411
+			$tax_total += $tax->total();
1412
+		}
1413
+		$this->_recalculate_tax_sub_total();
1414
+		return $tax_total;
1415
+	}
1416
+
1417
+
1418
+	/**
1419
+	 * Simply forces all the tax-sub-totals to recalculate. Assumes the taxes have been calculated
1420
+	 *
1421
+	 * @return void
1422
+	 * @throws EE_Error
1423
+	 * @throws InvalidArgumentException
1424
+	 * @throws InvalidDataTypeException
1425
+	 * @throws InvalidInterfaceException
1426
+	 * @throws ReflectionException
1427
+	 */
1428
+	private function _recalculate_tax_sub_total()
1429
+	{
1430
+		if ($this->is_tax_sub_total()) {
1431
+			$total = 0;
1432
+			$total_percent = 0;
1433
+			// simply loop through all its children (which should be taxes) and sum their total
1434
+			foreach ($this->children() as $child_tax) {
1435
+				if ($child_tax instanceof EE_Line_Item) {
1436
+					$total += $child_tax->total();
1437
+					$total_percent += $child_tax->percent();
1438
+				}
1439
+			}
1440
+			$this->set_total($total);
1441
+			$this->set_percent($total_percent);
1442
+			$this->maybe_save();
1443
+		} elseif ($this->is_total()) {
1444
+			foreach ($this->children() as $maybe_tax_subtotal) {
1445
+				if ($maybe_tax_subtotal instanceof EE_Line_Item) {
1446
+					$maybe_tax_subtotal->_recalculate_tax_sub_total();
1447
+				}
1448
+			}
1449
+		}
1450
+	}
1451
+
1452
+
1453
+	/**
1454
+	 * Gets the total tax on this line item. Assumes taxes have already been calculated using
1455
+	 * recalculate_taxes_and_total
1456
+	 *
1457
+	 * @return float
1458
+	 * @throws EE_Error
1459
+	 * @throws InvalidArgumentException
1460
+	 * @throws InvalidDataTypeException
1461
+	 * @throws InvalidInterfaceException
1462
+	 * @throws ReflectionException
1463
+	 */
1464
+	public function get_total_tax()
1465
+	{
1466
+		$this->_recalculate_tax_sub_total();
1467
+		$total = 0;
1468
+		foreach ($this->tax_descendants() as $tax_line_item) {
1469
+			if ($tax_line_item instanceof EE_Line_Item) {
1470
+				$total += $tax_line_item->total();
1471
+			}
1472
+		}
1473
+		return $total;
1474
+	}
1475
+
1476
+
1477
+	/**
1478
+	 * Gets the total for all the items purchased only
1479
+	 *
1480
+	 * @return float
1481
+	 * @throws EE_Error
1482
+	 * @throws InvalidArgumentException
1483
+	 * @throws InvalidDataTypeException
1484
+	 * @throws InvalidInterfaceException
1485
+	 * @throws ReflectionException
1486
+	 */
1487
+	public function get_items_total()
1488
+	{
1489
+		// by default, let's make sure we're consistent with the existing line item
1490
+		if ($this->is_total()) {
1491
+			$pretax_subtotal_li = EEH_Line_Item::get_pre_tax_subtotal($this);
1492
+			if ($pretax_subtotal_li instanceof EE_Line_Item) {
1493
+				return $pretax_subtotal_li->total();
1494
+			}
1495
+		}
1496
+		$total = 0;
1497
+		foreach ($this->get_items() as $item) {
1498
+			if ($item instanceof EE_Line_Item) {
1499
+				$total += $item->total();
1500
+			}
1501
+		}
1502
+		return $total;
1503
+	}
1504
+
1505
+
1506
+	/**
1507
+	 * Gets all the descendants (ie, children or children of children etc) that
1508
+	 * are of the type 'tax'
1509
+	 *
1510
+	 * @return EE_Line_Item[]
1511
+	 * @throws EE_Error
1512
+	 */
1513
+	public function tax_descendants()
1514
+	{
1515
+		return EEH_Line_Item::get_tax_descendants($this);
1516
+	}
1517
+
1518
+
1519
+	/**
1520
+	 * Gets all the real items purchased which are children of this item
1521
+	 *
1522
+	 * @return EE_Line_Item[]
1523
+	 * @throws EE_Error
1524
+	 */
1525
+	public function get_items()
1526
+	{
1527
+		return EEH_Line_Item::get_line_item_descendants($this);
1528
+	}
1529
+
1530
+
1531
+	/**
1532
+	 * Returns the amount taxable among this line item's children (or if it has no children,
1533
+	 * how much of it is taxable). Does not recalculate totals or subtotals.
1534
+	 * If the taxable total is negative, (eg, if none of the tickets were taxable,
1535
+	 * but there is a "Taxable" discount), returns 0.
1536
+	 *
1537
+	 * @return float
1538
+	 * @throws EE_Error
1539
+	 * @throws InvalidArgumentException
1540
+	 * @throws InvalidDataTypeException
1541
+	 * @throws InvalidInterfaceException
1542
+	 * @throws ReflectionException
1543
+	 */
1544
+	public function taxable_total()
1545
+	{
1546
+		$total = 0;
1547
+		if ($this->children()) {
1548
+			foreach ($this->children() as $child_line_item) {
1549
+				if ($child_line_item->type() === EEM_Line_Item::type_line_item && $child_line_item->is_taxable()) {
1550
+					// if it's a percent item, only take into account the percent
1551
+					// that's taxable too (the taxable total so far)
1552
+					if ($child_line_item->is_percent()) {
1553
+						$total += ($total * $child_line_item->percent() / 100);
1554
+					} else {
1555
+						$total += $child_line_item->total();
1556
+					}
1557
+				} elseif ($child_line_item->type() === EEM_Line_Item::type_sub_total) {
1558
+					$total += $child_line_item->taxable_total();
1559
+				}
1560
+			}
1561
+		}
1562
+		return max($total, 0);
1563
+	}
1564
+
1565
+
1566
+	/**
1567
+	 * Gets the transaction for this line item
1568
+	 *
1569
+	 * @return EE_Base_Class|EE_Transaction
1570
+	 * @throws EE_Error
1571
+	 * @throws InvalidArgumentException
1572
+	 * @throws InvalidDataTypeException
1573
+	 * @throws InvalidInterfaceException
1574
+	 * @throws ReflectionException
1575
+	 */
1576
+	public function transaction()
1577
+	{
1578
+		return $this->get_first_related(EEM_Line_Item::OBJ_TYPE_TRANSACTION);
1579
+	}
1580
+
1581
+
1582
+	/**
1583
+	 * Saves this line item to the DB, and recursively saves its descendants.
1584
+	 * Because there currently is no proper parent-child relation on the model,
1585
+	 * save_this_and_cached() will NOT save the descendants.
1586
+	 * Also sets the transaction on this line item and all its descendants before saving
1587
+	 *
1588
+	 * @param int $txn_id if none is provided, assumes $this->TXN_ID()
1589
+	 * @return int count of items saved
1590
+	 * @throws EE_Error
1591
+	 * @throws InvalidArgumentException
1592
+	 * @throws InvalidDataTypeException
1593
+	 * @throws InvalidInterfaceException
1594
+	 * @throws ReflectionException
1595
+	 */
1596
+	public function save_this_and_descendants_to_txn($txn_id = null)
1597
+	{
1598
+		$count = 0;
1599
+		if (! $txn_id) {
1600
+			$txn_id = $this->TXN_ID();
1601
+		}
1602
+		$this->set_TXN_ID($txn_id);
1603
+		$children = $this->children();
1604
+		$count += $this->save()
1605
+			? 1
1606
+			: 0;
1607
+		foreach ($children as $child_line_item) {
1608
+			if ($child_line_item instanceof EE_Line_Item) {
1609
+				$child_line_item->set_parent_ID($this->ID());
1610
+				$count += $child_line_item->save_this_and_descendants_to_txn($txn_id);
1611
+			}
1612
+		}
1613
+		return $count;
1614
+	}
1615
+
1616
+
1617
+	/**
1618
+	 * Saves this line item to the DB, and recursively saves its descendants.
1619
+	 *
1620
+	 * @return int count of items saved
1621
+	 * @throws EE_Error
1622
+	 * @throws InvalidArgumentException
1623
+	 * @throws InvalidDataTypeException
1624
+	 * @throws InvalidInterfaceException
1625
+	 * @throws ReflectionException
1626
+	 */
1627
+	public function save_this_and_descendants()
1628
+	{
1629
+		$count = 0;
1630
+		$children = $this->children();
1631
+		$count += $this->save()
1632
+			? 1
1633
+			: 0;
1634
+		foreach ($children as $child_line_item) {
1635
+			if ($child_line_item instanceof EE_Line_Item) {
1636
+				$child_line_item->set_parent_ID($this->ID());
1637
+				$count += $child_line_item->save_this_and_descendants();
1638
+			}
1639
+		}
1640
+		return $count;
1641
+	}
1642
+
1643
+
1644
+	/**
1645
+	 * returns the cancellation line item if this item was cancelled
1646
+	 *
1647
+	 * @return EE_Line_Item[]
1648
+	 * @throws InvalidArgumentException
1649
+	 * @throws InvalidInterfaceException
1650
+	 * @throws InvalidDataTypeException
1651
+	 * @throws ReflectionException
1652
+	 * @throws EE_Error
1653
+	 */
1654
+	public function get_cancellations()
1655
+	{
1656
+		EE_Registry::instance()->load_helper('Line_Item');
1657
+		return EEH_Line_Item::get_descendants_of_type($this, EEM_Line_Item::type_cancellation);
1658
+	}
1659
+
1660
+
1661
+	/**
1662
+	 * If this item has an ID, then this saves it again to update the db
1663
+	 *
1664
+	 * @return int count of items saved
1665
+	 * @throws EE_Error
1666
+	 * @throws InvalidArgumentException
1667
+	 * @throws InvalidDataTypeException
1668
+	 * @throws InvalidInterfaceException
1669
+	 * @throws ReflectionException
1670
+	 */
1671
+	public function maybe_save()
1672
+	{
1673
+		if ($this->ID()) {
1674
+			return $this->save();
1675
+		}
1676
+		return false;
1677
+	}
1678
+
1679
+
1680
+	/**
1681
+	 * clears the cached children and parent from the line item
1682
+	 *
1683
+	 * @return void
1684
+	 */
1685
+	public function clear_related_line_item_cache()
1686
+	{
1687
+		$this->_children = array();
1688
+		$this->_parent = null;
1689
+	}
1690
+
1691
+
1692
+	/**
1693
+	 * @param bool $raw
1694
+	 * @return int
1695
+	 * @throws EE_Error
1696
+	 * @throws InvalidArgumentException
1697
+	 * @throws InvalidDataTypeException
1698
+	 * @throws InvalidInterfaceException
1699
+	 * @throws ReflectionException
1700
+	 */
1701
+	public function timestamp($raw = false)
1702
+	{
1703
+		return $raw
1704
+			? $this->get_raw('LIN_timestamp')
1705
+			: $this->get('LIN_timestamp');
1706
+	}
1707
+
1708
+
1709
+
1710
+
1711
+	/************************* DEPRECATED *************************/
1712
+	/**
1713
+	 * @deprecated 4.6.0
1714
+	 * @param string $type one of the constants on EEM_Line_Item
1715
+	 * @return EE_Line_Item[]
1716
+	 * @throws EE_Error
1717
+	 */
1718
+	protected function _get_descendants_of_type($type)
1719
+	{
1720
+		EE_Error::doing_it_wrong(
1721
+			'EE_Line_Item::_get_descendants_of_type()',
1722
+			sprintf(
1723
+				esc_html__('Method replaced with %1$s', 'event_espresso'),
1724
+				'EEH_Line_Item::get_descendants_of_type()'
1725
+			),
1726
+			'4.6.0'
1727
+		);
1728
+		return EEH_Line_Item::get_descendants_of_type($this, $type);
1729
+	}
1730
+
1731
+
1732
+	/**
1733
+	 * @deprecated 4.6.0
1734
+	 * @param string $type like one of the EEM_Line_Item::type_*
1735
+	 * @return EE_Line_Item
1736
+	 * @throws EE_Error
1737
+	 * @throws InvalidArgumentException
1738
+	 * @throws InvalidDataTypeException
1739
+	 * @throws InvalidInterfaceException
1740
+	 * @throws ReflectionException
1741
+	 */
1742
+	public function get_nearest_descendant_of_type($type)
1743
+	{
1744
+		EE_Error::doing_it_wrong(
1745
+			'EE_Line_Item::get_nearest_descendant_of_type()',
1746
+			sprintf(
1747
+				esc_html__('Method replaced with %1$s', 'event_espresso'),
1748
+				'EEH_Line_Item::get_nearest_descendant_of_type()'
1749
+			),
1750
+			'4.6.0'
1751
+		);
1752
+		return EEH_Line_Item::get_nearest_descendant_of_type($this, $type);
1753
+	}
1754 1754
 }
Please login to merge, or discard this patch.
core/domain/entities/editor/blocks/EventAttendees.php 2 patches
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -154,19 +154,19 @@
 block discarded – undo
154 154
         $sanitized_attributes = array();
155 155
         foreach ($attributes as $attribute => $value) {
156 156
             $convert = $this->getAttributesMap();
157
-            if (isset($convert[ $attribute ])) {
158
-                $sanitize = $convert[ $attribute ];
157
+            if (isset($convert[$attribute])) {
158
+                $sanitize = $convert[$attribute];
159 159
                 if ($sanitize === 'bool') {
160
-                    $sanitized_attributes[ $attribute ] = filter_var(
160
+                    $sanitized_attributes[$attribute] = filter_var(
161 161
                         $value,
162 162
                         FILTER_VALIDATE_BOOLEAN
163 163
                     );
164 164
                 } else {
165
-                    $sanitized_attributes[ $attribute ] = $sanitize($value);
165
+                    $sanitized_attributes[$attribute] = $sanitize($value);
166 166
                 }
167 167
                 // don't pass along attributes with a 0 value
168
-                if ($sanitized_attributes[ $attribute ] === 0) {
169
-                    unset($sanitized_attributes[ $attribute ]);
168
+                if ($sanitized_attributes[$attribute] === 0) {
169
+                    unset($sanitized_attributes[$attribute]);
170 170
                 }
171 171
             }
172 172
         }
Please login to merge, or discard this patch.
Indentation   +157 added lines, -157 removed lines patch added patch discarded remove patch
@@ -20,172 +20,172 @@
 block discarded – undo
20 20
  */
21 21
 class EventAttendees extends Block
22 22
 {
23
-    const BLOCK_TYPE = 'event-attendees';
23
+	const BLOCK_TYPE = 'event-attendees';
24 24
 
25
-    /**
26
-     * @var EventAttendeesBlockRenderer $renderer
27
-     */
28
-    protected $renderer;
25
+	/**
26
+	 * @var EventAttendeesBlockRenderer $renderer
27
+	 */
28
+	protected $renderer;
29 29
 
30 30
 
31
-    /**
32
-     * EventAttendees constructor.
33
-     *
34
-     * @param CoreBlocksAssetManager      $block_asset_manager
35
-     * @param RequestInterface            $request
36
-     * @param EventAttendeesBlockRenderer $renderer
37
-     */
38
-    public function __construct(
39
-        CoreBlocksAssetManager $block_asset_manager,
40
-        RequestInterface $request,
41
-        EventAttendeesBlockRenderer $renderer
42
-    ) {
43
-        parent::__construct($block_asset_manager, $request);
44
-        $this->renderer = $renderer;
45
-    }
31
+	/**
32
+	 * EventAttendees constructor.
33
+	 *
34
+	 * @param CoreBlocksAssetManager      $block_asset_manager
35
+	 * @param RequestInterface            $request
36
+	 * @param EventAttendeesBlockRenderer $renderer
37
+	 */
38
+	public function __construct(
39
+		CoreBlocksAssetManager $block_asset_manager,
40
+		RequestInterface $request,
41
+		EventAttendeesBlockRenderer $renderer
42
+	) {
43
+		parent::__construct($block_asset_manager, $request);
44
+		$this->renderer = $renderer;
45
+	}
46 46
 
47 47
 
48
-    /**
49
-     * Perform any early setup required by the block
50
-     * including setting the block type and supported post types
51
-     *
52
-     * @return void
53
-     */
54
-    public function initialize()
55
-    {
56
-        $this->setBlockType(self::BLOCK_TYPE);
57
-        $this->setSupportedRoutes(
58
-            array(
59
-                'EventEspresso\core\domain\entities\route_match\specifications\admin\EspressoStandardPostTypeEditor',
60
-                'EventEspresso\core\domain\entities\route_match\specifications\admin\WordPressPostTypeEditor',
61
-                'EventEspresso\core\domain\entities\route_match\specifications\frontend\EspressoBlockRenderer',
62
-                'EventEspresso\core\domain\entities\route_match\specifications\frontend\AnyFrontendRequest'
63
-            )
64
-        );
65
-        $EVT_ID = $this->request->getRequestParam('page') === 'espresso_events'
66
-            ? $this->request->getRequestParam('post', 0, 'int')
67
-            : 0;
68
-        $this->setAttributes(
69
-            array(
70
-                'eventId'           => array(
71
-                    'type'    => 'number',
72
-                    'default' => $EVT_ID,
73
-                ),
74
-                'datetimeId'        => array(
75
-                    'type'    => 'number',
76
-                    'default' => 0,
77
-                ),
78
-                'ticketId'          => array(
79
-                    'type'    => 'number',
80
-                    'default' => 0,
81
-                ),
82
-                'status'            => array(
83
-                    'type'    => 'string',
84
-                    'default' => EEM_Registration::status_id_approved,
85
-                ),
86
-                'limit'             => array(
87
-                    'type'    => 'number',
88
-                    'default' => 100,
89
-                ),
90
-                'order' => array(
91
-                    'type' => 'string',
92
-                    'default' => 'ASC'
93
-                ),
94
-                'orderBy' => array(
95
-                    'type' => 'string',
96
-                    'default' => 'lastThenFirstName',
97
-                ),
98
-                'showGravatar'      => array(
99
-                    'type'    => 'boolean',
100
-                    'default' => false,
101
-                ),
102
-                'avatarClass' => array(
103
-                    'type' => 'string',
104
-                    'default' => 'contact',
105
-                ),
106
-                'avatarSize' => array(
107
-                    'type' => 'number',
108
-                    'default' => 24,
109
-                ),
110
-                'displayOnArchives' => array(
111
-                    'type'    => 'boolean',
112
-                    'default' => false,
113
-                ),
114
-            )
115
-        );
116
-        $this->setDynamic();
117
-    }
48
+	/**
49
+	 * Perform any early setup required by the block
50
+	 * including setting the block type and supported post types
51
+	 *
52
+	 * @return void
53
+	 */
54
+	public function initialize()
55
+	{
56
+		$this->setBlockType(self::BLOCK_TYPE);
57
+		$this->setSupportedRoutes(
58
+			array(
59
+				'EventEspresso\core\domain\entities\route_match\specifications\admin\EspressoStandardPostTypeEditor',
60
+				'EventEspresso\core\domain\entities\route_match\specifications\admin\WordPressPostTypeEditor',
61
+				'EventEspresso\core\domain\entities\route_match\specifications\frontend\EspressoBlockRenderer',
62
+				'EventEspresso\core\domain\entities\route_match\specifications\frontend\AnyFrontendRequest'
63
+			)
64
+		);
65
+		$EVT_ID = $this->request->getRequestParam('page') === 'espresso_events'
66
+			? $this->request->getRequestParam('post', 0, 'int')
67
+			: 0;
68
+		$this->setAttributes(
69
+			array(
70
+				'eventId'           => array(
71
+					'type'    => 'number',
72
+					'default' => $EVT_ID,
73
+				),
74
+				'datetimeId'        => array(
75
+					'type'    => 'number',
76
+					'default' => 0,
77
+				),
78
+				'ticketId'          => array(
79
+					'type'    => 'number',
80
+					'default' => 0,
81
+				),
82
+				'status'            => array(
83
+					'type'    => 'string',
84
+					'default' => EEM_Registration::status_id_approved,
85
+				),
86
+				'limit'             => array(
87
+					'type'    => 'number',
88
+					'default' => 100,
89
+				),
90
+				'order' => array(
91
+					'type' => 'string',
92
+					'default' => 'ASC'
93
+				),
94
+				'orderBy' => array(
95
+					'type' => 'string',
96
+					'default' => 'lastThenFirstName',
97
+				),
98
+				'showGravatar'      => array(
99
+					'type'    => 'boolean',
100
+					'default' => false,
101
+				),
102
+				'avatarClass' => array(
103
+					'type' => 'string',
104
+					'default' => 'contact',
105
+				),
106
+				'avatarSize' => array(
107
+					'type' => 'number',
108
+					'default' => 24,
109
+				),
110
+				'displayOnArchives' => array(
111
+					'type'    => 'boolean',
112
+					'default' => false,
113
+				),
114
+			)
115
+		);
116
+		$this->setDynamic();
117
+	}
118 118
 
119 119
 
120
-    /**
121
-     * Returns an array where the key corresponds to the incoming attribute name from the WP block
122
-     * and the value corresponds to the attribute name for the existing EspressoEventAttendees shortcode
123
-     *
124
-     * @since 4.9.71.p
125
-     * @return array
126
-     */
127
-    private function getAttributesMap()
128
-    {
129
-        return array(
130
-            'eventId'           => 'absint',
131
-            'datetimeId'        => 'absint',
132
-            'ticketId'          => 'absint',
133
-            'status'            => 'sanitize_text_field',
134
-            'limit'             => 'intval',
135
-            'showGravatar'      => 'bool',
136
-            'avatarClass'       => 'sanitize_text_field',
137
-            'avatarSize'        => 'absint',
138
-            'displayOnArchives' => 'bool',
139
-            'order' => 'sanitize_text_field',
140
-            'orderBy' => 'sanitize_text_field',
141
-        );
142
-    }
120
+	/**
121
+	 * Returns an array where the key corresponds to the incoming attribute name from the WP block
122
+	 * and the value corresponds to the attribute name for the existing EspressoEventAttendees shortcode
123
+	 *
124
+	 * @since 4.9.71.p
125
+	 * @return array
126
+	 */
127
+	private function getAttributesMap()
128
+	{
129
+		return array(
130
+			'eventId'           => 'absint',
131
+			'datetimeId'        => 'absint',
132
+			'ticketId'          => 'absint',
133
+			'status'            => 'sanitize_text_field',
134
+			'limit'             => 'intval',
135
+			'showGravatar'      => 'bool',
136
+			'avatarClass'       => 'sanitize_text_field',
137
+			'avatarSize'        => 'absint',
138
+			'displayOnArchives' => 'bool',
139
+			'order' => 'sanitize_text_field',
140
+			'orderBy' => 'sanitize_text_field',
141
+		);
142
+	}
143 143
 
144 144
 
145
-    /**
146
-     * Sanitizes attributes.
147
-     *
148
-     * @param array $attributes
149
-     * @return array
150
-     */
151
-    private function sanitizeAttributes(array $attributes)
152
-    {
153
-        $sanitized_attributes = array();
154
-        foreach ($attributes as $attribute => $value) {
155
-            $convert = $this->getAttributesMap();
156
-            if (isset($convert[ $attribute ])) {
157
-                $sanitize = $convert[ $attribute ];
158
-                if ($sanitize === 'bool') {
159
-                    $sanitized_attributes[ $attribute ] = filter_var(
160
-                        $value,
161
-                        FILTER_VALIDATE_BOOLEAN
162
-                    );
163
-                } else {
164
-                    $sanitized_attributes[ $attribute ] = $sanitize($value);
165
-                }
166
-                // don't pass along attributes with a 0 value
167
-                if ($sanitized_attributes[ $attribute ] === 0) {
168
-                    unset($sanitized_attributes[ $attribute ]);
169
-                }
170
-            }
171
-        }
172
-        return $attributes;
173
-    }
145
+	/**
146
+	 * Sanitizes attributes.
147
+	 *
148
+	 * @param array $attributes
149
+	 * @return array
150
+	 */
151
+	private function sanitizeAttributes(array $attributes)
152
+	{
153
+		$sanitized_attributes = array();
154
+		foreach ($attributes as $attribute => $value) {
155
+			$convert = $this->getAttributesMap();
156
+			if (isset($convert[ $attribute ])) {
157
+				$sanitize = $convert[ $attribute ];
158
+				if ($sanitize === 'bool') {
159
+					$sanitized_attributes[ $attribute ] = filter_var(
160
+						$value,
161
+						FILTER_VALIDATE_BOOLEAN
162
+					);
163
+				} else {
164
+					$sanitized_attributes[ $attribute ] = $sanitize($value);
165
+				}
166
+				// don't pass along attributes with a 0 value
167
+				if ($sanitized_attributes[ $attribute ] === 0) {
168
+					unset($sanitized_attributes[ $attribute ]);
169
+				}
170
+			}
171
+		}
172
+		return $attributes;
173
+	}
174 174
 
175 175
 
176
-    /**
177
-     * Returns the rendered HTML for the block
178
-     *
179
-     * @param array $attributes
180
-     * @return string
181
-     * @throws DomainException
182
-     * @throws EE_Error
183
-     */
184
-    public function renderBlock(array $attributes = array())
185
-    {
186
-        $attributes = $this->sanitizeAttributes($attributes);
187
-        return (is_archive() || is_front_page() || is_home()) && ! $attributes['displayOnArchives']
188
-            ? ''
189
-            : $this->renderer->render($attributes);
190
-    }
176
+	/**
177
+	 * Returns the rendered HTML for the block
178
+	 *
179
+	 * @param array $attributes
180
+	 * @return string
181
+	 * @throws DomainException
182
+	 * @throws EE_Error
183
+	 */
184
+	public function renderBlock(array $attributes = array())
185
+	{
186
+		$attributes = $this->sanitizeAttributes($attributes);
187
+		return (is_archive() || is_front_page() || is_home()) && ! $attributes['displayOnArchives']
188
+			? ''
189
+			: $this->renderer->render($attributes);
190
+	}
191 191
 }
Please login to merge, or discard this patch.
strategies/EE_Restriction_Generator_Default_Protected.strategy.php 2 patches
Indentation   +75 added lines, -75 removed lines patch added patch discarded remove patch
@@ -17,85 +17,85 @@
 block discarded – undo
17 17
  */
18 18
 class EE_Restriction_Generator_Default_Protected extends EE_Restriction_Generator_Base
19 19
 {
20
-    /**
21
-     * Name of the field on this model (or a related model, including the model chain to it)
22
-     * that is a boolean indicating whether or not a model object is considered "Default" or not
23
-     * @var string
24
-     */
25
-    protected $_default_field_name;
20
+	/**
21
+	 * Name of the field on this model (or a related model, including the model chain to it)
22
+	 * that is a boolean indicating whether or not a model object is considered "Default" or not
23
+	 * @var string
24
+	 */
25
+	protected $_default_field_name;
26 26
 
27
-    /**
28
-     * The model chain to follow to get to the event model, including the event model itself.
29
-     * Eg 'Ticket.Datetime.Event'
30
-     * @var string
31
-     */
32
-    protected $_path_to_event_model;
33
-    /**
34
-     *
35
-     * @param string $default_field_name the name of the field Name of the field on this model (or a related model, including the model chain to it)
36
-     * that is a boolean indicating whether or not a model object is considered "Default" or not
37
-     * @param string $path_to_event_model The model chain to follow to get to the event model, including the event model itself.
38
-     * Eg 'Ticket.Datetime.Event'
39
-     */
40
-    public function __construct($default_field_name, $path_to_event_model)
41
-    {
42
-        $this->_default_field_name = $default_field_name;
43
-        if (substr($path_to_event_model, -1, 1) != '.') {
44
-            $path_to_event_model .= '.';
45
-        }
46
-        $this->_path_to_event_model = $path_to_event_model;
47
-    }
27
+	/**
28
+	 * The model chain to follow to get to the event model, including the event model itself.
29
+	 * Eg 'Ticket.Datetime.Event'
30
+	 * @var string
31
+	 */
32
+	protected $_path_to_event_model;
33
+	/**
34
+	 *
35
+	 * @param string $default_field_name the name of the field Name of the field on this model (or a related model, including the model chain to it)
36
+	 * that is a boolean indicating whether or not a model object is considered "Default" or not
37
+	 * @param string $path_to_event_model The model chain to follow to get to the event model, including the event model itself.
38
+	 * Eg 'Ticket.Datetime.Event'
39
+	 */
40
+	public function __construct($default_field_name, $path_to_event_model)
41
+	{
42
+		$this->_default_field_name = $default_field_name;
43
+		if (substr($path_to_event_model, -1, 1) != '.') {
44
+			$path_to_event_model .= '.';
45
+		}
46
+		$this->_path_to_event_model = $path_to_event_model;
47
+	}
48 48
 
49 49
 
50 50
 
51
-    /**
52
-     *
53
-     * @return \EE_Default_Where_Conditions
54
-     */
55
-    protected function _generate_restrictions()
56
-    {
57
-        // if there are no standard caps for this model, then for now all we know is
58
-        // if they need the default cap to access this
59
-        if (! $this->model()->cap_slug()) {
60
-            return array(
61
-                self::get_default_restrictions_cap() => new EE_Return_None_Where_Conditions()
62
-            );
63
-        }
51
+	/**
52
+	 *
53
+	 * @return \EE_Default_Where_Conditions
54
+	 */
55
+	protected function _generate_restrictions()
56
+	{
57
+		// if there are no standard caps for this model, then for now all we know is
58
+		// if they need the default cap to access this
59
+		if (! $this->model()->cap_slug()) {
60
+			return array(
61
+				self::get_default_restrictions_cap() => new EE_Return_None_Where_Conditions()
62
+			);
63
+		}
64 64
 
65
-        $event_model = EEM_Event::instance();
65
+		$event_model = EEM_Event::instance();
66 66
 
67
-        $restrictions =  array(
68
-            // first: basically access to non-defaults is essentially controlled by which events are accessible
69
-            // if they don't have the basic event cap, they can't access ANY non-default items
70
-            EE_Restriction_Generator_Base::get_cap_name($event_model, $this->action()) => new EE_Default_Where_Conditions(array( $this->_default_field_name => true )),
71
-            // if they don't have the others event cap, they can't access others' non-default items
72
-            EE_Restriction_Generator_Base::get_cap_name($event_model, $this->action() . '_others') => new EE_Default_Where_Conditions(array(
73
-                'OR*no_' . EE_Restriction_Generator_Base::get_cap_name($event_model, $this->action() . '_others') => array(
74
-                    $this->_path_to_event_model . 'EVT_wp_user' => EE_Default_Where_Conditions::current_user_placeholder ),
75
-                    $this->_default_field_name => true )),
76
-            // if they have basic and others, but not private, they can't access others' private non-default items
77
-            EE_Restriction_Generator_Base::get_cap_name($event_model, $this->action() . '_private') => new EE_Default_Where_Conditions(array(
78
-                'OR*no_' . EE_Restriction_Generator_Base::get_cap_name($event_model, $this->action() . '_private') => array(
79
-                $this->_path_to_event_model . 'EVT_wp_user' => EE_Default_Where_Conditions::current_user_placeholder,
80
-                $this->_path_to_event_model . 'status' => array( '!=', 'private' ),
81
-                $this->_default_field_name => true ) )),
82
-            // second: access to defaults is controlled by the defaulty capabilities
83
-            // if they don't have the default capability, restrict access to only non-default items
84
-            EE_Restriction_Generator_Base::get_cap_name($this->model(), $this->action() . '_default') => new EE_Default_Where_Conditions(array( $this->_default_field_name => false )),
85
-            // if they don't have the "others" default capability, restrict access to only their default ones, and non-default ones
86
-             );
87
-        if (EE_Restriction_Generator_Base::is_cap($this->model(), $this->action() . '_others_default')) {
88
-            $restrictions[ EE_Restriction_Generator_Base::get_cap_name($this->model(), $this->action() . '_others_default') ] = new EE_Default_Where_Conditions(array(
89
-                    // if they don't have the others default cap, they can't access others default items (but they can access
90
-                    // their own default items, and non-default items)
91
-                    'OR*no_' . EE_Restriction_Generator_Base::get_cap_name($this->model(), $this->action() . '_others_default') => array(
92
-                        'AND' => array(
93
-                            $this->_path_to_event_model . 'EVT_wp_user' => EE_Default_Where_Conditions::current_user_placeholder,
94
-                            $this->_default_field_name => true
95
-                            ),
96
-                        $this->_default_field_name => false
97
-                    ) ));
98
-        }
99
-        return $restrictions;
100
-    }
67
+		$restrictions =  array(
68
+			// first: basically access to non-defaults is essentially controlled by which events are accessible
69
+			// if they don't have the basic event cap, they can't access ANY non-default items
70
+			EE_Restriction_Generator_Base::get_cap_name($event_model, $this->action()) => new EE_Default_Where_Conditions(array( $this->_default_field_name => true )),
71
+			// if they don't have the others event cap, they can't access others' non-default items
72
+			EE_Restriction_Generator_Base::get_cap_name($event_model, $this->action() . '_others') => new EE_Default_Where_Conditions(array(
73
+				'OR*no_' . EE_Restriction_Generator_Base::get_cap_name($event_model, $this->action() . '_others') => array(
74
+					$this->_path_to_event_model . 'EVT_wp_user' => EE_Default_Where_Conditions::current_user_placeholder ),
75
+					$this->_default_field_name => true )),
76
+			// if they have basic and others, but not private, they can't access others' private non-default items
77
+			EE_Restriction_Generator_Base::get_cap_name($event_model, $this->action() . '_private') => new EE_Default_Where_Conditions(array(
78
+				'OR*no_' . EE_Restriction_Generator_Base::get_cap_name($event_model, $this->action() . '_private') => array(
79
+				$this->_path_to_event_model . 'EVT_wp_user' => EE_Default_Where_Conditions::current_user_placeholder,
80
+				$this->_path_to_event_model . 'status' => array( '!=', 'private' ),
81
+				$this->_default_field_name => true ) )),
82
+			// second: access to defaults is controlled by the defaulty capabilities
83
+			// if they don't have the default capability, restrict access to only non-default items
84
+			EE_Restriction_Generator_Base::get_cap_name($this->model(), $this->action() . '_default') => new EE_Default_Where_Conditions(array( $this->_default_field_name => false )),
85
+			// if they don't have the "others" default capability, restrict access to only their default ones, and non-default ones
86
+			 );
87
+		if (EE_Restriction_Generator_Base::is_cap($this->model(), $this->action() . '_others_default')) {
88
+			$restrictions[ EE_Restriction_Generator_Base::get_cap_name($this->model(), $this->action() . '_others_default') ] = new EE_Default_Where_Conditions(array(
89
+					// if they don't have the others default cap, they can't access others default items (but they can access
90
+					// their own default items, and non-default items)
91
+					'OR*no_' . EE_Restriction_Generator_Base::get_cap_name($this->model(), $this->action() . '_others_default') => array(
92
+						'AND' => array(
93
+							$this->_path_to_event_model . 'EVT_wp_user' => EE_Default_Where_Conditions::current_user_placeholder,
94
+							$this->_default_field_name => true
95
+							),
96
+						$this->_default_field_name => false
97
+					) ));
98
+		}
99
+		return $restrictions;
100
+	}
101 101
 }
Please login to merge, or discard this patch.
Spacing   +15 added lines, -15 removed lines patch added patch discarded remove patch
@@ -56,7 +56,7 @@  discard block
 block discarded – undo
56 56
     {
57 57
         // if there are no standard caps for this model, then for now all we know is
58 58
         // if they need the default cap to access this
59
-        if (! $this->model()->cap_slug()) {
59
+        if ( ! $this->model()->cap_slug()) {
60 60
             return array(
61 61
                 self::get_default_restrictions_cap() => new EE_Return_None_Where_Conditions()
62 62
             );
@@ -64,33 +64,33 @@  discard block
 block discarded – undo
64 64
 
65 65
         $event_model = EEM_Event::instance();
66 66
 
67
-        $restrictions =  array(
67
+        $restrictions = array(
68 68
             // first: basically access to non-defaults is essentially controlled by which events are accessible
69 69
             // if they don't have the basic event cap, they can't access ANY non-default items
70
-            EE_Restriction_Generator_Base::get_cap_name($event_model, $this->action()) => new EE_Default_Where_Conditions(array( $this->_default_field_name => true )),
70
+            EE_Restriction_Generator_Base::get_cap_name($event_model, $this->action()) => new EE_Default_Where_Conditions(array($this->_default_field_name => true)),
71 71
             // if they don't have the others event cap, they can't access others' non-default items
72
-            EE_Restriction_Generator_Base::get_cap_name($event_model, $this->action() . '_others') => new EE_Default_Where_Conditions(array(
73
-                'OR*no_' . EE_Restriction_Generator_Base::get_cap_name($event_model, $this->action() . '_others') => array(
74
-                    $this->_path_to_event_model . 'EVT_wp_user' => EE_Default_Where_Conditions::current_user_placeholder ),
72
+            EE_Restriction_Generator_Base::get_cap_name($event_model, $this->action().'_others') => new EE_Default_Where_Conditions(array(
73
+                'OR*no_'.EE_Restriction_Generator_Base::get_cap_name($event_model, $this->action().'_others') => array(
74
+                    $this->_path_to_event_model.'EVT_wp_user' => EE_Default_Where_Conditions::current_user_placeholder ),
75 75
                     $this->_default_field_name => true )),
76 76
             // if they have basic and others, but not private, they can't access others' private non-default items
77
-            EE_Restriction_Generator_Base::get_cap_name($event_model, $this->action() . '_private') => new EE_Default_Where_Conditions(array(
78
-                'OR*no_' . EE_Restriction_Generator_Base::get_cap_name($event_model, $this->action() . '_private') => array(
79
-                $this->_path_to_event_model . 'EVT_wp_user' => EE_Default_Where_Conditions::current_user_placeholder,
80
-                $this->_path_to_event_model . 'status' => array( '!=', 'private' ),
77
+            EE_Restriction_Generator_Base::get_cap_name($event_model, $this->action().'_private') => new EE_Default_Where_Conditions(array(
78
+                'OR*no_'.EE_Restriction_Generator_Base::get_cap_name($event_model, $this->action().'_private') => array(
79
+                $this->_path_to_event_model.'EVT_wp_user' => EE_Default_Where_Conditions::current_user_placeholder,
80
+                $this->_path_to_event_model.'status' => array('!=', 'private'),
81 81
                 $this->_default_field_name => true ) )),
82 82
             // second: access to defaults is controlled by the defaulty capabilities
83 83
             // if they don't have the default capability, restrict access to only non-default items
84
-            EE_Restriction_Generator_Base::get_cap_name($this->model(), $this->action() . '_default') => new EE_Default_Where_Conditions(array( $this->_default_field_name => false )),
84
+            EE_Restriction_Generator_Base::get_cap_name($this->model(), $this->action().'_default') => new EE_Default_Where_Conditions(array($this->_default_field_name => false)),
85 85
             // if they don't have the "others" default capability, restrict access to only their default ones, and non-default ones
86 86
              );
87
-        if (EE_Restriction_Generator_Base::is_cap($this->model(), $this->action() . '_others_default')) {
88
-            $restrictions[ EE_Restriction_Generator_Base::get_cap_name($this->model(), $this->action() . '_others_default') ] = new EE_Default_Where_Conditions(array(
87
+        if (EE_Restriction_Generator_Base::is_cap($this->model(), $this->action().'_others_default')) {
88
+            $restrictions[EE_Restriction_Generator_Base::get_cap_name($this->model(), $this->action().'_others_default')] = new EE_Default_Where_Conditions(array(
89 89
                     // if they don't have the others default cap, they can't access others default items (but they can access
90 90
                     // their own default items, and non-default items)
91
-                    'OR*no_' . EE_Restriction_Generator_Base::get_cap_name($this->model(), $this->action() . '_others_default') => array(
91
+                    'OR*no_'.EE_Restriction_Generator_Base::get_cap_name($this->model(), $this->action().'_others_default') => array(
92 92
                         'AND' => array(
93
-                            $this->_path_to_event_model . 'EVT_wp_user' => EE_Default_Where_Conditions::current_user_placeholder,
93
+                            $this->_path_to_event_model.'EVT_wp_user' => EE_Default_Where_Conditions::current_user_placeholder,
94 94
                             $this->_default_field_name => true
95 95
                             ),
96 96
                         $this->_default_field_name => false
Please login to merge, or discard this patch.
core/helpers/EEH_Event_Query.helper.php 2 patches
Spacing   +40 added lines, -40 removed lines patch added patch discarded remove patch
@@ -216,7 +216,7 @@  discard block
 block discarded – undo
216 216
     {
217 217
         if (EEH_Event_Query::apply_query_filters($wp_query)) {
218 218
             global $wpdb;
219
-            $clauses['groupby'] = $wpdb->posts . '.ID ';
219
+            $clauses['groupby'] = $wpdb->posts.'.ID ';
220 220
         }
221 221
         return $clauses;
222 222
     }
@@ -251,23 +251,23 @@  discard block
 block discarded – undo
251 251
      */
252 252
     public static function posts_fields_sql_for_orderby(array $orderby_params = [])
253 253
     {
254
-        $SQL = ', MIN( ' . EEM_Datetime::instance()->table() . '.DTT_EVT_start ) as event_start_date ';
254
+        $SQL = ', MIN( '.EEM_Datetime::instance()->table().'.DTT_EVT_start ) as event_start_date ';
255 255
         foreach ($orderby_params as $orderby) {
256 256
             switch ($orderby) {
257 257
                 case 'ticket_start':
258
-                    $SQL .= ', ' . EEM_Ticket::instance()->table() . '.TKT_start_date';
258
+                    $SQL .= ', '.EEM_Ticket::instance()->table().'.TKT_start_date';
259 259
                     break;
260 260
                 case 'ticket_end':
261
-                    $SQL .= ', ' . EEM_Ticket::instance()->table() . '.TKT_end_date';
261
+                    $SQL .= ', '.EEM_Ticket::instance()->table().'.TKT_end_date';
262 262
                     break;
263 263
                 case 'venue_title':
264 264
                     $SQL .= ', Venue.post_title AS venue_title';
265 265
                     break;
266 266
                 case 'city':
267
-                    $SQL .= ', ' . EEM_Venue::instance()->second_table() . '.VNU_city';
267
+                    $SQL .= ', '.EEM_Venue::instance()->second_table().'.VNU_city';
268 268
                     break;
269 269
                 case 'state':
270
-                    $SQL .= ', ' . EEM_State::instance()->table() . '.STA_name';
270
+                    $SQL .= ', '.EEM_State::instance()->table().'.STA_name';
271 271
                     break;
272 272
             }
273 273
         }
@@ -307,12 +307,12 @@  discard block
 block discarded – undo
307 307
      */
308 308
     public static function posts_join_sql_for_show_expired($SQL = '', $show_expired = false)
309 309
     {
310
-        if (! $show_expired) {
311
-            $join = EEM_Event::instance()->table() . '.ID = ';
312
-            $join .= EEM_Datetime::instance()->table() . '.' . EEM_Event::instance()->primary_key_name();
310
+        if ( ! $show_expired) {
311
+            $join = EEM_Event::instance()->table().'.ID = ';
312
+            $join .= EEM_Datetime::instance()->table().'.'.EEM_Event::instance()->primary_key_name();
313 313
             // don't add if this is already in the SQL
314 314
             if (strpos($SQL, $join) === false) {
315
-                $SQL .= ' INNER JOIN ' . EEM_Datetime::instance()->table() . ' ON ( ' . $join . ' ) ';
315
+                $SQL .= ' INNER JOIN '.EEM_Datetime::instance()->table().' ON ( '.$join.' ) ';
316 316
             }
317 317
         }
318 318
         return $SQL;
@@ -327,7 +327,7 @@  discard block
 block discarded – undo
327 327
      */
328 328
     public static function posts_join_sql_for_terms($SQL = '', $join_terms = '')
329 329
     {
330
-        if (! empty($join_terms)) {
330
+        if ( ! empty($join_terms)) {
331 331
             global $wpdb;
332 332
             $SQL .= " LEFT JOIN $wpdb->term_relationships ON ($wpdb->posts.ID = $wpdb->term_relationships.object_id)";
333 333
             $SQL .= " LEFT JOIN $wpdb->term_taxonomy ON ($wpdb->term_relationships.term_taxonomy_id = $wpdb->term_taxonomy.term_taxonomy_id)";
@@ -356,13 +356,13 @@  discard block
 block discarded – undo
356 356
                 case 'ticket_end':
357 357
                     $SQL .= EEH_Event_Query::_posts_join_for_datetime(
358 358
                         $SQL,
359
-                        EEM_Datetime_Ticket::instance()->table() . '.' . EEM_Datetime::instance()->primary_key_name()
359
+                        EEM_Datetime_Ticket::instance()->table().'.'.EEM_Datetime::instance()->primary_key_name()
360 360
                     );
361
-                    $SQL .= ' LEFT JOIN ' . EEM_Ticket::instance()->table();
361
+                    $SQL .= ' LEFT JOIN '.EEM_Ticket::instance()->table();
362 362
                     $SQL .= ' ON (';
363
-                    $SQL .= EEM_Datetime_Ticket::instance()->table() . '.' . EEM_Ticket::instance()->primary_key_name();
363
+                    $SQL .= EEM_Datetime_Ticket::instance()->table().'.'.EEM_Ticket::instance()->primary_key_name();
364 364
                     $SQL .= ' = ';
365
-                    $SQL .= EEM_Ticket::instance()->table() . '.' . EEM_Ticket::instance()->primary_key_name();
365
+                    $SQL .= EEM_Ticket::instance()->table().'.'.EEM_Ticket::instance()->primary_key_name();
366 366
                     $SQL .= ' )';
367 367
                     break;
368 368
                 case 'venue_title':
@@ -375,7 +375,7 @@  discard block
 block discarded – undo
375 375
                     break;
376 376
                 case 'start_date':
377 377
                 default:
378
-                    $SQL .= EEH_Event_Query::_posts_join_for_datetime($SQL, EEM_Event::instance()->table() . '.ID');
378
+                    $SQL .= EEH_Event_Query::_posts_join_for_datetime($SQL, EEM_Event::instance()->table().'.ID');
379 379
                     break;
380 380
             }
381 381
         }
@@ -394,10 +394,10 @@  discard block
 block discarded – undo
394 394
      */
395 395
     protected static function _posts_join_for_datetime($SQL = '', $join = '')
396 396
     {
397
-        if (! empty($join)) {
398
-            $join .= ' = ' . EEM_Datetime::instance()->table() . '.' . EEM_Event::instance()->primary_key_name();
397
+        if ( ! empty($join)) {
398
+            $join .= ' = '.EEM_Datetime::instance()->table().'.'.EEM_Event::instance()->primary_key_name();
399 399
             if (strpos($SQL, $join) === false) {
400
-                return ' INNER JOIN ' . EEM_Datetime::instance()->table() . ' ON ( ' . $join . ' )';
400
+                return ' INNER JOIN '.EEM_Datetime::instance()->table().' ON ( '.$join.' )';
401 401
             }
402 402
         }
403 403
         return '';
@@ -417,8 +417,8 @@  discard block
 block discarded – undo
417 417
         // Event Venue table name
418 418
         $event_venue_table = EEM_Event_Venue::instance()->table();
419 419
         // generate conditions for:  Event <=> Event Venue  JOIN clause
420
-        $event_to_event_venue_join = EEM_Event::instance()->table() . '.ID = ';
421
-        $event_to_event_venue_join .= $event_venue_table . '.' . EEM_Event::instance()->primary_key_name();
420
+        $event_to_event_venue_join = EEM_Event::instance()->table().'.ID = ';
421
+        $event_to_event_venue_join .= $event_venue_table.'.'.EEM_Event::instance()->primary_key_name();
422 422
         // don't add joins if they have already been added
423 423
         if (strpos($SQL, $event_to_event_venue_join) === false) {
424 424
             // Venue table name
@@ -506,7 +506,7 @@  discard block
 block discarded – undo
506 506
     public static function posts_where_sql_for_show_expired($show_expired = false)
507 507
     {
508 508
         return ! $show_expired
509
-            ? ' AND ' . EEM_Datetime::instance()->table() . '.DTT_EVT_end > \'' . current_time('mysql', true) . '\' '
509
+            ? ' AND '.EEM_Datetime::instance()->table().'.DTT_EVT_end > \''.current_time('mysql', true).'\' '
510 510
             : '';
511 511
     }
512 512
 
@@ -518,7 +518,7 @@  discard block
 block discarded – undo
518 518
     public static function posts_where_sql_for_event_category_slug($event_category_slug = null)
519 519
     {
520 520
         global $wpdb;
521
-        if (! empty($event_category_slug)) {
521
+        if ( ! empty($event_category_slug)) {
522 522
             $event_category_slugs_array   = array_map('trim', explode(',', $event_category_slug));
523 523
             $event_category_slugs_prepare = implode(', ', array_fill(0, count($event_category_slugs_array), '%s'));
524 524
             return $wpdb->prepare(
@@ -541,14 +541,14 @@  discard block
 block discarded – undo
541 541
     public static function posts_where_sql_for_event_list_month($month = null)
542 542
     {
543 543
         $SQL = '';
544
-        if (! empty($month)) {
544
+        if ( ! empty($month)) {
545 545
             $datetime_table = EEM_Datetime::instance()->table();
546 546
             // event start date is LESS than the end of the month ( so nothing that doesn't start until next month )
547 547
             $SQL = " AND {$datetime_table}.DTT_EVT_start <= '";
548
-            $SQL .= date('Y-m-t 23:59:59', EEH_DTT_Helper::first_of_month_timestamp($month)) . "'";
548
+            $SQL .= date('Y-m-t 23:59:59', EEH_DTT_Helper::first_of_month_timestamp($month))."'";
549 549
             // event end date is GREATER than the start of the month ( so nothing that ended before this month )
550 550
             $SQL .= " AND {$datetime_table}.DTT_EVT_end >= '";
551
-            $SQL .= date('Y-m-01 0:0:00', EEH_DTT_Helper::first_of_month_timestamp($month)) . "' ";
551
+            $SQL .= date('Y-m-01 0:0:00', EEH_DTT_Helper::first_of_month_timestamp($month))."' ";
552 552
         }
553 553
         return $SQL;
554 554
     }
@@ -609,15 +609,15 @@  discard block
 block discarded – undo
609 609
             ? strtoupper($sort)
610 610
             : 'ASC';
611 611
         // make sure 'orderby' is set in query params
612
-        if (! isset(self::$_query_params['orderby'])) {
612
+        if ( ! isset(self::$_query_params['orderby'])) {
613 613
             self::$_query_params['orderby'] = [];
614 614
         }
615 615
         // loop thru $orderby_params (type cast as array)
616 616
         foreach ($orderby_params as $orderby) {
617 617
             // check if we have already added this param
618
-            if (isset(self::$_query_params['orderby'][ $orderby ])) {
618
+            if (isset(self::$_query_params['orderby'][$orderby])) {
619 619
                 // if so then remove from the $orderby_params so that the count() method below is accurate
620
-                unset($orderby_params[ $orderby ]);
620
+                unset($orderby_params[$orderby]);
621 621
                 // then bump ahead to the next param
622 622
                 continue;
623 623
             }
@@ -627,39 +627,39 @@  discard block
 block discarded – undo
627 627
             switch ($orderby) {
628 628
                 case 'id':
629 629
                 case 'ID':
630
-                    $SQL .= $glue . $wpdb->posts . '.ID ' . $sort;
630
+                    $SQL .= $glue.$wpdb->posts.'.ID '.$sort;
631 631
                     break;
632 632
                 case 'end_date':
633
-                    $SQL .= $glue . EEM_Datetime::instance()->table() . '.DTT_EVT_end ' . $sort;
633
+                    $SQL .= $glue.EEM_Datetime::instance()->table().'.DTT_EVT_end '.$sort;
634 634
                     break;
635 635
                 case 'event_name':
636
-                    $SQL .= $glue . $wpdb->posts . '.post_title ' . $sort;
636
+                    $SQL .= $glue.$wpdb->posts.'.post_title '.$sort;
637 637
                     break;
638 638
                 case 'category_slug':
639
-                    $SQL .= $glue . $wpdb->terms . '.slug ' . $sort;
639
+                    $SQL .= $glue.$wpdb->terms.'.slug '.$sort;
640 640
                     break;
641 641
                 case 'ticket_start':
642
-                    $SQL .= $glue . EEM_Ticket::instance()->table() . '.TKT_start_date ' . $sort;
642
+                    $SQL .= $glue.EEM_Ticket::instance()->table().'.TKT_start_date '.$sort;
643 643
                     break;
644 644
                 case 'ticket_end':
645
-                    $SQL .= $glue . EEM_Ticket::instance()->table() . '.TKT_end_date ' . $sort;
645
+                    $SQL .= $glue.EEM_Ticket::instance()->table().'.TKT_end_date '.$sort;
646 646
                     break;
647 647
                 case 'venue_title':
648
-                    $SQL .= $glue . 'venue_title ' . $sort;
648
+                    $SQL .= $glue.'venue_title '.$sort;
649 649
                     break;
650 650
                 case 'city':
651
-                    $SQL .= $glue . EEM_Venue::instance()->second_table() . '.VNU_city ' . $sort;
651
+                    $SQL .= $glue.EEM_Venue::instance()->second_table().'.VNU_city '.$sort;
652 652
                     break;
653 653
                 case 'state':
654
-                    $SQL .= $glue . EEM_State::instance()->table() . '.STA_name ' . $sort;
654
+                    $SQL .= $glue.EEM_State::instance()->table().'.STA_name '.$sort;
655 655
                     break;
656 656
                 case 'start_date':
657 657
                 default:
658
-                    $SQL .= $glue . ' event_start_date ' . $sort;
658
+                    $SQL .= $glue.' event_start_date '.$sort;
659 659
                     break;
660 660
             }
661 661
             // add to array of orderby params that have been added
662
-            self::$_query_params['orderby'][ $orderby ] = true;
662
+            self::$_query_params['orderby'][$orderby] = true;
663 663
             $counter++;
664 664
         }
665 665
         return $SQL;
Please login to merge, or discard this patch.
Indentation   +662 added lines, -662 removed lines patch added patch discarded remove patch
@@ -17,666 +17,666 @@
 block discarded – undo
17 17
  */
18 18
 class EEH_Event_Query
19 19
 {
20
-    /**
21
-     * Start Date
22
-     *
23
-     * @var $_event_query_month
24
-     */
25
-    protected static $_event_query_month;
26
-
27
-    /**
28
-     * Category
29
-     *
30
-     * @var $_event_query_category
31
-     */
32
-    protected static $_event_query_category;
33
-
34
-    /**
35
-     * whether to display expired events in the event list
36
-     *
37
-     * @var bool $_show_expired
38
-     */
39
-    protected static $_event_query_show_expired = false;
40
-
41
-    /**
42
-     * list of params for controlling how the query results are ordered
43
-     *
44
-     * @var array $_event_query_orderby
45
-     */
46
-    protected static $_event_query_orderby = [];
47
-
48
-    /**
49
-     * direction list is sorted
50
-     *
51
-     * @var string $_event_query_sort
52
-     */
53
-    protected static $_event_query_sort;
54
-
55
-    /**
56
-     * list of params used to build the query's various clauses
57
-     *
58
-     * @var $_query_params
59
-     */
60
-    protected static $_query_params = [];
61
-
62
-
63
-    /**
64
-     * @return void
65
-     */
66
-    public static function add_query_filters()
67
-    {
68
-        // add query filters
69
-        add_action('pre_get_posts', ['EEH_Event_Query', 'filter_query_parts'], 10, 1);
70
-    }
71
-
72
-
73
-    /**
74
-     * @param WP_Query $WP_Query
75
-     * @return bool
76
-     */
77
-    public static function apply_query_filters(WP_Query $WP_Query)
78
-    {
79
-        return (
80
-                   isset($WP_Query->query['post_type'])
81
-                   && $WP_Query->query['post_type'] === 'espresso_events'
82
-               )
83
-               || apply_filters('FHEE__EEH_Event_Query__apply_query_filters', false);
84
-    }
85
-
86
-
87
-    /**
88
-     * @param WP_Query $WP_Query
89
-     */
90
-    public static function filter_query_parts(WP_Query $WP_Query)
91
-    {
92
-        // ONLY add our filters if this isn't the main wp_query,
93
-        // because if this is the main wp_query we already have
94
-        // our cpt strategies take care of adding things in.
95
-        if ($WP_Query instanceof WP_Query && ! $WP_Query->is_main_query()) {
96
-            // build event list query
97
-            add_filter('posts_fields', ['EEH_Event_Query', 'posts_fields'], 10, 2);
98
-            add_filter('posts_join', ['EEH_Event_Query', 'posts_join'], 10, 2);
99
-            add_filter('posts_where', ['EEH_Event_Query', 'posts_where'], 10, 2);
100
-            add_filter('posts_orderby', ['EEH_Event_Query', 'posts_orderby'], 10, 2);
101
-            add_filter('posts_clauses_request', ['EEH_Event_Query', 'posts_clauses'], 10, 2);
102
-        }
103
-    }
104
-
105
-
106
-    /**
107
-     * @param string $month
108
-     * @param string $category
109
-     * @param bool   $show_expired
110
-     * @param array|string $orderby
111
-     * @param string $sort
112
-     * @throws InvalidArgumentException
113
-     * @throws InvalidDataTypeException
114
-     * @throws InvalidInterfaceException
115
-     */
116
-    public static function set_query_params(
117
-        $month = '',
118
-        $category = '',
119
-        $show_expired = false,
120
-        $orderby = 'start_date',
121
-        $sort = 'ASC'
122
-    ) {
123
-        self::$_query_params                        = [];
124
-        EEH_Event_Query::$_event_query_month        = EEH_Event_Query::_display_month($month);
125
-        EEH_Event_Query::$_event_query_category     = EEH_Event_Query::_event_category_slug($category);
126
-        EEH_Event_Query::$_event_query_show_expired = EEH_Event_Query::_show_expired($show_expired);
127
-        EEH_Event_Query::$_event_query_orderby      = EEH_Event_Query::_orderby($orderby);
128
-        EEH_Event_Query::$_event_query_sort         = EEH_Event_Query::_sort($sort);
129
-    }
130
-
131
-
132
-    /**
133
-     * what month should the event list display events for?
134
-     *
135
-     * @param string $month
136
-     * @return string
137
-     * @throws InvalidArgumentException
138
-     * @throws InvalidDataTypeException
139
-     * @throws InvalidInterfaceException
140
-     */
141
-    private static function _display_month($month = '')
142
-    {
143
-        return self::getRequest()->getRequestParam('event_query_month', $month);
144
-    }
145
-
146
-
147
-    /**
148
-     * @param string $category
149
-     * @return string
150
-     * @throws InvalidArgumentException
151
-     * @throws InvalidDataTypeException
152
-     * @throws InvalidInterfaceException
153
-     */
154
-    private static function _event_category_slug($category = '')
155
-    {
156
-        return self::getRequest()->getRequestParam('event_query_category', $category);
157
-    }
158
-
159
-
160
-    /**
161
-     * @param bool $show_expired
162
-     * @return bool
163
-     * @throws InvalidArgumentException
164
-     * @throws InvalidDataTypeException
165
-     * @throws InvalidInterfaceException
166
-     */
167
-    private static function _show_expired($show_expired = false)
168
-    {
169
-        // override default expired option if set via filter
170
-        return self::getRequest()->getRequestParam('event_query_show_expired', $show_expired, 'bool');
171
-    }
172
-
173
-
174
-    /**
175
-     * @param array|string $orderby
176
-     * @return array
177
-     * @throws InvalidArgumentException
178
-     * @throws InvalidDataTypeException
179
-     * @throws InvalidInterfaceException
180
-     */
181
-    private static function _orderby($orderby = 'start_date')
182
-    {
183
-        $event_query_orderby = self::getRequest()->getRequestParam(
184
-            'event_query_orderby',
185
-            (array) $orderby,
186
-            DataType::STRING,
187
-            true
188
-        );
189
-        $event_query_orderby = is_array($event_query_orderby)
190
-            ? $event_query_orderby
191
-            : explode(',', $event_query_orderby);
192
-        $event_query_orderby = array_map('trim', $event_query_orderby);
193
-        return array_map('sanitize_text_field', $event_query_orderby);
194
-    }
195
-
196
-
197
-    /**
198
-     * @param string $sort
199
-     * @return string
200
-     * @throws InvalidArgumentException
201
-     * @throws InvalidDataTypeException
202
-     * @throws InvalidInterfaceException
203
-     */
204
-    private static function _sort($sort = 'ASC')
205
-    {
206
-        $sort = self::getRequest()->getRequestParam('event_query_sort', $sort);
207
-        return in_array($sort, ['ASC', 'asc', 'DESC', 'desc'], true)
208
-            ? strtoupper($sort)
209
-            : 'ASC';
210
-    }
211
-
212
-
213
-    /**
214
-     * Filters the clauses for the WP_Query object
215
-     *
216
-     * @param array    $clauses array of clauses
217
-     * @param WP_Query $wp_query
218
-     * @return array   array of clauses
219
-     */
220
-    public static function posts_clauses($clauses, WP_Query $wp_query)
221
-    {
222
-        if (EEH_Event_Query::apply_query_filters($wp_query)) {
223
-            global $wpdb;
224
-            $clauses['groupby'] = $wpdb->posts . '.ID ';
225
-        }
226
-        return $clauses;
227
-    }
228
-
229
-
230
-    /**
231
-     * @param string   $SQL
232
-     * @param WP_Query $wp_query
233
-     * @return string
234
-     * @throws EE_Error
235
-     * @throws InvalidArgumentException
236
-     * @throws InvalidDataTypeException
237
-     * @throws InvalidInterfaceException
238
-     */
239
-    public static function posts_fields($SQL, WP_Query $wp_query)
240
-    {
241
-        if (EEH_Event_Query::apply_query_filters($wp_query)) {
242
-            // adds something like ", wp_esp_datetime.* " to WP Query SELECT statement
243
-            $SQL .= EEH_Event_Query::posts_fields_sql_for_orderby(EEH_Event_Query::$_event_query_orderby);
244
-        }
245
-        return $SQL;
246
-    }
247
-
248
-
249
-    /**
250
-     * @param array $orderby_params
251
-     * @return string
252
-     * @throws EE_Error
253
-     * @throws InvalidArgumentException
254
-     * @throws InvalidDataTypeException
255
-     * @throws InvalidInterfaceException
256
-     */
257
-    public static function posts_fields_sql_for_orderby(array $orderby_params = [])
258
-    {
259
-        $SQL = ', MIN( ' . EEM_Datetime::instance()->table() . '.DTT_EVT_start ) as event_start_date ';
260
-        foreach ($orderby_params as $orderby) {
261
-            switch ($orderby) {
262
-                case 'ticket_start':
263
-                    $SQL .= ', ' . EEM_Ticket::instance()->table() . '.TKT_start_date';
264
-                    break;
265
-                case 'ticket_end':
266
-                    $SQL .= ', ' . EEM_Ticket::instance()->table() . '.TKT_end_date';
267
-                    break;
268
-                case 'venue_title':
269
-                    $SQL .= ', Venue.post_title AS venue_title';
270
-                    break;
271
-                case 'city':
272
-                    $SQL .= ', ' . EEM_Venue::instance()->second_table() . '.VNU_city';
273
-                    break;
274
-                case 'state':
275
-                    $SQL .= ', ' . EEM_State::instance()->table() . '.STA_name';
276
-                    break;
277
-            }
278
-        }
279
-        return $SQL;
280
-    }
281
-
282
-
283
-    /**
284
-     * @param string   $SQL
285
-     * @param WP_Query $wp_query
286
-     * @return string
287
-     * @throws EE_Error
288
-     * @throws InvalidArgumentException
289
-     * @throws InvalidDataTypeException
290
-     * @throws InvalidInterfaceException
291
-     */
292
-    public static function posts_join($SQL, WP_Query $wp_query)
293
-    {
294
-        if (EEH_Event_Query::apply_query_filters($wp_query)) {
295
-            // Category
296
-            $SQL = EEH_Event_Query::posts_join_sql_for_show_expired($SQL, EEH_Event_Query::$_event_query_show_expired);
297
-            $SQL = EEH_Event_Query::posts_join_sql_for_terms($SQL, EEH_Event_Query::$_event_query_category);
298
-            $SQL = EEH_Event_Query::posts_join_for_orderby($SQL, EEH_Event_Query::$_event_query_orderby);
299
-        }
300
-        return $SQL;
301
-    }
302
-
303
-
304
-    /**
305
-     * @param string  $SQL
306
-     * @param boolean $show_expired if TRUE, then displayed past events
307
-     * @return string
308
-     * @throws EE_Error
309
-     * @throws InvalidArgumentException
310
-     * @throws InvalidDataTypeException
311
-     * @throws InvalidInterfaceException
312
-     */
313
-    public static function posts_join_sql_for_show_expired($SQL = '', $show_expired = false)
314
-    {
315
-        if (! $show_expired) {
316
-            $join = EEM_Event::instance()->table() . '.ID = ';
317
-            $join .= EEM_Datetime::instance()->table() . '.' . EEM_Event::instance()->primary_key_name();
318
-            // don't add if this is already in the SQL
319
-            if (strpos($SQL, $join) === false) {
320
-                $SQL .= ' INNER JOIN ' . EEM_Datetime::instance()->table() . ' ON ( ' . $join . ' ) ';
321
-            }
322
-        }
323
-        return $SQL;
324
-    }
325
-
326
-
327
-    /**
328
-     * @param string $SQL
329
-     * @param string $join_terms    pass TRUE or term string, doesn't really matter since this value doesn't really get
330
-     *                              used for anything yet
331
-     * @return string
332
-     */
333
-    public static function posts_join_sql_for_terms($SQL = '', $join_terms = '')
334
-    {
335
-        if (! empty($join_terms)) {
336
-            global $wpdb;
337
-            $SQL .= " LEFT JOIN $wpdb->term_relationships ON ($wpdb->posts.ID = $wpdb->term_relationships.object_id)";
338
-            $SQL .= " LEFT JOIN $wpdb->term_taxonomy ON ($wpdb->term_relationships.term_taxonomy_id = $wpdb->term_taxonomy.term_taxonomy_id)";
339
-            $SQL .= " LEFT JOIN $wpdb->terms ON ($wpdb->terms.term_id = $wpdb->term_taxonomy.term_id) ";
340
-        }
341
-        return $SQL;
342
-    }
343
-
344
-
345
-    /**
346
-     * usage:  $SQL .= EEH_Event_Query::posts_join_for_orderby( $orderby_params );
347
-     *
348
-     * @param string $SQL
349
-     * @param array  $orderby_params
350
-     * @return string
351
-     * @throws EE_Error
352
-     * @throws InvalidArgumentException
353
-     * @throws InvalidDataTypeException
354
-     * @throws InvalidInterfaceException
355
-     */
356
-    public static function posts_join_for_orderby($SQL = '', array $orderby_params = [])
357
-    {
358
-        foreach ($orderby_params as $orderby) {
359
-            switch ($orderby) {
360
-                case 'ticket_start':
361
-                case 'ticket_end':
362
-                    $SQL .= EEH_Event_Query::_posts_join_for_datetime(
363
-                        $SQL,
364
-                        EEM_Datetime_Ticket::instance()->table() . '.' . EEM_Datetime::instance()->primary_key_name()
365
-                    );
366
-                    $SQL .= ' LEFT JOIN ' . EEM_Ticket::instance()->table();
367
-                    $SQL .= ' ON (';
368
-                    $SQL .= EEM_Datetime_Ticket::instance()->table() . '.' . EEM_Ticket::instance()->primary_key_name();
369
-                    $SQL .= ' = ';
370
-                    $SQL .= EEM_Ticket::instance()->table() . '.' . EEM_Ticket::instance()->primary_key_name();
371
-                    $SQL .= ' )';
372
-                    break;
373
-                case 'venue_title':
374
-                case 'city':
375
-                    $SQL .= EEH_Event_Query::_posts_join_for_event_venue($SQL);
376
-                    break;
377
-                case 'state':
378
-                    $SQL .= EEH_Event_Query::_posts_join_for_event_venue($SQL);
379
-                    $SQL .= EEH_Event_Query::_posts_join_for_venue_state($SQL);
380
-                    break;
381
-                case 'start_date':
382
-                default:
383
-                    $SQL .= EEH_Event_Query::_posts_join_for_datetime($SQL, EEM_Event::instance()->table() . '.ID');
384
-                    break;
385
-            }
386
-        }
387
-        return $SQL;
388
-    }
389
-
390
-
391
-    /**
392
-     * @param string $SQL
393
-     * @param string $join
394
-     * @return string
395
-     * @throws EE_Error
396
-     * @throws InvalidArgumentException
397
-     * @throws InvalidDataTypeException
398
-     * @throws InvalidInterfaceException
399
-     */
400
-    protected static function _posts_join_for_datetime($SQL = '', $join = '')
401
-    {
402
-        if (! empty($join)) {
403
-            $join .= ' = ' . EEM_Datetime::instance()->table() . '.' . EEM_Event::instance()->primary_key_name();
404
-            if (strpos($SQL, $join) === false) {
405
-                return ' INNER JOIN ' . EEM_Datetime::instance()->table() . ' ON ( ' . $join . ' )';
406
-            }
407
-        }
408
-        return '';
409
-    }
410
-
411
-
412
-    /**
413
-     * @param string $SQL
414
-     * @return string
415
-     * @throws EE_Error
416
-     * @throws InvalidArgumentException
417
-     * @throws InvalidDataTypeException
418
-     * @throws InvalidInterfaceException
419
-     */
420
-    protected static function _posts_join_for_event_venue($SQL = '')
421
-    {
422
-        // Event Venue table name
423
-        $event_venue_table = EEM_Event_Venue::instance()->table();
424
-        // generate conditions for:  Event <=> Event Venue  JOIN clause
425
-        $event_to_event_venue_join = EEM_Event::instance()->table() . '.ID = ';
426
-        $event_to_event_venue_join .= $event_venue_table . '.' . EEM_Event::instance()->primary_key_name();
427
-        // don't add joins if they have already been added
428
-        if (strpos($SQL, $event_to_event_venue_join) === false) {
429
-            // Venue table name
430
-            $venue_table = EEM_Venue::instance()->table();
431
-            // Venue table pk
432
-            $venue_table_pk = EEM_Venue::instance()->primary_key_name();
433
-            // Venue Meta table name
434
-            $venue_meta_table = EEM_Venue::instance()->second_table();
435
-            // generate JOIN clause for: Event <=> Event Venue
436
-            $venue_SQL = " LEFT JOIN $event_venue_table ON ( $event_to_event_venue_join )";
437
-            // generate JOIN clause for: Event Venue <=> Venue
438
-            $venue_SQL .= " LEFT JOIN $venue_table as Venue ON ( $event_venue_table.$venue_table_pk = Venue.ID )";
439
-            // generate JOIN clause for: Venue <=> Venue Meta
440
-            $venue_SQL .= " LEFT JOIN $venue_meta_table ON ( Venue.ID = $venue_meta_table.$venue_table_pk )";
441
-            unset($event_venue_table, $event_to_event_venue_join, $venue_table, $venue_table_pk, $venue_meta_table);
442
-            return $venue_SQL;
443
-        }
444
-        unset($event_venue_table, $event_to_event_venue_join);
445
-        return '';
446
-    }
447
-
448
-
449
-    /**
450
-     * @param string $SQL
451
-     * @return string
452
-     * @throws EE_Error
453
-     * @throws InvalidArgumentException
454
-     * @throws InvalidDataTypeException
455
-     * @throws InvalidInterfaceException
456
-     */
457
-    protected static function _posts_join_for_venue_state($SQL = '')
458
-    {
459
-        // Venue Meta table name
460
-        $venue_meta_table = EEM_Venue::instance()->second_table();
461
-        // State table name
462
-        $state_table = EEM_State::instance()->table();
463
-        // State table pk
464
-        $state_table_pk = EEM_State::instance()->primary_key_name();
465
-        // verify vars
466
-        if ($venue_meta_table && $state_table && $state_table_pk) {
467
-            // like: wp_esp_venue_meta.STA_ID = wp_esp_state.STA_ID
468
-            $join = "$venue_meta_table.$state_table_pk = $state_table.$state_table_pk";
469
-            // don't add join if it has already been added
470
-            if (strpos($SQL, $join) === false) {
471
-                unset($state_table_pk, $venue_meta_table, $venue_table_pk);
472
-                return " LEFT JOIN $state_table ON ( $join )";
473
-            }
474
-        }
475
-        unset($join, $state_table, $state_table_pk, $venue_meta_table, $venue_table_pk);
476
-        return '';
477
-    }
478
-
479
-
480
-    /**
481
-     * @param string   $SQL
482
-     * @param WP_Query $wp_query
483
-     * @return string
484
-     * @throws EE_Error
485
-     * @throws InvalidArgumentException
486
-     * @throws InvalidDataTypeException
487
-     * @throws InvalidInterfaceException
488
-     */
489
-    public static function posts_where($SQL, WP_Query $wp_query)
490
-    {
491
-        if (EEH_Event_Query::apply_query_filters($wp_query)) {
492
-            // Show Expired ?
493
-            $SQL .= EEH_Event_Query::posts_where_sql_for_show_expired(EEH_Event_Query::$_event_query_show_expired);
494
-            // Category
495
-            $SQL .= EEH_Event_Query::posts_where_sql_for_event_category_slug(EEH_Event_Query::$_event_query_category);
496
-            // Start Date
497
-            $SQL .= EEH_Event_Query::posts_where_sql_for_event_list_month(EEH_Event_Query::$_event_query_month);
498
-        }
499
-        return $SQL;
500
-    }
501
-
502
-
503
-    /**
504
-     * @param boolean $show_expired if TRUE, then displayed past events
505
-     * @return string
506
-     * @throws EE_Error
507
-     * @throws InvalidArgumentException
508
-     * @throws InvalidDataTypeException
509
-     * @throws InvalidInterfaceException
510
-     */
511
-    public static function posts_where_sql_for_show_expired($show_expired = false)
512
-    {
513
-        return ! $show_expired
514
-            ? ' AND ' . EEM_Datetime::instance()->table() . '.DTT_EVT_end > \'' . current_time('mysql', true) . '\' '
515
-            : '';
516
-    }
517
-
518
-
519
-    /**
520
-     * @param boolean $event_category_slug
521
-     * @return string
522
-     */
523
-    public static function posts_where_sql_for_event_category_slug($event_category_slug = null)
524
-    {
525
-        global $wpdb;
526
-        if (! empty($event_category_slug)) {
527
-            $event_category_slugs_array   = array_map('trim', explode(',', $event_category_slug));
528
-            $event_category_slugs_prepare = implode(', ', array_fill(0, count($event_category_slugs_array), '%s'));
529
-            return $wpdb->prepare(
530
-                " AND {$wpdb->terms}.slug IN ({$event_category_slugs_prepare}) ",
531
-                $event_category_slugs_array
532
-            );
533
-        }
534
-        return '';
535
-    }
536
-
537
-
538
-    /**
539
-     * @param boolean $month
540
-     * @return string
541
-     * @throws EE_Error
542
-     * @throws InvalidArgumentException
543
-     * @throws InvalidDataTypeException
544
-     * @throws InvalidInterfaceException
545
-     */
546
-    public static function posts_where_sql_for_event_list_month($month = null)
547
-    {
548
-        $SQL = '';
549
-        if (! empty($month)) {
550
-            $datetime_table = EEM_Datetime::instance()->table();
551
-            // event start date is LESS than the end of the month ( so nothing that doesn't start until next month )
552
-            $SQL = " AND {$datetime_table}.DTT_EVT_start <= '";
553
-            $SQL .= date('Y-m-t 23:59:59', EEH_DTT_Helper::first_of_month_timestamp($month)) . "'";
554
-            // event end date is GREATER than the start of the month ( so nothing that ended before this month )
555
-            $SQL .= " AND {$datetime_table}.DTT_EVT_end >= '";
556
-            $SQL .= date('Y-m-01 0:0:00', EEH_DTT_Helper::first_of_month_timestamp($month)) . "' ";
557
-        }
558
-        return $SQL;
559
-    }
560
-
561
-
562
-    /**
563
-     * @param string   $SQL
564
-     * @param WP_Query $wp_query
565
-     * @return string
566
-     * @throws EE_Error
567
-     * @throws InvalidArgumentException
568
-     * @throws InvalidDataTypeException
569
-     * @throws InvalidInterfaceException
570
-     */
571
-    public static function posts_orderby($SQL, WP_Query $wp_query)
572
-    {
573
-        if (EEH_Event_Query::apply_query_filters($wp_query)) {
574
-            $SQL = EEH_Event_Query::posts_orderby_sql(
575
-                EEH_Event_Query::$_event_query_orderby,
576
-                EEH_Event_Query::$_event_query_sort
577
-            );
578
-        }
579
-        return $SQL;
580
-    }
581
-
582
-
583
-    /**
584
-     *    posts_orderby_sql
585
-     *    possible parameters:
586
-     *    ID
587
-     *    start_date
588
-     *    end_date
589
-     *    event_name
590
-     *    category_slug
591
-     *    ticket_start
592
-     *    ticket_end
593
-     *    venue_title
594
-     *    city
595
-     *    state
596
-     *    **IMPORTANT**
597
-     *    make sure to also send the $orderby_params array to the posts_join_for_orderby() method
598
-     *    or else some of the table references below will result in MySQL errors
599
-     *
600
-     * @param array  $orderby_params
601
-     * @param string $sort
602
-     * @return string
603
-     * @throws EE_Error
604
-     * @throws InvalidArgumentException
605
-     * @throws InvalidDataTypeException
606
-     * @throws InvalidInterfaceException
607
-     */
608
-    public static function posts_orderby_sql(array $orderby_params = [], $sort = 'ASC')
609
-    {
610
-        global $wpdb;
611
-        $SQL     = '';
612
-        $counter = 0;
613
-        $sort    = in_array($sort, ['ASC', 'asc', 'DESC', 'desc'], true)
614
-            ? strtoupper($sort)
615
-            : 'ASC';
616
-        // make sure 'orderby' is set in query params
617
-        if (! isset(self::$_query_params['orderby'])) {
618
-            self::$_query_params['orderby'] = [];
619
-        }
620
-        // loop thru $orderby_params (type cast as array)
621
-        foreach ($orderby_params as $orderby) {
622
-            // check if we have already added this param
623
-            if (isset(self::$_query_params['orderby'][ $orderby ])) {
624
-                // if so then remove from the $orderby_params so that the count() method below is accurate
625
-                unset($orderby_params[ $orderby ]);
626
-                // then bump ahead to the next param
627
-                continue;
628
-            }
629
-            // this will ad a comma depending on whether this is the first or last param
630
-            $glue = $counter === 0 || $counter === count($orderby_params) ? ' ' : ', ';
631
-            // ok what's we dealing with?
632
-            switch ($orderby) {
633
-                case 'id':
634
-                case 'ID':
635
-                    $SQL .= $glue . $wpdb->posts . '.ID ' . $sort;
636
-                    break;
637
-                case 'end_date':
638
-                    $SQL .= $glue . EEM_Datetime::instance()->table() . '.DTT_EVT_end ' . $sort;
639
-                    break;
640
-                case 'event_name':
641
-                    $SQL .= $glue . $wpdb->posts . '.post_title ' . $sort;
642
-                    break;
643
-                case 'category_slug':
644
-                    $SQL .= $glue . $wpdb->terms . '.slug ' . $sort;
645
-                    break;
646
-                case 'ticket_start':
647
-                    $SQL .= $glue . EEM_Ticket::instance()->table() . '.TKT_start_date ' . $sort;
648
-                    break;
649
-                case 'ticket_end':
650
-                    $SQL .= $glue . EEM_Ticket::instance()->table() . '.TKT_end_date ' . $sort;
651
-                    break;
652
-                case 'venue_title':
653
-                    $SQL .= $glue . 'venue_title ' . $sort;
654
-                    break;
655
-                case 'city':
656
-                    $SQL .= $glue . EEM_Venue::instance()->second_table() . '.VNU_city ' . $sort;
657
-                    break;
658
-                case 'state':
659
-                    $SQL .= $glue . EEM_State::instance()->table() . '.STA_name ' . $sort;
660
-                    break;
661
-                case 'start_date':
662
-                default:
663
-                    $SQL .= $glue . ' event_start_date ' . $sort;
664
-                    break;
665
-            }
666
-            // add to array of orderby params that have been added
667
-            self::$_query_params['orderby'][ $orderby ] = true;
668
-            $counter++;
669
-        }
670
-        return $SQL;
671
-    }
672
-
673
-
674
-    /**
675
-     * @return RequestInterface
676
-     * @since   4.10.14.p
677
-     */
678
-    private static function getRequest()
679
-    {
680
-        return LoaderFactory::getLoader()->getShared(RequestInterface::class);
681
-    }
20
+	/**
21
+	 * Start Date
22
+	 *
23
+	 * @var $_event_query_month
24
+	 */
25
+	protected static $_event_query_month;
26
+
27
+	/**
28
+	 * Category
29
+	 *
30
+	 * @var $_event_query_category
31
+	 */
32
+	protected static $_event_query_category;
33
+
34
+	/**
35
+	 * whether to display expired events in the event list
36
+	 *
37
+	 * @var bool $_show_expired
38
+	 */
39
+	protected static $_event_query_show_expired = false;
40
+
41
+	/**
42
+	 * list of params for controlling how the query results are ordered
43
+	 *
44
+	 * @var array $_event_query_orderby
45
+	 */
46
+	protected static $_event_query_orderby = [];
47
+
48
+	/**
49
+	 * direction list is sorted
50
+	 *
51
+	 * @var string $_event_query_sort
52
+	 */
53
+	protected static $_event_query_sort;
54
+
55
+	/**
56
+	 * list of params used to build the query's various clauses
57
+	 *
58
+	 * @var $_query_params
59
+	 */
60
+	protected static $_query_params = [];
61
+
62
+
63
+	/**
64
+	 * @return void
65
+	 */
66
+	public static function add_query_filters()
67
+	{
68
+		// add query filters
69
+		add_action('pre_get_posts', ['EEH_Event_Query', 'filter_query_parts'], 10, 1);
70
+	}
71
+
72
+
73
+	/**
74
+	 * @param WP_Query $WP_Query
75
+	 * @return bool
76
+	 */
77
+	public static function apply_query_filters(WP_Query $WP_Query)
78
+	{
79
+		return (
80
+				   isset($WP_Query->query['post_type'])
81
+				   && $WP_Query->query['post_type'] === 'espresso_events'
82
+			   )
83
+			   || apply_filters('FHEE__EEH_Event_Query__apply_query_filters', false);
84
+	}
85
+
86
+
87
+	/**
88
+	 * @param WP_Query $WP_Query
89
+	 */
90
+	public static function filter_query_parts(WP_Query $WP_Query)
91
+	{
92
+		// ONLY add our filters if this isn't the main wp_query,
93
+		// because if this is the main wp_query we already have
94
+		// our cpt strategies take care of adding things in.
95
+		if ($WP_Query instanceof WP_Query && ! $WP_Query->is_main_query()) {
96
+			// build event list query
97
+			add_filter('posts_fields', ['EEH_Event_Query', 'posts_fields'], 10, 2);
98
+			add_filter('posts_join', ['EEH_Event_Query', 'posts_join'], 10, 2);
99
+			add_filter('posts_where', ['EEH_Event_Query', 'posts_where'], 10, 2);
100
+			add_filter('posts_orderby', ['EEH_Event_Query', 'posts_orderby'], 10, 2);
101
+			add_filter('posts_clauses_request', ['EEH_Event_Query', 'posts_clauses'], 10, 2);
102
+		}
103
+	}
104
+
105
+
106
+	/**
107
+	 * @param string $month
108
+	 * @param string $category
109
+	 * @param bool   $show_expired
110
+	 * @param array|string $orderby
111
+	 * @param string $sort
112
+	 * @throws InvalidArgumentException
113
+	 * @throws InvalidDataTypeException
114
+	 * @throws InvalidInterfaceException
115
+	 */
116
+	public static function set_query_params(
117
+		$month = '',
118
+		$category = '',
119
+		$show_expired = false,
120
+		$orderby = 'start_date',
121
+		$sort = 'ASC'
122
+	) {
123
+		self::$_query_params                        = [];
124
+		EEH_Event_Query::$_event_query_month        = EEH_Event_Query::_display_month($month);
125
+		EEH_Event_Query::$_event_query_category     = EEH_Event_Query::_event_category_slug($category);
126
+		EEH_Event_Query::$_event_query_show_expired = EEH_Event_Query::_show_expired($show_expired);
127
+		EEH_Event_Query::$_event_query_orderby      = EEH_Event_Query::_orderby($orderby);
128
+		EEH_Event_Query::$_event_query_sort         = EEH_Event_Query::_sort($sort);
129
+	}
130
+
131
+
132
+	/**
133
+	 * what month should the event list display events for?
134
+	 *
135
+	 * @param string $month
136
+	 * @return string
137
+	 * @throws InvalidArgumentException
138
+	 * @throws InvalidDataTypeException
139
+	 * @throws InvalidInterfaceException
140
+	 */
141
+	private static function _display_month($month = '')
142
+	{
143
+		return self::getRequest()->getRequestParam('event_query_month', $month);
144
+	}
145
+
146
+
147
+	/**
148
+	 * @param string $category
149
+	 * @return string
150
+	 * @throws InvalidArgumentException
151
+	 * @throws InvalidDataTypeException
152
+	 * @throws InvalidInterfaceException
153
+	 */
154
+	private static function _event_category_slug($category = '')
155
+	{
156
+		return self::getRequest()->getRequestParam('event_query_category', $category);
157
+	}
158
+
159
+
160
+	/**
161
+	 * @param bool $show_expired
162
+	 * @return bool
163
+	 * @throws InvalidArgumentException
164
+	 * @throws InvalidDataTypeException
165
+	 * @throws InvalidInterfaceException
166
+	 */
167
+	private static function _show_expired($show_expired = false)
168
+	{
169
+		// override default expired option if set via filter
170
+		return self::getRequest()->getRequestParam('event_query_show_expired', $show_expired, 'bool');
171
+	}
172
+
173
+
174
+	/**
175
+	 * @param array|string $orderby
176
+	 * @return array
177
+	 * @throws InvalidArgumentException
178
+	 * @throws InvalidDataTypeException
179
+	 * @throws InvalidInterfaceException
180
+	 */
181
+	private static function _orderby($orderby = 'start_date')
182
+	{
183
+		$event_query_orderby = self::getRequest()->getRequestParam(
184
+			'event_query_orderby',
185
+			(array) $orderby,
186
+			DataType::STRING,
187
+			true
188
+		);
189
+		$event_query_orderby = is_array($event_query_orderby)
190
+			? $event_query_orderby
191
+			: explode(',', $event_query_orderby);
192
+		$event_query_orderby = array_map('trim', $event_query_orderby);
193
+		return array_map('sanitize_text_field', $event_query_orderby);
194
+	}
195
+
196
+
197
+	/**
198
+	 * @param string $sort
199
+	 * @return string
200
+	 * @throws InvalidArgumentException
201
+	 * @throws InvalidDataTypeException
202
+	 * @throws InvalidInterfaceException
203
+	 */
204
+	private static function _sort($sort = 'ASC')
205
+	{
206
+		$sort = self::getRequest()->getRequestParam('event_query_sort', $sort);
207
+		return in_array($sort, ['ASC', 'asc', 'DESC', 'desc'], true)
208
+			? strtoupper($sort)
209
+			: 'ASC';
210
+	}
211
+
212
+
213
+	/**
214
+	 * Filters the clauses for the WP_Query object
215
+	 *
216
+	 * @param array    $clauses array of clauses
217
+	 * @param WP_Query $wp_query
218
+	 * @return array   array of clauses
219
+	 */
220
+	public static function posts_clauses($clauses, WP_Query $wp_query)
221
+	{
222
+		if (EEH_Event_Query::apply_query_filters($wp_query)) {
223
+			global $wpdb;
224
+			$clauses['groupby'] = $wpdb->posts . '.ID ';
225
+		}
226
+		return $clauses;
227
+	}
228
+
229
+
230
+	/**
231
+	 * @param string   $SQL
232
+	 * @param WP_Query $wp_query
233
+	 * @return string
234
+	 * @throws EE_Error
235
+	 * @throws InvalidArgumentException
236
+	 * @throws InvalidDataTypeException
237
+	 * @throws InvalidInterfaceException
238
+	 */
239
+	public static function posts_fields($SQL, WP_Query $wp_query)
240
+	{
241
+		if (EEH_Event_Query::apply_query_filters($wp_query)) {
242
+			// adds something like ", wp_esp_datetime.* " to WP Query SELECT statement
243
+			$SQL .= EEH_Event_Query::posts_fields_sql_for_orderby(EEH_Event_Query::$_event_query_orderby);
244
+		}
245
+		return $SQL;
246
+	}
247
+
248
+
249
+	/**
250
+	 * @param array $orderby_params
251
+	 * @return string
252
+	 * @throws EE_Error
253
+	 * @throws InvalidArgumentException
254
+	 * @throws InvalidDataTypeException
255
+	 * @throws InvalidInterfaceException
256
+	 */
257
+	public static function posts_fields_sql_for_orderby(array $orderby_params = [])
258
+	{
259
+		$SQL = ', MIN( ' . EEM_Datetime::instance()->table() . '.DTT_EVT_start ) as event_start_date ';
260
+		foreach ($orderby_params as $orderby) {
261
+			switch ($orderby) {
262
+				case 'ticket_start':
263
+					$SQL .= ', ' . EEM_Ticket::instance()->table() . '.TKT_start_date';
264
+					break;
265
+				case 'ticket_end':
266
+					$SQL .= ', ' . EEM_Ticket::instance()->table() . '.TKT_end_date';
267
+					break;
268
+				case 'venue_title':
269
+					$SQL .= ', Venue.post_title AS venue_title';
270
+					break;
271
+				case 'city':
272
+					$SQL .= ', ' . EEM_Venue::instance()->second_table() . '.VNU_city';
273
+					break;
274
+				case 'state':
275
+					$SQL .= ', ' . EEM_State::instance()->table() . '.STA_name';
276
+					break;
277
+			}
278
+		}
279
+		return $SQL;
280
+	}
281
+
282
+
283
+	/**
284
+	 * @param string   $SQL
285
+	 * @param WP_Query $wp_query
286
+	 * @return string
287
+	 * @throws EE_Error
288
+	 * @throws InvalidArgumentException
289
+	 * @throws InvalidDataTypeException
290
+	 * @throws InvalidInterfaceException
291
+	 */
292
+	public static function posts_join($SQL, WP_Query $wp_query)
293
+	{
294
+		if (EEH_Event_Query::apply_query_filters($wp_query)) {
295
+			// Category
296
+			$SQL = EEH_Event_Query::posts_join_sql_for_show_expired($SQL, EEH_Event_Query::$_event_query_show_expired);
297
+			$SQL = EEH_Event_Query::posts_join_sql_for_terms($SQL, EEH_Event_Query::$_event_query_category);
298
+			$SQL = EEH_Event_Query::posts_join_for_orderby($SQL, EEH_Event_Query::$_event_query_orderby);
299
+		}
300
+		return $SQL;
301
+	}
302
+
303
+
304
+	/**
305
+	 * @param string  $SQL
306
+	 * @param boolean $show_expired if TRUE, then displayed past events
307
+	 * @return string
308
+	 * @throws EE_Error
309
+	 * @throws InvalidArgumentException
310
+	 * @throws InvalidDataTypeException
311
+	 * @throws InvalidInterfaceException
312
+	 */
313
+	public static function posts_join_sql_for_show_expired($SQL = '', $show_expired = false)
314
+	{
315
+		if (! $show_expired) {
316
+			$join = EEM_Event::instance()->table() . '.ID = ';
317
+			$join .= EEM_Datetime::instance()->table() . '.' . EEM_Event::instance()->primary_key_name();
318
+			// don't add if this is already in the SQL
319
+			if (strpos($SQL, $join) === false) {
320
+				$SQL .= ' INNER JOIN ' . EEM_Datetime::instance()->table() . ' ON ( ' . $join . ' ) ';
321
+			}
322
+		}
323
+		return $SQL;
324
+	}
325
+
326
+
327
+	/**
328
+	 * @param string $SQL
329
+	 * @param string $join_terms    pass TRUE or term string, doesn't really matter since this value doesn't really get
330
+	 *                              used for anything yet
331
+	 * @return string
332
+	 */
333
+	public static function posts_join_sql_for_terms($SQL = '', $join_terms = '')
334
+	{
335
+		if (! empty($join_terms)) {
336
+			global $wpdb;
337
+			$SQL .= " LEFT JOIN $wpdb->term_relationships ON ($wpdb->posts.ID = $wpdb->term_relationships.object_id)";
338
+			$SQL .= " LEFT JOIN $wpdb->term_taxonomy ON ($wpdb->term_relationships.term_taxonomy_id = $wpdb->term_taxonomy.term_taxonomy_id)";
339
+			$SQL .= " LEFT JOIN $wpdb->terms ON ($wpdb->terms.term_id = $wpdb->term_taxonomy.term_id) ";
340
+		}
341
+		return $SQL;
342
+	}
343
+
344
+
345
+	/**
346
+	 * usage:  $SQL .= EEH_Event_Query::posts_join_for_orderby( $orderby_params );
347
+	 *
348
+	 * @param string $SQL
349
+	 * @param array  $orderby_params
350
+	 * @return string
351
+	 * @throws EE_Error
352
+	 * @throws InvalidArgumentException
353
+	 * @throws InvalidDataTypeException
354
+	 * @throws InvalidInterfaceException
355
+	 */
356
+	public static function posts_join_for_orderby($SQL = '', array $orderby_params = [])
357
+	{
358
+		foreach ($orderby_params as $orderby) {
359
+			switch ($orderby) {
360
+				case 'ticket_start':
361
+				case 'ticket_end':
362
+					$SQL .= EEH_Event_Query::_posts_join_for_datetime(
363
+						$SQL,
364
+						EEM_Datetime_Ticket::instance()->table() . '.' . EEM_Datetime::instance()->primary_key_name()
365
+					);
366
+					$SQL .= ' LEFT JOIN ' . EEM_Ticket::instance()->table();
367
+					$SQL .= ' ON (';
368
+					$SQL .= EEM_Datetime_Ticket::instance()->table() . '.' . EEM_Ticket::instance()->primary_key_name();
369
+					$SQL .= ' = ';
370
+					$SQL .= EEM_Ticket::instance()->table() . '.' . EEM_Ticket::instance()->primary_key_name();
371
+					$SQL .= ' )';
372
+					break;
373
+				case 'venue_title':
374
+				case 'city':
375
+					$SQL .= EEH_Event_Query::_posts_join_for_event_venue($SQL);
376
+					break;
377
+				case 'state':
378
+					$SQL .= EEH_Event_Query::_posts_join_for_event_venue($SQL);
379
+					$SQL .= EEH_Event_Query::_posts_join_for_venue_state($SQL);
380
+					break;
381
+				case 'start_date':
382
+				default:
383
+					$SQL .= EEH_Event_Query::_posts_join_for_datetime($SQL, EEM_Event::instance()->table() . '.ID');
384
+					break;
385
+			}
386
+		}
387
+		return $SQL;
388
+	}
389
+
390
+
391
+	/**
392
+	 * @param string $SQL
393
+	 * @param string $join
394
+	 * @return string
395
+	 * @throws EE_Error
396
+	 * @throws InvalidArgumentException
397
+	 * @throws InvalidDataTypeException
398
+	 * @throws InvalidInterfaceException
399
+	 */
400
+	protected static function _posts_join_for_datetime($SQL = '', $join = '')
401
+	{
402
+		if (! empty($join)) {
403
+			$join .= ' = ' . EEM_Datetime::instance()->table() . '.' . EEM_Event::instance()->primary_key_name();
404
+			if (strpos($SQL, $join) === false) {
405
+				return ' INNER JOIN ' . EEM_Datetime::instance()->table() . ' ON ( ' . $join . ' )';
406
+			}
407
+		}
408
+		return '';
409
+	}
410
+
411
+
412
+	/**
413
+	 * @param string $SQL
414
+	 * @return string
415
+	 * @throws EE_Error
416
+	 * @throws InvalidArgumentException
417
+	 * @throws InvalidDataTypeException
418
+	 * @throws InvalidInterfaceException
419
+	 */
420
+	protected static function _posts_join_for_event_venue($SQL = '')
421
+	{
422
+		// Event Venue table name
423
+		$event_venue_table = EEM_Event_Venue::instance()->table();
424
+		// generate conditions for:  Event <=> Event Venue  JOIN clause
425
+		$event_to_event_venue_join = EEM_Event::instance()->table() . '.ID = ';
426
+		$event_to_event_venue_join .= $event_venue_table . '.' . EEM_Event::instance()->primary_key_name();
427
+		// don't add joins if they have already been added
428
+		if (strpos($SQL, $event_to_event_venue_join) === false) {
429
+			// Venue table name
430
+			$venue_table = EEM_Venue::instance()->table();
431
+			// Venue table pk
432
+			$venue_table_pk = EEM_Venue::instance()->primary_key_name();
433
+			// Venue Meta table name
434
+			$venue_meta_table = EEM_Venue::instance()->second_table();
435
+			// generate JOIN clause for: Event <=> Event Venue
436
+			$venue_SQL = " LEFT JOIN $event_venue_table ON ( $event_to_event_venue_join )";
437
+			// generate JOIN clause for: Event Venue <=> Venue
438
+			$venue_SQL .= " LEFT JOIN $venue_table as Venue ON ( $event_venue_table.$venue_table_pk = Venue.ID )";
439
+			// generate JOIN clause for: Venue <=> Venue Meta
440
+			$venue_SQL .= " LEFT JOIN $venue_meta_table ON ( Venue.ID = $venue_meta_table.$venue_table_pk )";
441
+			unset($event_venue_table, $event_to_event_venue_join, $venue_table, $venue_table_pk, $venue_meta_table);
442
+			return $venue_SQL;
443
+		}
444
+		unset($event_venue_table, $event_to_event_venue_join);
445
+		return '';
446
+	}
447
+
448
+
449
+	/**
450
+	 * @param string $SQL
451
+	 * @return string
452
+	 * @throws EE_Error
453
+	 * @throws InvalidArgumentException
454
+	 * @throws InvalidDataTypeException
455
+	 * @throws InvalidInterfaceException
456
+	 */
457
+	protected static function _posts_join_for_venue_state($SQL = '')
458
+	{
459
+		// Venue Meta table name
460
+		$venue_meta_table = EEM_Venue::instance()->second_table();
461
+		// State table name
462
+		$state_table = EEM_State::instance()->table();
463
+		// State table pk
464
+		$state_table_pk = EEM_State::instance()->primary_key_name();
465
+		// verify vars
466
+		if ($venue_meta_table && $state_table && $state_table_pk) {
467
+			// like: wp_esp_venue_meta.STA_ID = wp_esp_state.STA_ID
468
+			$join = "$venue_meta_table.$state_table_pk = $state_table.$state_table_pk";
469
+			// don't add join if it has already been added
470
+			if (strpos($SQL, $join) === false) {
471
+				unset($state_table_pk, $venue_meta_table, $venue_table_pk);
472
+				return " LEFT JOIN $state_table ON ( $join )";
473
+			}
474
+		}
475
+		unset($join, $state_table, $state_table_pk, $venue_meta_table, $venue_table_pk);
476
+		return '';
477
+	}
478
+
479
+
480
+	/**
481
+	 * @param string   $SQL
482
+	 * @param WP_Query $wp_query
483
+	 * @return string
484
+	 * @throws EE_Error
485
+	 * @throws InvalidArgumentException
486
+	 * @throws InvalidDataTypeException
487
+	 * @throws InvalidInterfaceException
488
+	 */
489
+	public static function posts_where($SQL, WP_Query $wp_query)
490
+	{
491
+		if (EEH_Event_Query::apply_query_filters($wp_query)) {
492
+			// Show Expired ?
493
+			$SQL .= EEH_Event_Query::posts_where_sql_for_show_expired(EEH_Event_Query::$_event_query_show_expired);
494
+			// Category
495
+			$SQL .= EEH_Event_Query::posts_where_sql_for_event_category_slug(EEH_Event_Query::$_event_query_category);
496
+			// Start Date
497
+			$SQL .= EEH_Event_Query::posts_where_sql_for_event_list_month(EEH_Event_Query::$_event_query_month);
498
+		}
499
+		return $SQL;
500
+	}
501
+
502
+
503
+	/**
504
+	 * @param boolean $show_expired if TRUE, then displayed past events
505
+	 * @return string
506
+	 * @throws EE_Error
507
+	 * @throws InvalidArgumentException
508
+	 * @throws InvalidDataTypeException
509
+	 * @throws InvalidInterfaceException
510
+	 */
511
+	public static function posts_where_sql_for_show_expired($show_expired = false)
512
+	{
513
+		return ! $show_expired
514
+			? ' AND ' . EEM_Datetime::instance()->table() . '.DTT_EVT_end > \'' . current_time('mysql', true) . '\' '
515
+			: '';
516
+	}
517
+
518
+
519
+	/**
520
+	 * @param boolean $event_category_slug
521
+	 * @return string
522
+	 */
523
+	public static function posts_where_sql_for_event_category_slug($event_category_slug = null)
524
+	{
525
+		global $wpdb;
526
+		if (! empty($event_category_slug)) {
527
+			$event_category_slugs_array   = array_map('trim', explode(',', $event_category_slug));
528
+			$event_category_slugs_prepare = implode(', ', array_fill(0, count($event_category_slugs_array), '%s'));
529
+			return $wpdb->prepare(
530
+				" AND {$wpdb->terms}.slug IN ({$event_category_slugs_prepare}) ",
531
+				$event_category_slugs_array
532
+			);
533
+		}
534
+		return '';
535
+	}
536
+
537
+
538
+	/**
539
+	 * @param boolean $month
540
+	 * @return string
541
+	 * @throws EE_Error
542
+	 * @throws InvalidArgumentException
543
+	 * @throws InvalidDataTypeException
544
+	 * @throws InvalidInterfaceException
545
+	 */
546
+	public static function posts_where_sql_for_event_list_month($month = null)
547
+	{
548
+		$SQL = '';
549
+		if (! empty($month)) {
550
+			$datetime_table = EEM_Datetime::instance()->table();
551
+			// event start date is LESS than the end of the month ( so nothing that doesn't start until next month )
552
+			$SQL = " AND {$datetime_table}.DTT_EVT_start <= '";
553
+			$SQL .= date('Y-m-t 23:59:59', EEH_DTT_Helper::first_of_month_timestamp($month)) . "'";
554
+			// event end date is GREATER than the start of the month ( so nothing that ended before this month )
555
+			$SQL .= " AND {$datetime_table}.DTT_EVT_end >= '";
556
+			$SQL .= date('Y-m-01 0:0:00', EEH_DTT_Helper::first_of_month_timestamp($month)) . "' ";
557
+		}
558
+		return $SQL;
559
+	}
560
+
561
+
562
+	/**
563
+	 * @param string   $SQL
564
+	 * @param WP_Query $wp_query
565
+	 * @return string
566
+	 * @throws EE_Error
567
+	 * @throws InvalidArgumentException
568
+	 * @throws InvalidDataTypeException
569
+	 * @throws InvalidInterfaceException
570
+	 */
571
+	public static function posts_orderby($SQL, WP_Query $wp_query)
572
+	{
573
+		if (EEH_Event_Query::apply_query_filters($wp_query)) {
574
+			$SQL = EEH_Event_Query::posts_orderby_sql(
575
+				EEH_Event_Query::$_event_query_orderby,
576
+				EEH_Event_Query::$_event_query_sort
577
+			);
578
+		}
579
+		return $SQL;
580
+	}
581
+
582
+
583
+	/**
584
+	 *    posts_orderby_sql
585
+	 *    possible parameters:
586
+	 *    ID
587
+	 *    start_date
588
+	 *    end_date
589
+	 *    event_name
590
+	 *    category_slug
591
+	 *    ticket_start
592
+	 *    ticket_end
593
+	 *    venue_title
594
+	 *    city
595
+	 *    state
596
+	 *    **IMPORTANT**
597
+	 *    make sure to also send the $orderby_params array to the posts_join_for_orderby() method
598
+	 *    or else some of the table references below will result in MySQL errors
599
+	 *
600
+	 * @param array  $orderby_params
601
+	 * @param string $sort
602
+	 * @return string
603
+	 * @throws EE_Error
604
+	 * @throws InvalidArgumentException
605
+	 * @throws InvalidDataTypeException
606
+	 * @throws InvalidInterfaceException
607
+	 */
608
+	public static function posts_orderby_sql(array $orderby_params = [], $sort = 'ASC')
609
+	{
610
+		global $wpdb;
611
+		$SQL     = '';
612
+		$counter = 0;
613
+		$sort    = in_array($sort, ['ASC', 'asc', 'DESC', 'desc'], true)
614
+			? strtoupper($sort)
615
+			: 'ASC';
616
+		// make sure 'orderby' is set in query params
617
+		if (! isset(self::$_query_params['orderby'])) {
618
+			self::$_query_params['orderby'] = [];
619
+		}
620
+		// loop thru $orderby_params (type cast as array)
621
+		foreach ($orderby_params as $orderby) {
622
+			// check if we have already added this param
623
+			if (isset(self::$_query_params['orderby'][ $orderby ])) {
624
+				// if so then remove from the $orderby_params so that the count() method below is accurate
625
+				unset($orderby_params[ $orderby ]);
626
+				// then bump ahead to the next param
627
+				continue;
628
+			}
629
+			// this will ad a comma depending on whether this is the first or last param
630
+			$glue = $counter === 0 || $counter === count($orderby_params) ? ' ' : ', ';
631
+			// ok what's we dealing with?
632
+			switch ($orderby) {
633
+				case 'id':
634
+				case 'ID':
635
+					$SQL .= $glue . $wpdb->posts . '.ID ' . $sort;
636
+					break;
637
+				case 'end_date':
638
+					$SQL .= $glue . EEM_Datetime::instance()->table() . '.DTT_EVT_end ' . $sort;
639
+					break;
640
+				case 'event_name':
641
+					$SQL .= $glue . $wpdb->posts . '.post_title ' . $sort;
642
+					break;
643
+				case 'category_slug':
644
+					$SQL .= $glue . $wpdb->terms . '.slug ' . $sort;
645
+					break;
646
+				case 'ticket_start':
647
+					$SQL .= $glue . EEM_Ticket::instance()->table() . '.TKT_start_date ' . $sort;
648
+					break;
649
+				case 'ticket_end':
650
+					$SQL .= $glue . EEM_Ticket::instance()->table() . '.TKT_end_date ' . $sort;
651
+					break;
652
+				case 'venue_title':
653
+					$SQL .= $glue . 'venue_title ' . $sort;
654
+					break;
655
+				case 'city':
656
+					$SQL .= $glue . EEM_Venue::instance()->second_table() . '.VNU_city ' . $sort;
657
+					break;
658
+				case 'state':
659
+					$SQL .= $glue . EEM_State::instance()->table() . '.STA_name ' . $sort;
660
+					break;
661
+				case 'start_date':
662
+				default:
663
+					$SQL .= $glue . ' event_start_date ' . $sort;
664
+					break;
665
+			}
666
+			// add to array of orderby params that have been added
667
+			self::$_query_params['orderby'][ $orderby ] = true;
668
+			$counter++;
669
+		}
670
+		return $SQL;
671
+	}
672
+
673
+
674
+	/**
675
+	 * @return RequestInterface
676
+	 * @since   4.10.14.p
677
+	 */
678
+	private static function getRequest()
679
+	{
680
+		return LoaderFactory::getLoader()->getShared(RequestInterface::class);
681
+	}
682 682
 }
Please login to merge, or discard this patch.