@@ -13,1835 +13,1835 @@ |
||
| 13 | 13 | */ |
| 14 | 14 | class EE_Ticket extends EE_Soft_Delete_Base_Class implements EEI_Line_Item_Object, EEI_Event_Relation, EEI_Has_Icon |
| 15 | 15 | { |
| 16 | - /** |
|
| 17 | - * The following constants are used by the ticket_status() method to indicate whether a ticket is on sale or not. |
|
| 18 | - */ |
|
| 19 | - const sold_out = 'TKS'; |
|
| 20 | - |
|
| 21 | - /** |
|
| 22 | - * |
|
| 23 | - */ |
|
| 24 | - const expired = 'TKE'; |
|
| 25 | - |
|
| 26 | - /** |
|
| 27 | - * |
|
| 28 | - */ |
|
| 29 | - const archived = 'TKA'; |
|
| 30 | - |
|
| 31 | - /** |
|
| 32 | - * |
|
| 33 | - */ |
|
| 34 | - const pending = 'TKP'; |
|
| 35 | - |
|
| 36 | - /** |
|
| 37 | - * |
|
| 38 | - */ |
|
| 39 | - const onsale = 'TKO'; |
|
| 40 | - |
|
| 41 | - /** |
|
| 42 | - * extra meta key for tracking ticket reservations |
|
| 43 | - * |
|
| 44 | - * @type string |
|
| 45 | - */ |
|
| 46 | - const META_KEY_TICKET_RESERVATIONS = 'ticket_reservations'; |
|
| 47 | - |
|
| 48 | - /** |
|
| 49 | - * cached result from method of the same name |
|
| 50 | - * |
|
| 51 | - * @var float $_ticket_total_with_taxes |
|
| 52 | - */ |
|
| 53 | - private $_ticket_total_with_taxes; |
|
| 54 | - |
|
| 55 | - |
|
| 56 | - /** |
|
| 57 | - * @param array $props_n_values incoming values |
|
| 58 | - * @param string $timezone incoming timezone (if not set the timezone set for the website will be |
|
| 59 | - * used.) |
|
| 60 | - * @param array $date_formats incoming date_formats in an array where the first value is the |
|
| 61 | - * date_format and the second value is the time format |
|
| 62 | - * @return EE_Ticket |
|
| 63 | - * @throws EE_Error |
|
| 64 | - * @throws ReflectionException |
|
| 65 | - */ |
|
| 66 | - public static function new_instance($props_n_values = [], $timezone = null, $date_formats = []) |
|
| 67 | - { |
|
| 68 | - $ticket = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats); |
|
| 69 | - if (! $ticket instanceof EE_Ticket) { |
|
| 70 | - $ticket = new EE_Ticket($props_n_values, false, $timezone, $date_formats); |
|
| 71 | - } |
|
| 72 | - return $ticket; |
|
| 73 | - } |
|
| 74 | - |
|
| 75 | - |
|
| 76 | - /** |
|
| 77 | - * @param array $props_n_values incoming values from the database |
|
| 78 | - * @param string $timezone incoming timezone as set by the model. If not set the timezone for |
|
| 79 | - * the website will be used. |
|
| 80 | - * @return EE_Ticket |
|
| 81 | - * @throws EE_Error |
|
| 82 | - * @throws ReflectionException |
|
| 83 | - */ |
|
| 84 | - public static function new_instance_from_db($props_n_values = [], $timezone = null) |
|
| 85 | - { |
|
| 86 | - return new self($props_n_values, true, $timezone); |
|
| 87 | - } |
|
| 88 | - |
|
| 89 | - |
|
| 90 | - /** |
|
| 91 | - * @param array $props_n_values |
|
| 92 | - * @param bool $bydb |
|
| 93 | - * @param string $timezone |
|
| 94 | - * @param array $date_formats incoming date_formats in an array where the first value is the |
|
| 95 | - * date_format and the second value is the time format |
|
| 96 | - * @throws EE_Error |
|
| 97 | - * @throws ReflectionException |
|
| 98 | - */ |
|
| 99 | - protected function __construct($props_n_values = [], $bydb = false, $timezone = '', $date_formats = []) |
|
| 100 | - { |
|
| 101 | - parent::__construct($props_n_values, $bydb, $timezone, $date_formats); |
|
| 102 | - } |
|
| 103 | - |
|
| 104 | - |
|
| 105 | - /** |
|
| 106 | - * @return bool |
|
| 107 | - * @throws EE_Error|ReflectionException |
|
| 108 | - */ |
|
| 109 | - public function parent() |
|
| 110 | - { |
|
| 111 | - return $this->get('TKT_parent'); |
|
| 112 | - } |
|
| 113 | - |
|
| 114 | - |
|
| 115 | - /** |
|
| 116 | - * return if a ticket has quantities available for purchase |
|
| 117 | - * |
|
| 118 | - * @param int $DTT_ID the primary key for a particular datetime |
|
| 119 | - * @return boolean |
|
| 120 | - * @throws EE_Error |
|
| 121 | - * @throws ReflectionException |
|
| 122 | - */ |
|
| 123 | - public function available($DTT_ID = 0) |
|
| 124 | - { |
|
| 125 | - // are we checking availability for a particular datetime ? |
|
| 126 | - if ($DTT_ID) { |
|
| 127 | - // get that datetime object |
|
| 128 | - $datetime = $this->get_first_related('Datetime', [['DTT_ID' => $DTT_ID]]); |
|
| 129 | - // if ticket sales for this datetime have exceeded the reg limit... |
|
| 130 | - if ($datetime instanceof EE_Datetime && $datetime->sold_out()) { |
|
| 131 | - return false; |
|
| 132 | - } |
|
| 133 | - } |
|
| 134 | - // datetime is still open for registration, but is this ticket sold out ? |
|
| 135 | - return $this->qty() < 1 || $this->qty() > $this->sold(); |
|
| 136 | - } |
|
| 137 | - |
|
| 138 | - |
|
| 139 | - /** |
|
| 140 | - * Using the start date and end date this method calculates whether the ticket is On Sale, Pending, or Expired |
|
| 141 | - * |
|
| 142 | - * @param bool $display true = we'll return a localized string, otherwise we just return the value of the |
|
| 143 | - * relevant status const |
|
| 144 | - * @param bool | null $remaining if it is already known that tickets are available, then simply pass a bool to save |
|
| 145 | - * further processing |
|
| 146 | - * @return mixed status int if the display string isn't requested |
|
| 147 | - * @throws EE_Error |
|
| 148 | - * @throws ReflectionException |
|
| 149 | - */ |
|
| 150 | - public function ticket_status($display = false, $remaining = null) |
|
| 151 | - { |
|
| 152 | - $remaining = is_bool($remaining) ? $remaining : $this->is_remaining(); |
|
| 153 | - if (! $remaining) { |
|
| 154 | - return $display |
|
| 155 | - ? EEH_Template::pretty_status(EE_Ticket::sold_out, false, 'sentence') |
|
| 156 | - : EE_Ticket::sold_out; |
|
| 157 | - } |
|
| 158 | - if ($this->get('TKT_deleted')) { |
|
| 159 | - return $display |
|
| 160 | - ? EEH_Template::pretty_status(EE_Ticket::archived, false, 'sentence') |
|
| 161 | - : EE_Ticket::archived; |
|
| 162 | - } |
|
| 163 | - if ($this->is_expired()) { |
|
| 164 | - return $display |
|
| 165 | - ? EEH_Template::pretty_status(EE_Ticket::expired, false, 'sentence') |
|
| 166 | - : EE_Ticket::expired; |
|
| 167 | - } |
|
| 168 | - if ($this->is_pending()) { |
|
| 169 | - return $display |
|
| 170 | - ? EEH_Template::pretty_status(EE_Ticket::pending, false, 'sentence') |
|
| 171 | - : EE_Ticket::pending; |
|
| 172 | - } |
|
| 173 | - if ($this->is_on_sale()) { |
|
| 174 | - return $display |
|
| 175 | - ? EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence') |
|
| 176 | - : EE_Ticket::onsale; |
|
| 177 | - } |
|
| 178 | - return ''; |
|
| 179 | - } |
|
| 180 | - |
|
| 181 | - |
|
| 182 | - /** |
|
| 183 | - * The purpose of this method is to simply return a boolean for whether there are any tickets remaining for sale |
|
| 184 | - * considering ALL the factors used for figuring that out. |
|
| 185 | - * |
|
| 186 | - * @access public |
|
| 187 | - * @param int $DTT_ID if an int above 0 is included here then we get a specific dtt. |
|
| 188 | - * @return boolean true = tickets remaining, false not. |
|
| 189 | - * @throws EE_Error|ReflectionException |
|
| 190 | - */ |
|
| 191 | - public function is_remaining($DTT_ID = 0) |
|
| 192 | - { |
|
| 193 | - $num_remaining = $this->remaining($DTT_ID); |
|
| 194 | - if ($num_remaining === 0) { |
|
| 195 | - return false; |
|
| 196 | - } |
|
| 197 | - if ($num_remaining > 0 && $num_remaining < $this->min()) { |
|
| 198 | - return false; |
|
| 199 | - } |
|
| 200 | - return true; |
|
| 201 | - } |
|
| 202 | - |
|
| 203 | - |
|
| 204 | - /** |
|
| 205 | - * return the total number of tickets available for purchase |
|
| 206 | - * |
|
| 207 | - * @param int $DTT_ID the primary key for a particular datetime. |
|
| 208 | - * set to 0 for all related datetimes |
|
| 209 | - * @return int |
|
| 210 | - * @throws EE_Error|ReflectionException |
|
| 211 | - */ |
|
| 212 | - public function remaining($DTT_ID = 0) |
|
| 213 | - { |
|
| 214 | - return $this->real_quantity_on_ticket('saleable', $DTT_ID); |
|
| 215 | - } |
|
| 216 | - |
|
| 217 | - |
|
| 218 | - /** |
|
| 219 | - * Gets min |
|
| 220 | - * |
|
| 221 | - * @return int |
|
| 222 | - * @throws EE_Error |
|
| 223 | - * @throws ReflectionException |
|
| 224 | - */ |
|
| 225 | - public function min() |
|
| 226 | - { |
|
| 227 | - return $this->get('TKT_min'); |
|
| 228 | - } |
|
| 229 | - |
|
| 230 | - |
|
| 231 | - /** |
|
| 232 | - * return if a ticket is no longer available cause its available dates have expired. |
|
| 233 | - * |
|
| 234 | - * @return boolean |
|
| 235 | - * @throws EE_Error |
|
| 236 | - * @throws ReflectionException |
|
| 237 | - */ |
|
| 238 | - public function is_expired() |
|
| 239 | - { |
|
| 240 | - return ($this->get_raw('TKT_end_date') < time()); |
|
| 241 | - } |
|
| 242 | - |
|
| 243 | - |
|
| 244 | - /** |
|
| 245 | - * Return if a ticket is yet to go on sale or not |
|
| 246 | - * |
|
| 247 | - * @return boolean |
|
| 248 | - * @throws EE_Error |
|
| 249 | - * @throws ReflectionException |
|
| 250 | - */ |
|
| 251 | - public function is_pending() |
|
| 252 | - { |
|
| 253 | - return ($this->get_raw('TKT_start_date') > time()); |
|
| 254 | - } |
|
| 255 | - |
|
| 256 | - |
|
| 257 | - /** |
|
| 258 | - * Return if a ticket is on sale or not |
|
| 259 | - * |
|
| 260 | - * @return boolean |
|
| 261 | - * @throws EE_Error |
|
| 262 | - * @throws ReflectionException |
|
| 263 | - */ |
|
| 264 | - public function is_on_sale() |
|
| 265 | - { |
|
| 266 | - return ($this->get_raw('TKT_start_date') < time() && $this->get_raw('TKT_end_date') > time()); |
|
| 267 | - } |
|
| 268 | - |
|
| 269 | - |
|
| 270 | - /** |
|
| 271 | - * This returns the chronologically last datetime that this ticket is associated with |
|
| 272 | - * |
|
| 273 | - * @param string $date_format |
|
| 274 | - * @param string $conjunction - conjunction junction what's your function ? this string joins the start date with |
|
| 275 | - * the end date ie: Jan 01 "to" Dec 31 |
|
| 276 | - * @return string |
|
| 277 | - * @throws EE_Error |
|
| 278 | - * @throws ReflectionException |
|
| 279 | - */ |
|
| 280 | - public function date_range($date_format = '', $conjunction = ' - ') |
|
| 281 | - { |
|
| 282 | - $date_format = ! empty($date_format) ? $date_format : $this->_dt_frmt; |
|
| 283 | - $first_date = $this->first_datetime() instanceof EE_Datetime |
|
| 284 | - ? $this->first_datetime()->get_i18n_datetime('DTT_EVT_start', $date_format) |
|
| 285 | - : ''; |
|
| 286 | - $last_date = $this->last_datetime() instanceof EE_Datetime |
|
| 287 | - ? $this->last_datetime()->get_i18n_datetime('DTT_EVT_end', $date_format) |
|
| 288 | - : ''; |
|
| 289 | - |
|
| 290 | - return $first_date && $last_date ? $first_date . $conjunction . $last_date : ''; |
|
| 291 | - } |
|
| 292 | - |
|
| 293 | - |
|
| 294 | - /** |
|
| 295 | - * This returns the chronologically first datetime that this ticket is associated with |
|
| 296 | - * |
|
| 297 | - * @return EE_Datetime |
|
| 298 | - * @throws EE_Error|ReflectionException |
|
| 299 | - */ |
|
| 300 | - public function first_datetime() |
|
| 301 | - { |
|
| 302 | - $datetimes = $this->datetimes(['limit' => 1]); |
|
| 303 | - return reset($datetimes); |
|
| 304 | - } |
|
| 305 | - |
|
| 306 | - |
|
| 307 | - /** |
|
| 308 | - * Gets all the datetimes this ticket can be used for attending. |
|
| 309 | - * Unless otherwise specified, orders datetimes by start date. |
|
| 310 | - * |
|
| 311 | - * @param array $query_params |
|
| 312 | - * @return EE_Datetime[]|EE_Base_Class[] |
|
| 313 | - * @throws EE_Error |
|
| 314 | - * @throws ReflectionException |
|
| 315 | - */ |
|
| 316 | - public function datetimes($query_params = []) |
|
| 317 | - { |
|
| 318 | - if (! isset($query_params['order_by'])) { |
|
| 319 | - $query_params['order_by']['DTT_order'] = 'ASC'; |
|
| 320 | - } |
|
| 321 | - return $this->get_many_related('Datetime', $query_params); |
|
| 322 | - } |
|
| 323 | - |
|
| 324 | - |
|
| 325 | - /** |
|
| 326 | - * This returns the chronologically last datetime that this ticket is associated with |
|
| 327 | - * |
|
| 328 | - * @return EE_Datetime |
|
| 329 | - * @throws EE_Error|ReflectionException |
|
| 330 | - */ |
|
| 331 | - public function last_datetime() |
|
| 332 | - { |
|
| 333 | - $datetimes = $this->datetimes(['limit' => 1, 'order_by' => ['DTT_EVT_start' => 'DESC']]); |
|
| 334 | - return end($datetimes); |
|
| 335 | - } |
|
| 336 | - |
|
| 337 | - |
|
| 338 | - /** |
|
| 339 | - * This returns the total tickets sold depending on the given parameters. |
|
| 340 | - * |
|
| 341 | - * @param string $get_sold_for Can be one of two options: 'ticket', 'datetime'. |
|
| 342 | - * 'ticket' = total ticket sales for all datetimes this ticket is related to |
|
| 343 | - * 'datetime' = total ticket sales for a specified datetime (required $dtt_id) |
|
| 344 | - * 'datetime' = total ticket sales in the datetime_ticket table. |
|
| 345 | - * If $dtt_id is not given then we return an array of sales indexed by datetime. |
|
| 346 | - * If $dtt_id IS given then we return the tickets sold for that given datetime. |
|
| 347 | - * @param int $dtt_id [optional] include the dtt_id with $what = 'datetime'. |
|
| 348 | - * @return mixed (array|int) how many tickets have sold |
|
| 349 | - * @throws EE_Error|ReflectionException |
|
| 350 | - */ |
|
| 351 | - public function tickets_sold($get_sold_for = 'ticket', $dtt_id = null) |
|
| 352 | - { |
|
| 353 | - $total = 0; |
|
| 354 | - $tickets_sold = $this->_all_tickets_sold(); |
|
| 355 | - |
|
| 356 | - if ($get_sold_for === 'ticket') { |
|
| 357 | - return $tickets_sold['ticket']; |
|
| 358 | - } |
|
| 359 | - |
|
| 360 | - if (empty($tickets_sold['datetime'])) { |
|
| 361 | - return $total; |
|
| 362 | - } |
|
| 363 | - |
|
| 364 | - if (! empty($dtt_id) && ! isset($tickets_sold['datetime'][ $dtt_id ])) { |
|
| 365 | - EE_Error::add_error( |
|
| 366 | - esc_html__( |
|
| 367 | - 'You\'ve requested the amount of tickets sold for a given ticket and datetime, however there are no records for the datetime id you included. Are you SURE that is a datetime related to this ticket?', |
|
| 368 | - 'event_espresso' |
|
| 369 | - ), |
|
| 370 | - __FILE__, |
|
| 371 | - __FUNCTION__, |
|
| 372 | - __LINE__ |
|
| 373 | - ); |
|
| 374 | - return $total; |
|
| 375 | - } |
|
| 376 | - return empty($dtt_id) ? $tickets_sold['datetime'] : $tickets_sold['datetime'][ $dtt_id ]; |
|
| 377 | - } |
|
| 378 | - |
|
| 379 | - |
|
| 380 | - /** |
|
| 381 | - * This returns an array indexed by datetime_id for tickets sold with this ticket. |
|
| 382 | - * |
|
| 383 | - * @return EE_Ticket[] |
|
| 384 | - * @throws EE_Error |
|
| 385 | - * @throws ReflectionException |
|
| 386 | - */ |
|
| 387 | - protected function _all_tickets_sold() |
|
| 388 | - { |
|
| 389 | - $datetimes = $this->get_many_related('Datetime'); |
|
| 390 | - $tickets_sold = []; |
|
| 391 | - if (! empty($datetimes)) { |
|
| 392 | - foreach ($datetimes as $datetime) { |
|
| 393 | - $tickets_sold['datetime'][ $datetime->ID() ] = $datetime->get('DTT_sold'); |
|
| 394 | - } |
|
| 395 | - } |
|
| 396 | - // Tickets sold |
|
| 397 | - $tickets_sold['ticket'] = $this->sold(); |
|
| 398 | - return $tickets_sold; |
|
| 399 | - } |
|
| 400 | - |
|
| 401 | - |
|
| 402 | - /** |
|
| 403 | - * This returns the base price object for the ticket. |
|
| 404 | - * |
|
| 405 | - * @param bool $return_array whether to return as an array indexed by price id or just the object. |
|
| 406 | - * @return EE_Price|EE_Base_Class|EE_Price[]|EE_Base_Class[] |
|
| 407 | - * @throws EE_Error |
|
| 408 | - * @throws ReflectionException |
|
| 409 | - */ |
|
| 410 | - public function base_price($return_array = false) |
|
| 411 | - { |
|
| 412 | - $_where = ['Price_Type.PBT_ID' => EEM_Price_Type::base_type_base_price]; |
|
| 413 | - return $return_array |
|
| 414 | - ? $this->get_many_related('Price', [$_where]) |
|
| 415 | - : $this->get_first_related('Price', [$_where]); |
|
| 416 | - } |
|
| 417 | - |
|
| 418 | - |
|
| 419 | - /** |
|
| 420 | - * This returns ONLY the price modifiers for the ticket (i.e. no taxes or base price) |
|
| 421 | - * |
|
| 422 | - * @access public |
|
| 423 | - * @return EE_Price[] |
|
| 424 | - * @throws EE_Error|ReflectionException |
|
| 425 | - */ |
|
| 426 | - public function price_modifiers() |
|
| 427 | - { |
|
| 428 | - $query_params = [ |
|
| 429 | - 0 => [ |
|
| 430 | - 'Price_Type.PBT_ID' => [ |
|
| 431 | - 'NOT IN', |
|
| 432 | - [EEM_Price_Type::base_type_base_price, EEM_Price_Type::base_type_tax], |
|
| 433 | - ], |
|
| 434 | - ], |
|
| 435 | - ]; |
|
| 436 | - return $this->prices($query_params); |
|
| 437 | - } |
|
| 438 | - |
|
| 439 | - |
|
| 440 | - /** |
|
| 441 | - * Gets all the prices that combine to form the final price of this ticket |
|
| 442 | - * |
|
| 443 | - * @param array $query_params |
|
| 444 | - * @return EE_Price[]|EE_Base_Class[] |
|
| 445 | - * @throws EE_Error |
|
| 446 | - * @throws ReflectionException |
|
| 447 | - * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md |
|
| 448 | - */ |
|
| 449 | - public function prices($query_params = []) |
|
| 450 | - { |
|
| 451 | - return $this->get_many_related('Price', $query_params); |
|
| 452 | - } |
|
| 453 | - |
|
| 454 | - |
|
| 455 | - /** |
|
| 456 | - * Gets all the ticket applicabilities (ie, relations between datetimes and tickets) |
|
| 457 | - * |
|
| 458 | - * @param array $query_params |
|
| 459 | - * @return EE_Datetime_Ticket|EE_Base_Class[] |
|
| 460 | - * @throws EE_Error |
|
| 461 | - * @throws ReflectionException |
|
| 462 | - * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md |
|
| 463 | - */ |
|
| 464 | - public function datetime_tickets($query_params = []) |
|
| 465 | - { |
|
| 466 | - return $this->get_many_related('Datetime_Ticket', $query_params); |
|
| 467 | - } |
|
| 468 | - |
|
| 469 | - |
|
| 470 | - /** |
|
| 471 | - * Gets all the datetimes from the db ordered by DTT_order |
|
| 472 | - * |
|
| 473 | - * @param boolean $show_expired |
|
| 474 | - * @param boolean $show_deleted |
|
| 475 | - * @return EE_Datetime[] |
|
| 476 | - * @throws EE_Error|ReflectionException |
|
| 477 | - */ |
|
| 478 | - public function datetimes_ordered($show_expired = true, $show_deleted = false) |
|
| 479 | - { |
|
| 480 | - return EEM_Datetime::instance($this->_timezone)->get_datetimes_for_ticket_ordered_by_DTT_order( |
|
| 481 | - $this->ID(), |
|
| 482 | - $show_expired, |
|
| 483 | - $show_deleted |
|
| 484 | - ); |
|
| 485 | - } |
|
| 486 | - |
|
| 487 | - |
|
| 488 | - /** |
|
| 489 | - * Gets ID |
|
| 490 | - * |
|
| 491 | - * @return int |
|
| 492 | - * @throws EE_Error |
|
| 493 | - * @throws ReflectionException |
|
| 494 | - */ |
|
| 495 | - public function ID() |
|
| 496 | - { |
|
| 497 | - return (int) $this->get('TKT_ID'); |
|
| 498 | - } |
|
| 499 | - |
|
| 500 | - |
|
| 501 | - /** |
|
| 502 | - * get the author of the ticket. |
|
| 503 | - * |
|
| 504 | - * @return int |
|
| 505 | - * @throws EE_Error |
|
| 506 | - * @throws ReflectionException |
|
| 507 | - * @since 4.5.0 |
|
| 508 | - */ |
|
| 509 | - public function wp_user() |
|
| 510 | - { |
|
| 511 | - return $this->get('TKT_wp_user'); |
|
| 512 | - } |
|
| 513 | - |
|
| 514 | - |
|
| 515 | - /** |
|
| 516 | - * Gets the template for the ticket |
|
| 517 | - * |
|
| 518 | - * @return EE_Ticket_Template|EE_Base_Class |
|
| 519 | - * @throws EE_Error |
|
| 520 | - * @throws ReflectionException |
|
| 521 | - */ |
|
| 522 | - public function template() |
|
| 523 | - { |
|
| 524 | - return $this->get_first_related('Ticket_Template'); |
|
| 525 | - } |
|
| 526 | - |
|
| 527 | - |
|
| 528 | - /** |
|
| 529 | - * Simply returns an array of EE_Price objects that are taxes. |
|
| 530 | - * |
|
| 531 | - * @return EE_Price[] |
|
| 532 | - * @throws EE_Error |
|
| 533 | - */ |
|
| 534 | - public function get_ticket_taxes_for_admin() |
|
| 535 | - { |
|
| 536 | - return EE_Taxes::get_taxes_for_admin(); |
|
| 537 | - } |
|
| 538 | - |
|
| 539 | - |
|
| 540 | - /** |
|
| 541 | - * @return float |
|
| 542 | - * @throws EE_Error |
|
| 543 | - * @throws ReflectionException |
|
| 544 | - */ |
|
| 545 | - public function ticket_price() |
|
| 546 | - { |
|
| 547 | - return $this->get('TKT_price'); |
|
| 548 | - } |
|
| 549 | - |
|
| 550 | - |
|
| 551 | - /** |
|
| 552 | - * @param string|null $schema |
|
| 553 | - * @return mixed |
|
| 554 | - * @throws EE_Error |
|
| 555 | - * @throws ReflectionException |
|
| 556 | - */ |
|
| 557 | - public function pretty_price($schema = 'localized_currency') |
|
| 558 | - { |
|
| 559 | - return $this->get_pretty('TKT_price', $schema); |
|
| 560 | - } |
|
| 561 | - |
|
| 562 | - |
|
| 563 | - /** |
|
| 564 | - * @return bool |
|
| 565 | - * @throws EE_Error|ReflectionException |
|
| 566 | - */ |
|
| 567 | - public function is_free() |
|
| 568 | - { |
|
| 569 | - return $this->get_ticket_total_with_taxes() === (float) 0; |
|
| 570 | - } |
|
| 571 | - |
|
| 572 | - |
|
| 573 | - /** |
|
| 574 | - * get_ticket_total_with_taxes |
|
| 575 | - * |
|
| 576 | - * @param bool $no_cache |
|
| 577 | - * @return float |
|
| 578 | - * @throws EE_Error|ReflectionException |
|
| 579 | - */ |
|
| 580 | - public function get_ticket_total_with_taxes($no_cache = false) |
|
| 581 | - { |
|
| 582 | - if ($this->_ticket_total_with_taxes === null || $no_cache) { |
|
| 583 | - $this->_ticket_total_with_taxes = $this->get_ticket_subtotal() + $this->get_ticket_taxes_total_for_admin(); |
|
| 584 | - } |
|
| 585 | - return (float) $this->_ticket_total_with_taxes; |
|
| 586 | - } |
|
| 587 | - |
|
| 588 | - |
|
| 589 | - /** |
|
| 590 | - * @throws EE_Error |
|
| 591 | - * @throws ReflectionException |
|
| 592 | - */ |
|
| 593 | - public function ensure_TKT_Price_correct() |
|
| 594 | - { |
|
| 595 | - $this->set_price(EE_Taxes::get_subtotal_for_admin($this)); |
|
| 596 | - $this->save(); |
|
| 597 | - } |
|
| 598 | - |
|
| 599 | - |
|
| 600 | - /** |
|
| 601 | - * @return float |
|
| 602 | - * @throws EE_Error|ReflectionException |
|
| 603 | - */ |
|
| 604 | - public function get_ticket_subtotal() |
|
| 605 | - { |
|
| 606 | - return EE_Taxes::get_subtotal_for_admin($this); |
|
| 607 | - } |
|
| 608 | - |
|
| 609 | - |
|
| 610 | - /** |
|
| 611 | - * Returns the total taxes applied to this ticket |
|
| 612 | - * |
|
| 613 | - * @return float |
|
| 614 | - * @throws EE_Error|ReflectionException |
|
| 615 | - */ |
|
| 616 | - public function get_ticket_taxes_total_for_admin() |
|
| 617 | - { |
|
| 618 | - return EE_Taxes::get_total_taxes_for_admin($this); |
|
| 619 | - } |
|
| 620 | - |
|
| 621 | - |
|
| 622 | - /** |
|
| 623 | - * Sets name |
|
| 624 | - * |
|
| 625 | - * @param string $name |
|
| 626 | - * @throws EE_Error |
|
| 627 | - * @throws ReflectionException |
|
| 628 | - */ |
|
| 629 | - public function set_name($name) |
|
| 630 | - { |
|
| 631 | - $this->set('TKT_name', $name); |
|
| 632 | - } |
|
| 633 | - |
|
| 634 | - |
|
| 635 | - /** |
|
| 636 | - * Gets description |
|
| 637 | - * |
|
| 638 | - * @return string |
|
| 639 | - * @throws EE_Error |
|
| 640 | - * @throws ReflectionException |
|
| 641 | - */ |
|
| 642 | - public function description() |
|
| 643 | - { |
|
| 644 | - return $this->get('TKT_description'); |
|
| 645 | - } |
|
| 646 | - |
|
| 647 | - |
|
| 648 | - /** |
|
| 649 | - * Sets description |
|
| 650 | - * |
|
| 651 | - * @param string $description |
|
| 652 | - * @throws EE_Error |
|
| 653 | - * @throws ReflectionException |
|
| 654 | - */ |
|
| 655 | - public function set_description($description) |
|
| 656 | - { |
|
| 657 | - $this->set('TKT_description', $description); |
|
| 658 | - } |
|
| 659 | - |
|
| 660 | - |
|
| 661 | - /** |
|
| 662 | - * Gets start_date |
|
| 663 | - * |
|
| 664 | - * @param string $date_format |
|
| 665 | - * @param string $time_format |
|
| 666 | - * @return string |
|
| 667 | - * @throws EE_Error |
|
| 668 | - * @throws ReflectionException |
|
| 669 | - */ |
|
| 670 | - public function start_date($date_format = '', $time_format = '') |
|
| 671 | - { |
|
| 672 | - return $this->_get_datetime('TKT_start_date', $date_format, $time_format); |
|
| 673 | - } |
|
| 674 | - |
|
| 675 | - |
|
| 676 | - /** |
|
| 677 | - * Sets start_date |
|
| 678 | - * |
|
| 679 | - * @param string $start_date |
|
| 680 | - * @return void |
|
| 681 | - * @throws EE_Error |
|
| 682 | - * @throws ReflectionException |
|
| 683 | - */ |
|
| 684 | - public function set_start_date($start_date) |
|
| 685 | - { |
|
| 686 | - $this->_set_date_time('B', $start_date, 'TKT_start_date'); |
|
| 687 | - } |
|
| 688 | - |
|
| 689 | - |
|
| 690 | - /** |
|
| 691 | - * Gets end_date |
|
| 692 | - * |
|
| 693 | - * @param string $date_format |
|
| 694 | - * @param string $time_format |
|
| 695 | - * @return string |
|
| 696 | - * @throws EE_Error |
|
| 697 | - * @throws ReflectionException |
|
| 698 | - */ |
|
| 699 | - public function end_date($date_format = '', $time_format = '') |
|
| 700 | - { |
|
| 701 | - return $this->_get_datetime('TKT_end_date', $date_format, $time_format); |
|
| 702 | - } |
|
| 703 | - |
|
| 704 | - |
|
| 705 | - /** |
|
| 706 | - * Sets end_date |
|
| 707 | - * |
|
| 708 | - * @param string $end_date |
|
| 709 | - * @return void |
|
| 710 | - * @throws EE_Error |
|
| 711 | - * @throws ReflectionException |
|
| 712 | - */ |
|
| 713 | - public function set_end_date($end_date) |
|
| 714 | - { |
|
| 715 | - $this->_set_date_time('B', $end_date, 'TKT_end_date'); |
|
| 716 | - } |
|
| 717 | - |
|
| 718 | - |
|
| 719 | - /** |
|
| 720 | - * Sets sell until time |
|
| 721 | - * |
|
| 722 | - * @param string $time a string representation of the sell until time (ex 9am or 7:30pm) |
|
| 723 | - * @throws EE_Error |
|
| 724 | - * @throws ReflectionException |
|
| 725 | - * @since 4.5.0 |
|
| 726 | - */ |
|
| 727 | - public function set_end_time($time) |
|
| 728 | - { |
|
| 729 | - $this->_set_time_for($time, 'TKT_end_date'); |
|
| 730 | - } |
|
| 731 | - |
|
| 732 | - |
|
| 733 | - /** |
|
| 734 | - * Sets min |
|
| 735 | - * |
|
| 736 | - * @param int $min |
|
| 737 | - * @return void |
|
| 738 | - * @throws EE_Error |
|
| 739 | - * @throws ReflectionException |
|
| 740 | - */ |
|
| 741 | - public function set_min($min) |
|
| 742 | - { |
|
| 743 | - $this->set('TKT_min', $min); |
|
| 744 | - } |
|
| 745 | - |
|
| 746 | - |
|
| 747 | - /** |
|
| 748 | - * Gets max |
|
| 749 | - * |
|
| 750 | - * @return int |
|
| 751 | - * @throws EE_Error |
|
| 752 | - * @throws ReflectionException |
|
| 753 | - */ |
|
| 754 | - public function max() |
|
| 755 | - { |
|
| 756 | - return $this->get('TKT_max'); |
|
| 757 | - } |
|
| 758 | - |
|
| 759 | - |
|
| 760 | - /** |
|
| 761 | - * Sets max |
|
| 762 | - * |
|
| 763 | - * @param int $max |
|
| 764 | - * @return void |
|
| 765 | - * @throws EE_Error |
|
| 766 | - * @throws ReflectionException |
|
| 767 | - */ |
|
| 768 | - public function set_max($max) |
|
| 769 | - { |
|
| 770 | - $this->set('TKT_max', $max); |
|
| 771 | - } |
|
| 772 | - |
|
| 773 | - |
|
| 774 | - /** |
|
| 775 | - * Sets price |
|
| 776 | - * |
|
| 777 | - * @param float $price |
|
| 778 | - * @return void |
|
| 779 | - * @throws EE_Error |
|
| 780 | - * @throws ReflectionException |
|
| 781 | - */ |
|
| 782 | - public function set_price($price) |
|
| 783 | - { |
|
| 784 | - $this->set('TKT_price', $price); |
|
| 785 | - } |
|
| 786 | - |
|
| 787 | - |
|
| 788 | - /** |
|
| 789 | - * Gets sold |
|
| 790 | - * |
|
| 791 | - * @return int |
|
| 792 | - * @throws EE_Error |
|
| 793 | - * @throws ReflectionException |
|
| 794 | - */ |
|
| 795 | - public function sold() |
|
| 796 | - { |
|
| 797 | - return $this->get_raw('TKT_sold'); |
|
| 798 | - } |
|
| 799 | - |
|
| 800 | - |
|
| 801 | - /** |
|
| 802 | - * Sets sold |
|
| 803 | - * |
|
| 804 | - * @param int $sold |
|
| 805 | - * @return void |
|
| 806 | - * @throws EE_Error |
|
| 807 | - * @throws ReflectionException |
|
| 808 | - */ |
|
| 809 | - public function set_sold($sold) |
|
| 810 | - { |
|
| 811 | - // sold can not go below zero |
|
| 812 | - $sold = max(0, absint($sold)); |
|
| 813 | - $this->set('TKT_sold', $sold); |
|
| 814 | - } |
|
| 815 | - |
|
| 816 | - |
|
| 817 | - /** |
|
| 818 | - * Increments sold by amount passed by $qty AND decrements the reserved count on both this ticket and its |
|
| 819 | - * associated datetimes. |
|
| 820 | - * |
|
| 821 | - * @param int $qty |
|
| 822 | - * @return boolean |
|
| 823 | - * @throws EE_Error |
|
| 824 | - * @throws InvalidArgumentException |
|
| 825 | - * @throws InvalidDataTypeException |
|
| 826 | - * @throws InvalidInterfaceException |
|
| 827 | - * @throws ReflectionException |
|
| 828 | - * @since 4.9.80.p |
|
| 829 | - */ |
|
| 830 | - public function increaseSold($qty = 1) |
|
| 831 | - { |
|
| 832 | - $qty = absint($qty); |
|
| 833 | - // increment sold and decrement reserved datetime quantities simultaneously |
|
| 834 | - // don't worry about failures, because they must have already had a spot reserved |
|
| 835 | - $this->increaseSoldForDatetimes($qty); |
|
| 836 | - // Increment and decrement ticket quantities simultaneously |
|
| 837 | - $success = $this->adjustNumericFieldsInDb( |
|
| 838 | - [ |
|
| 839 | - 'TKT_reserved' => $qty * -1, |
|
| 840 | - 'TKT_sold' => $qty, |
|
| 841 | - ] |
|
| 842 | - ); |
|
| 843 | - do_action( |
|
| 844 | - 'AHEE__EE_Ticket__increase_sold', |
|
| 845 | - $this, |
|
| 846 | - $qty, |
|
| 847 | - $this->sold(), |
|
| 848 | - $success |
|
| 849 | - ); |
|
| 850 | - return $success; |
|
| 851 | - } |
|
| 852 | - |
|
| 853 | - |
|
| 854 | - /** |
|
| 855 | - * On each datetime related to this ticket, increases its sold count and decreases its reserved count by $qty. |
|
| 856 | - * |
|
| 857 | - * @param int $qty positive or negative. Positive means to increase sold counts (and decrease reserved |
|
| 858 | - * counts), Negative means to decreases old counts (and increase reserved counts). |
|
| 859 | - * @param EE_Datetime[] $datetimes |
|
| 860 | - * @throws EE_Error |
|
| 861 | - * @throws InvalidArgumentException |
|
| 862 | - * @throws InvalidDataTypeException |
|
| 863 | - * @throws InvalidInterfaceException |
|
| 864 | - * @throws ReflectionException |
|
| 865 | - * @since 4.9.80.p |
|
| 866 | - */ |
|
| 867 | - protected function increaseSoldForDatetimes($qty, array $datetimes = []) |
|
| 868 | - { |
|
| 869 | - $datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes(); |
|
| 870 | - foreach ($datetimes as $datetime) { |
|
| 871 | - $datetime->increaseSold($qty); |
|
| 872 | - } |
|
| 873 | - } |
|
| 874 | - |
|
| 875 | - |
|
| 876 | - /** |
|
| 877 | - * Decrements (subtracts) sold by amount passed by $qty on both the ticket and its related datetimes directly in the |
|
| 878 | - * DB and then updates the model objects. |
|
| 879 | - * Does not affect the reserved counts. |
|
| 880 | - * |
|
| 881 | - * @param int $qty |
|
| 882 | - * @return boolean |
|
| 883 | - * @throws EE_Error |
|
| 884 | - * @throws InvalidArgumentException |
|
| 885 | - * @throws InvalidDataTypeException |
|
| 886 | - * @throws InvalidInterfaceException |
|
| 887 | - * @throws ReflectionException |
|
| 888 | - * @since 4.9.80.p |
|
| 889 | - */ |
|
| 890 | - public function decreaseSold($qty = 1) |
|
| 891 | - { |
|
| 892 | - $qty = absint($qty); |
|
| 893 | - $this->decreaseSoldForDatetimes($qty); |
|
| 894 | - $success = $this->adjustNumericFieldsInDb( |
|
| 895 | - [ |
|
| 896 | - 'TKT_sold' => $qty * -1, |
|
| 897 | - ] |
|
| 898 | - ); |
|
| 899 | - do_action( |
|
| 900 | - 'AHEE__EE_Ticket__decrease_sold', |
|
| 901 | - $this, |
|
| 902 | - $qty, |
|
| 903 | - $this->sold(), |
|
| 904 | - $success |
|
| 905 | - ); |
|
| 906 | - return $success; |
|
| 907 | - } |
|
| 908 | - |
|
| 909 | - |
|
| 910 | - /** |
|
| 911 | - * Decreases sold on related datetimes |
|
| 912 | - * |
|
| 913 | - * @param int $qty |
|
| 914 | - * @param EE_Datetime[] $datetimes |
|
| 915 | - * @return void |
|
| 916 | - * @throws EE_Error |
|
| 917 | - * @throws InvalidArgumentException |
|
| 918 | - * @throws InvalidDataTypeException |
|
| 919 | - * @throws InvalidInterfaceException |
|
| 920 | - * @throws ReflectionException |
|
| 921 | - * @since 4.9.80.p |
|
| 922 | - */ |
|
| 923 | - protected function decreaseSoldForDatetimes($qty = 1, array $datetimes = []) |
|
| 924 | - { |
|
| 925 | - $datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes(); |
|
| 926 | - if (is_array($datetimes)) { |
|
| 927 | - foreach ($datetimes as $datetime) { |
|
| 928 | - if ($datetime instanceof EE_Datetime) { |
|
| 929 | - $datetime->decreaseSold($qty); |
|
| 930 | - } |
|
| 931 | - } |
|
| 932 | - } |
|
| 933 | - } |
|
| 934 | - |
|
| 935 | - |
|
| 936 | - /** |
|
| 937 | - * Gets qty of reserved tickets |
|
| 938 | - * |
|
| 939 | - * @return int |
|
| 940 | - * @throws EE_Error |
|
| 941 | - * @throws ReflectionException |
|
| 942 | - */ |
|
| 943 | - public function reserved() |
|
| 944 | - { |
|
| 945 | - return $this->get_raw('TKT_reserved'); |
|
| 946 | - } |
|
| 947 | - |
|
| 948 | - |
|
| 949 | - /** |
|
| 950 | - * Sets reserved |
|
| 951 | - * |
|
| 952 | - * @param int $reserved |
|
| 953 | - * @return void |
|
| 954 | - * @throws EE_Error |
|
| 955 | - * @throws ReflectionException |
|
| 956 | - */ |
|
| 957 | - public function set_reserved($reserved) |
|
| 958 | - { |
|
| 959 | - // reserved can not go below zero |
|
| 960 | - $reserved = max(0, (int) $reserved); |
|
| 961 | - $this->set('TKT_reserved', $reserved); |
|
| 962 | - } |
|
| 963 | - |
|
| 964 | - |
|
| 965 | - /** |
|
| 966 | - * Increments reserved by amount passed by $qty, and persists it immediately to the database. |
|
| 967 | - * |
|
| 968 | - * @param int $qty |
|
| 969 | - * @param string $source |
|
| 970 | - * @return bool whether we successfully reserved the ticket or not. |
|
| 971 | - * @throws EE_Error |
|
| 972 | - * @throws InvalidArgumentException |
|
| 973 | - * @throws ReflectionException |
|
| 974 | - * @throws InvalidDataTypeException |
|
| 975 | - * @throws InvalidInterfaceException |
|
| 976 | - * @since 4.9.80.p |
|
| 977 | - */ |
|
| 978 | - public function increaseReserved($qty = 1, $source = 'unknown') |
|
| 979 | - { |
|
| 980 | - $qty = absint($qty); |
|
| 981 | - do_action( |
|
| 982 | - 'AHEE__EE_Ticket__increase_reserved__begin', |
|
| 983 | - $this, |
|
| 984 | - $qty, |
|
| 985 | - $source |
|
| 986 | - ); |
|
| 987 | - $this->add_extra_meta(EE_Ticket::META_KEY_TICKET_RESERVATIONS, "{$qty} from {$source}"); |
|
| 988 | - $success = false; |
|
| 989 | - $datetimes_adjusted_successfully = $this->increaseReservedForDatetimes($qty); |
|
| 990 | - if ($datetimes_adjusted_successfully) { |
|
| 991 | - $success = $this->incrementFieldConditionallyInDb( |
|
| 992 | - 'TKT_reserved', |
|
| 993 | - 'TKT_sold', |
|
| 994 | - 'TKT_qty', |
|
| 995 | - $qty |
|
| 996 | - ); |
|
| 997 | - if (! $success) { |
|
| 998 | - // The datetimes were successfully bumped, but not the |
|
| 999 | - // ticket. So we need to manually rollback the datetimes. |
|
| 1000 | - $this->decreaseReservedForDatetimes($qty); |
|
| 1001 | - } |
|
| 1002 | - } |
|
| 1003 | - do_action( |
|
| 1004 | - 'AHEE__EE_Ticket__increase_reserved', |
|
| 1005 | - $this, |
|
| 1006 | - $qty, |
|
| 1007 | - $this->reserved(), |
|
| 1008 | - $success |
|
| 1009 | - ); |
|
| 1010 | - return $success; |
|
| 1011 | - } |
|
| 1012 | - |
|
| 1013 | - |
|
| 1014 | - /** |
|
| 1015 | - * Increases reserved counts on related datetimes |
|
| 1016 | - * |
|
| 1017 | - * @param int $qty |
|
| 1018 | - * @param EE_Datetime[] $datetimes |
|
| 1019 | - * @return boolean indicating success |
|
| 1020 | - * @throws EE_Error |
|
| 1021 | - * @throws InvalidArgumentException |
|
| 1022 | - * @throws InvalidDataTypeException |
|
| 1023 | - * @throws InvalidInterfaceException |
|
| 1024 | - * @throws ReflectionException |
|
| 1025 | - * @since 4.9.80.p |
|
| 1026 | - */ |
|
| 1027 | - protected function increaseReservedForDatetimes($qty = 1, array $datetimes = []) |
|
| 1028 | - { |
|
| 1029 | - $datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes(); |
|
| 1030 | - $datetimes_updated = []; |
|
| 1031 | - $limit_exceeded = false; |
|
| 1032 | - if (is_array($datetimes)) { |
|
| 1033 | - foreach ($datetimes as $datetime) { |
|
| 1034 | - if ($datetime instanceof EE_Datetime) { |
|
| 1035 | - if ($datetime->increaseReserved($qty)) { |
|
| 1036 | - $datetimes_updated[] = $datetime; |
|
| 1037 | - } else { |
|
| 1038 | - $limit_exceeded = true; |
|
| 1039 | - break; |
|
| 1040 | - } |
|
| 1041 | - } |
|
| 1042 | - } |
|
| 1043 | - // If somewhere along the way we detected a datetime whose |
|
| 1044 | - // limit was exceeded, do a manual rollback. |
|
| 1045 | - if ($limit_exceeded) { |
|
| 1046 | - $this->decreaseReservedForDatetimes($qty, $datetimes_updated); |
|
| 1047 | - return false; |
|
| 1048 | - } |
|
| 1049 | - } |
|
| 1050 | - return true; |
|
| 1051 | - } |
|
| 1052 | - |
|
| 1053 | - |
|
| 1054 | - /** |
|
| 1055 | - * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database. |
|
| 1056 | - * |
|
| 1057 | - * @param int $qty |
|
| 1058 | - * @param bool $adjust_datetimes |
|
| 1059 | - * @param string $source |
|
| 1060 | - * @return boolean |
|
| 1061 | - * @throws EE_Error |
|
| 1062 | - * @throws InvalidArgumentException |
|
| 1063 | - * @throws ReflectionException |
|
| 1064 | - * @throws InvalidDataTypeException |
|
| 1065 | - * @throws InvalidInterfaceException |
|
| 1066 | - * @since 4.9.80.p |
|
| 1067 | - */ |
|
| 1068 | - public function decreaseReserved($qty = 1, $adjust_datetimes = true, $source = 'unknown') |
|
| 1069 | - { |
|
| 1070 | - $qty = absint($qty); |
|
| 1071 | - $this->add_extra_meta(EE_Ticket::META_KEY_TICKET_RESERVATIONS, "-{$qty} from {$source}"); |
|
| 1072 | - if ($adjust_datetimes) { |
|
| 1073 | - $this->decreaseReservedForDatetimes($qty); |
|
| 1074 | - } |
|
| 1075 | - $success = $this->adjustNumericFieldsInDb( |
|
| 1076 | - [ |
|
| 1077 | - 'TKT_reserved' => $qty * -1, |
|
| 1078 | - ] |
|
| 1079 | - ); |
|
| 1080 | - do_action( |
|
| 1081 | - 'AHEE__EE_Ticket__decrease_reserved', |
|
| 1082 | - $this, |
|
| 1083 | - $qty, |
|
| 1084 | - $this->reserved(), |
|
| 1085 | - $success |
|
| 1086 | - ); |
|
| 1087 | - return $success; |
|
| 1088 | - } |
|
| 1089 | - |
|
| 1090 | - |
|
| 1091 | - /** |
|
| 1092 | - * Decreases the reserved count on the specified datetimes. |
|
| 1093 | - * |
|
| 1094 | - * @param int $qty |
|
| 1095 | - * @param EE_Datetime[] $datetimes |
|
| 1096 | - * @throws EE_Error |
|
| 1097 | - * @throws InvalidArgumentException |
|
| 1098 | - * @throws ReflectionException |
|
| 1099 | - * @throws InvalidDataTypeException |
|
| 1100 | - * @throws InvalidInterfaceException |
|
| 1101 | - * @since 4.9.80.p |
|
| 1102 | - */ |
|
| 1103 | - protected function decreaseReservedForDatetimes($qty = 1, array $datetimes = []) |
|
| 1104 | - { |
|
| 1105 | - $datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes(); |
|
| 1106 | - foreach ($datetimes as $datetime) { |
|
| 1107 | - if ($datetime instanceof EE_Datetime) { |
|
| 1108 | - $datetime->decreaseReserved($qty); |
|
| 1109 | - } |
|
| 1110 | - } |
|
| 1111 | - } |
|
| 1112 | - |
|
| 1113 | - |
|
| 1114 | - /** |
|
| 1115 | - * Gets ticket quantity |
|
| 1116 | - * |
|
| 1117 | - * @param string $context ticket quantity is somewhat subjective depending on the exact information sought |
|
| 1118 | - * therefore $context can be one of three values: '', 'reg_limit', or 'saleable' |
|
| 1119 | - * '' (default) quantity is the actual db value for TKT_qty, unaffected by other objects |
|
| 1120 | - * REG LIMIT: caps qty based on DTT_reg_limit for ALL related datetimes |
|
| 1121 | - * SALEABLE: also considers datetime sold and returns zero if ANY DTT is sold out, and |
|
| 1122 | - * is therefore the truest measure of tickets that can be purchased at the moment |
|
| 1123 | - * @return int |
|
| 1124 | - * @throws EE_Error |
|
| 1125 | - * @throws ReflectionException |
|
| 1126 | - */ |
|
| 1127 | - public function qty($context = '') |
|
| 1128 | - { |
|
| 1129 | - switch ($context) { |
|
| 1130 | - case 'reg_limit': |
|
| 1131 | - return $this->real_quantity_on_ticket(); |
|
| 1132 | - case 'saleable': |
|
| 1133 | - return $this->real_quantity_on_ticket('saleable'); |
|
| 1134 | - default: |
|
| 1135 | - return $this->get_raw('TKT_qty'); |
|
| 1136 | - } |
|
| 1137 | - } |
|
| 1138 | - |
|
| 1139 | - |
|
| 1140 | - /** |
|
| 1141 | - * Gets ticket quantity |
|
| 1142 | - * |
|
| 1143 | - * @param string $context ticket quantity is somewhat subjective depending on the exact information sought |
|
| 1144 | - * therefore $context can be one of two values: 'reg_limit', or 'saleable' |
|
| 1145 | - * REG LIMIT: caps qty based on DTT_reg_limit for ALL related datetimes |
|
| 1146 | - * SALEABLE: also considers datetime sold and returns zero if ANY DTT is sold out, and |
|
| 1147 | - * is therefore the truest measure of tickets that can be purchased at the moment |
|
| 1148 | - * @param int $DTT_ID the primary key for a particular datetime. |
|
| 1149 | - * set to 0 for all related datetimes |
|
| 1150 | - * @return int |
|
| 1151 | - * @throws EE_Error |
|
| 1152 | - * @throws ReflectionException |
|
| 1153 | - */ |
|
| 1154 | - public function real_quantity_on_ticket($context = 'reg_limit', $DTT_ID = 0) |
|
| 1155 | - { |
|
| 1156 | - $raw = $this->get_raw('TKT_qty'); |
|
| 1157 | - // return immediately if it's zero |
|
| 1158 | - if ($raw === 0) { |
|
| 1159 | - return $raw; |
|
| 1160 | - } |
|
| 1161 | - // echo "\n\n<br />Ticket: " . $this->name() . '<br />'; |
|
| 1162 | - // ensure qty doesn't exceed raw value for THIS ticket |
|
| 1163 | - $qty = min(EE_INF, $raw); |
|
| 1164 | - // echo "\n . qty: " . $qty . '<br />'; |
|
| 1165 | - // calculate this ticket's total sales and reservations |
|
| 1166 | - $sold_and_reserved_for_this_ticket = $this->sold() + $this->reserved(); |
|
| 1167 | - // echo "\n . sold: " . $this->sold() . '<br />'; |
|
| 1168 | - // echo "\n . reserved: " . $this->reserved() . '<br />'; |
|
| 1169 | - // echo "\n . sold_and_reserved_for_this_ticket: " . $sold_and_reserved_for_this_ticket . '<br />'; |
|
| 1170 | - // first we need to calculate the maximum number of tickets available for the datetime |
|
| 1171 | - // do we want data for one datetime or all of them ? |
|
| 1172 | - $query_params = $DTT_ID ? [['DTT_ID' => $DTT_ID]] : []; |
|
| 1173 | - $datetimes = $this->datetimes($query_params); |
|
| 1174 | - if (is_array($datetimes) && ! empty($datetimes)) { |
|
| 1175 | - foreach ($datetimes as $datetime) { |
|
| 1176 | - if ($datetime instanceof EE_Datetime) { |
|
| 1177 | - $datetime->refresh_from_db(); |
|
| 1178 | - // echo "\n . . datetime name: " . $datetime->name() . '<br />'; |
|
| 1179 | - // echo "\n . . datetime ID: " . $datetime->ID() . '<br />'; |
|
| 1180 | - // initialize with no restrictions for each datetime |
|
| 1181 | - // but adjust datetime qty based on datetime reg limit |
|
| 1182 | - $datetime_qty = min(EE_INF, $datetime->reg_limit()); |
|
| 1183 | - // echo "\n . . . datetime reg_limit: " . $datetime->reg_limit() . '<br />'; |
|
| 1184 | - // echo "\n . . . datetime_qty: " . $datetime_qty . '<br />'; |
|
| 1185 | - // if we want the actual saleable amount, then we need to consider OTHER ticket sales |
|
| 1186 | - // and reservations for this datetime, that do NOT include sales and reservations |
|
| 1187 | - // for this ticket (so we add $this->sold() and $this->reserved() back in) |
|
| 1188 | - if ($context === 'saleable') { |
|
| 1189 | - $datetime_qty = max( |
|
| 1190 | - $datetime_qty - $datetime->sold_and_reserved() + $sold_and_reserved_for_this_ticket, |
|
| 1191 | - 0 |
|
| 1192 | - ); |
|
| 1193 | - // echo "\n . . . datetime sold: " . $datetime->sold() . '<br />'; |
|
| 1194 | - // echo "\n . . . datetime reserved: " . $datetime->reserved() . '<br />'; |
|
| 1195 | - // echo "\n . . . datetime sold_and_reserved: " . $datetime->sold_and_reserved() . '<br />'; |
|
| 1196 | - // echo "\n . . . datetime_qty: " . $datetime_qty . '<br />'; |
|
| 1197 | - $datetime_qty = ! $datetime->sold_out() ? $datetime_qty : 0; |
|
| 1198 | - // echo "\n . . . datetime_qty: " . $datetime_qty . '<br />'; |
|
| 1199 | - } |
|
| 1200 | - $qty = min($datetime_qty, $qty); |
|
| 1201 | - // echo "\n . . qty: " . $qty . '<br />'; |
|
| 1202 | - } |
|
| 1203 | - } |
|
| 1204 | - } |
|
| 1205 | - // NOW that we know the maximum number of tickets available for the datetime |
|
| 1206 | - // we can finally factor in the details for this specific ticket |
|
| 1207 | - if ($qty > 0 && $context === 'saleable') { |
|
| 1208 | - // and subtract the sales for THIS ticket |
|
| 1209 | - $qty = max($qty - $sold_and_reserved_for_this_ticket, 0); |
|
| 1210 | - // echo "\n . qty: " . $qty . '<br />'; |
|
| 1211 | - } |
|
| 1212 | - // echo "\nFINAL QTY: " . $qty . "<br /><br />"; |
|
| 1213 | - return $qty; |
|
| 1214 | - } |
|
| 1215 | - |
|
| 1216 | - |
|
| 1217 | - /** |
|
| 1218 | - * Sets qty - IMPORTANT!!! Does NOT allow QTY to be set higher than the lowest reg limit of any related datetimes |
|
| 1219 | - * |
|
| 1220 | - * @param int $qty |
|
| 1221 | - * @return void |
|
| 1222 | - * @throws EE_Error |
|
| 1223 | - * @throws ReflectionException |
|
| 1224 | - */ |
|
| 1225 | - public function set_qty($qty) |
|
| 1226 | - { |
|
| 1227 | - $datetimes = $this->datetimes(); |
|
| 1228 | - foreach ($datetimes as $datetime) { |
|
| 1229 | - if ($datetime instanceof EE_Datetime) { |
|
| 1230 | - $qty = min($qty, $datetime->reg_limit()); |
|
| 1231 | - } |
|
| 1232 | - } |
|
| 1233 | - $this->set('TKT_qty', $qty); |
|
| 1234 | - } |
|
| 1235 | - |
|
| 1236 | - |
|
| 1237 | - /** |
|
| 1238 | - * Gets uses |
|
| 1239 | - * |
|
| 1240 | - * @return int |
|
| 1241 | - * @throws EE_Error |
|
| 1242 | - * @throws ReflectionException |
|
| 1243 | - */ |
|
| 1244 | - public function uses() |
|
| 1245 | - { |
|
| 1246 | - return $this->get('TKT_uses'); |
|
| 1247 | - } |
|
| 1248 | - |
|
| 1249 | - |
|
| 1250 | - /** |
|
| 1251 | - * Sets uses |
|
| 1252 | - * |
|
| 1253 | - * @param int $uses |
|
| 1254 | - * @return void |
|
| 1255 | - * @throws EE_Error |
|
| 1256 | - * @throws ReflectionException |
|
| 1257 | - */ |
|
| 1258 | - public function set_uses($uses) |
|
| 1259 | - { |
|
| 1260 | - $this->set('TKT_uses', $uses); |
|
| 1261 | - } |
|
| 1262 | - |
|
| 1263 | - |
|
| 1264 | - /** |
|
| 1265 | - * returns whether ticket is required or not. |
|
| 1266 | - * |
|
| 1267 | - * @return boolean |
|
| 1268 | - * @throws EE_Error |
|
| 1269 | - * @throws ReflectionException |
|
| 1270 | - */ |
|
| 1271 | - public function required() |
|
| 1272 | - { |
|
| 1273 | - return $this->get('TKT_required'); |
|
| 1274 | - } |
|
| 1275 | - |
|
| 1276 | - |
|
| 1277 | - /** |
|
| 1278 | - * sets the TKT_required property |
|
| 1279 | - * |
|
| 1280 | - * @param boolean $required |
|
| 1281 | - * @return void |
|
| 1282 | - * @throws EE_Error |
|
| 1283 | - * @throws ReflectionException |
|
| 1284 | - */ |
|
| 1285 | - public function set_required($required) |
|
| 1286 | - { |
|
| 1287 | - $this->set('TKT_required', $required); |
|
| 1288 | - } |
|
| 1289 | - |
|
| 1290 | - |
|
| 1291 | - /** |
|
| 1292 | - * Gets taxable |
|
| 1293 | - * |
|
| 1294 | - * @return boolean |
|
| 1295 | - * @throws EE_Error |
|
| 1296 | - * @throws ReflectionException |
|
| 1297 | - */ |
|
| 1298 | - public function taxable() |
|
| 1299 | - { |
|
| 1300 | - return $this->get('TKT_taxable'); |
|
| 1301 | - } |
|
| 1302 | - |
|
| 1303 | - |
|
| 1304 | - /** |
|
| 1305 | - * Sets taxable |
|
| 1306 | - * |
|
| 1307 | - * @param boolean $taxable |
|
| 1308 | - * @return void |
|
| 1309 | - * @throws EE_Error |
|
| 1310 | - * @throws ReflectionException |
|
| 1311 | - */ |
|
| 1312 | - public function set_taxable($taxable) |
|
| 1313 | - { |
|
| 1314 | - $this->set('TKT_taxable', $taxable); |
|
| 1315 | - } |
|
| 1316 | - |
|
| 1317 | - |
|
| 1318 | - /** |
|
| 1319 | - * Gets is_default |
|
| 1320 | - * |
|
| 1321 | - * @return boolean |
|
| 1322 | - * @throws EE_Error |
|
| 1323 | - * @throws ReflectionException |
|
| 1324 | - */ |
|
| 1325 | - public function is_default() |
|
| 1326 | - { |
|
| 1327 | - return $this->get('TKT_is_default'); |
|
| 1328 | - } |
|
| 1329 | - |
|
| 1330 | - |
|
| 1331 | - /** |
|
| 1332 | - * Sets is_default |
|
| 1333 | - * |
|
| 1334 | - * @param boolean $is_default |
|
| 1335 | - * @return void |
|
| 1336 | - * @throws EE_Error |
|
| 1337 | - * @throws ReflectionException |
|
| 1338 | - */ |
|
| 1339 | - public function set_is_default($is_default) |
|
| 1340 | - { |
|
| 1341 | - $this->set('TKT_is_default', $is_default); |
|
| 1342 | - } |
|
| 1343 | - |
|
| 1344 | - |
|
| 1345 | - /** |
|
| 1346 | - * Gets order |
|
| 1347 | - * |
|
| 1348 | - * @return int |
|
| 1349 | - * @throws EE_Error |
|
| 1350 | - * @throws ReflectionException |
|
| 1351 | - */ |
|
| 1352 | - public function order() |
|
| 1353 | - { |
|
| 1354 | - return $this->get('TKT_order'); |
|
| 1355 | - } |
|
| 1356 | - |
|
| 1357 | - |
|
| 1358 | - /** |
|
| 1359 | - * Sets order |
|
| 1360 | - * |
|
| 1361 | - * @param int $order |
|
| 1362 | - * @return void |
|
| 1363 | - * @throws EE_Error |
|
| 1364 | - * @throws ReflectionException |
|
| 1365 | - */ |
|
| 1366 | - public function set_order($order) |
|
| 1367 | - { |
|
| 1368 | - $this->set('TKT_order', $order); |
|
| 1369 | - } |
|
| 1370 | - |
|
| 1371 | - |
|
| 1372 | - /** |
|
| 1373 | - * Gets row |
|
| 1374 | - * |
|
| 1375 | - * @return int |
|
| 1376 | - * @throws EE_Error |
|
| 1377 | - * @throws ReflectionException |
|
| 1378 | - */ |
|
| 1379 | - public function row() |
|
| 1380 | - { |
|
| 1381 | - return $this->get('TKT_row'); |
|
| 1382 | - } |
|
| 1383 | - |
|
| 1384 | - |
|
| 1385 | - /** |
|
| 1386 | - * Sets row |
|
| 1387 | - * |
|
| 1388 | - * @param int $row |
|
| 1389 | - * @return void |
|
| 1390 | - * @throws EE_Error |
|
| 1391 | - * @throws ReflectionException |
|
| 1392 | - */ |
|
| 1393 | - public function set_row($row) |
|
| 1394 | - { |
|
| 1395 | - $this->set('TKT_row', $row); |
|
| 1396 | - } |
|
| 1397 | - |
|
| 1398 | - |
|
| 1399 | - /** |
|
| 1400 | - * Gets deleted |
|
| 1401 | - * |
|
| 1402 | - * @return boolean |
|
| 1403 | - * @throws EE_Error |
|
| 1404 | - * @throws ReflectionException |
|
| 1405 | - */ |
|
| 1406 | - public function deleted() |
|
| 1407 | - { |
|
| 1408 | - return $this->get('TKT_deleted'); |
|
| 1409 | - } |
|
| 1410 | - |
|
| 1411 | - |
|
| 1412 | - /** |
|
| 1413 | - * Sets deleted |
|
| 1414 | - * |
|
| 1415 | - * @param boolean $deleted |
|
| 1416 | - * @return void |
|
| 1417 | - * @throws EE_Error |
|
| 1418 | - * @throws ReflectionException |
|
| 1419 | - */ |
|
| 1420 | - public function set_deleted($deleted) |
|
| 1421 | - { |
|
| 1422 | - $this->set('TKT_deleted', $deleted); |
|
| 1423 | - } |
|
| 1424 | - |
|
| 1425 | - |
|
| 1426 | - /** |
|
| 1427 | - * Gets parent |
|
| 1428 | - * |
|
| 1429 | - * @return int |
|
| 1430 | - * @throws EE_Error |
|
| 1431 | - * @throws ReflectionException |
|
| 1432 | - */ |
|
| 1433 | - public function parent_ID() |
|
| 1434 | - { |
|
| 1435 | - return $this->get('TKT_parent'); |
|
| 1436 | - } |
|
| 1437 | - |
|
| 1438 | - |
|
| 1439 | - /** |
|
| 1440 | - * Sets parent |
|
| 1441 | - * |
|
| 1442 | - * @param int $parent |
|
| 1443 | - * @return void |
|
| 1444 | - * @throws EE_Error |
|
| 1445 | - * @throws ReflectionException |
|
| 1446 | - */ |
|
| 1447 | - public function set_parent_ID($parent) |
|
| 1448 | - { |
|
| 1449 | - $this->set('TKT_parent', $parent); |
|
| 1450 | - } |
|
| 1451 | - |
|
| 1452 | - |
|
| 1453 | - /** |
|
| 1454 | - * Gets a string which is handy for showing in gateways etc that describes the ticket. |
|
| 1455 | - * |
|
| 1456 | - * @return string |
|
| 1457 | - * @throws EE_Error |
|
| 1458 | - * @throws ReflectionException |
|
| 1459 | - */ |
|
| 1460 | - public function name_and_info() |
|
| 1461 | - { |
|
| 1462 | - $times = []; |
|
| 1463 | - foreach ($this->datetimes() as $datetime) { |
|
| 1464 | - $times[] = $datetime->start_date_and_time(); |
|
| 1465 | - } |
|
| 1466 | - return $this->name() . ' @ ' . implode(', ', $times) . ' for ' . $this->pretty_price(); |
|
| 1467 | - } |
|
| 1468 | - |
|
| 1469 | - |
|
| 1470 | - /** |
|
| 1471 | - * Gets name |
|
| 1472 | - * |
|
| 1473 | - * @return string |
|
| 1474 | - * @throws EE_Error |
|
| 1475 | - * @throws ReflectionException |
|
| 1476 | - */ |
|
| 1477 | - public function name() |
|
| 1478 | - { |
|
| 1479 | - return $this->get('TKT_name'); |
|
| 1480 | - } |
|
| 1481 | - |
|
| 1482 | - |
|
| 1483 | - /** |
|
| 1484 | - * Gets price |
|
| 1485 | - * |
|
| 1486 | - * @return float |
|
| 1487 | - * @throws EE_Error |
|
| 1488 | - * @throws ReflectionException |
|
| 1489 | - */ |
|
| 1490 | - public function price() |
|
| 1491 | - { |
|
| 1492 | - return $this->get('TKT_price'); |
|
| 1493 | - } |
|
| 1494 | - |
|
| 1495 | - |
|
| 1496 | - /** |
|
| 1497 | - * Gets all the registrations for this ticket |
|
| 1498 | - * |
|
| 1499 | - * @param array $query_params |
|
| 1500 | - * @return EE_Registration[]|EE_Base_Class[] |
|
| 1501 | - * @throws EE_Error |
|
| 1502 | - * @throws ReflectionException |
|
| 1503 | - * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md |
|
| 1504 | - */ |
|
| 1505 | - public function registrations($query_params = []) |
|
| 1506 | - { |
|
| 1507 | - return $this->get_many_related('Registration', $query_params); |
|
| 1508 | - } |
|
| 1509 | - |
|
| 1510 | - |
|
| 1511 | - /** |
|
| 1512 | - * Updates the TKT_sold attribute (and saves) based on the number of APPROVED registrations for this ticket. |
|
| 1513 | - * |
|
| 1514 | - * @return int |
|
| 1515 | - * @throws EE_Error |
|
| 1516 | - * @throws ReflectionException |
|
| 1517 | - */ |
|
| 1518 | - public function update_tickets_sold() |
|
| 1519 | - { |
|
| 1520 | - $count_regs_for_this_ticket = $this->count_registrations( |
|
| 1521 | - [ |
|
| 1522 | - [ |
|
| 1523 | - 'STS_ID' => EEM_Registration::status_id_approved, |
|
| 1524 | - 'REG_deleted' => 0, |
|
| 1525 | - ], |
|
| 1526 | - ] |
|
| 1527 | - ); |
|
| 1528 | - $this->set_sold($count_regs_for_this_ticket); |
|
| 1529 | - $this->save(); |
|
| 1530 | - return $count_regs_for_this_ticket; |
|
| 1531 | - } |
|
| 1532 | - |
|
| 1533 | - |
|
| 1534 | - /** |
|
| 1535 | - * Counts the registrations for this ticket |
|
| 1536 | - * |
|
| 1537 | - * @param array $query_params |
|
| 1538 | - * @return int |
|
| 1539 | - * @throws EE_Error |
|
| 1540 | - * @throws ReflectionException |
|
| 1541 | - * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md |
|
| 1542 | - */ |
|
| 1543 | - public function count_registrations($query_params = []) |
|
| 1544 | - { |
|
| 1545 | - return $this->count_related('Registration', $query_params); |
|
| 1546 | - } |
|
| 1547 | - |
|
| 1548 | - |
|
| 1549 | - /** |
|
| 1550 | - * Implementation for EEI_Has_Icon interface method. |
|
| 1551 | - * |
|
| 1552 | - * @return string |
|
| 1553 | - * @see EEI_Visual_Representation for comments |
|
| 1554 | - */ |
|
| 1555 | - public function get_icon() |
|
| 1556 | - { |
|
| 1557 | - return '<span class="dashicons dashicons-tickets-alt"></span>'; |
|
| 1558 | - } |
|
| 1559 | - |
|
| 1560 | - |
|
| 1561 | - /** |
|
| 1562 | - * Implementation of the EEI_Event_Relation interface method |
|
| 1563 | - * |
|
| 1564 | - * @return EE_Event |
|
| 1565 | - * @throws EE_Error |
|
| 1566 | - * @throws UnexpectedEntityException |
|
| 1567 | - * @throws ReflectionException |
|
| 1568 | - * @see EEI_Event_Relation for comments |
|
| 1569 | - */ |
|
| 1570 | - public function get_related_event() |
|
| 1571 | - { |
|
| 1572 | - // get one datetime to use for getting the event |
|
| 1573 | - $datetime = $this->first_datetime(); |
|
| 1574 | - if (! $datetime instanceof EE_Datetime) { |
|
| 1575 | - throw new UnexpectedEntityException( |
|
| 1576 | - $datetime, |
|
| 1577 | - 'EE_Datetime', |
|
| 1578 | - sprintf( |
|
| 1579 | - esc_html__('The ticket (%s) is not associated with any valid datetimes.', 'event_espresso'), |
|
| 1580 | - $this->name() |
|
| 1581 | - ) |
|
| 1582 | - ); |
|
| 1583 | - } |
|
| 1584 | - $event = $datetime->event(); |
|
| 1585 | - if (! $event instanceof EE_Event) { |
|
| 1586 | - throw new UnexpectedEntityException( |
|
| 1587 | - $event, |
|
| 1588 | - 'EE_Event', |
|
| 1589 | - sprintf( |
|
| 1590 | - esc_html__('The ticket (%s) is not associated with a valid event.', 'event_espresso'), |
|
| 1591 | - $this->name() |
|
| 1592 | - ) |
|
| 1593 | - ); |
|
| 1594 | - } |
|
| 1595 | - return $event; |
|
| 1596 | - } |
|
| 1597 | - |
|
| 1598 | - |
|
| 1599 | - /** |
|
| 1600 | - * Implementation of the EEI_Event_Relation interface method |
|
| 1601 | - * |
|
| 1602 | - * @return string |
|
| 1603 | - * @throws UnexpectedEntityException |
|
| 1604 | - * @throws EE_Error |
|
| 1605 | - * @throws ReflectionException |
|
| 1606 | - * @see EEI_Event_Relation for comments |
|
| 1607 | - */ |
|
| 1608 | - public function get_event_name() |
|
| 1609 | - { |
|
| 1610 | - $event = $this->get_related_event(); |
|
| 1611 | - return $event instanceof EE_Event ? $event->name() : ''; |
|
| 1612 | - } |
|
| 1613 | - |
|
| 1614 | - |
|
| 1615 | - /** |
|
| 1616 | - * Implementation of the EEI_Event_Relation interface method |
|
| 1617 | - * |
|
| 1618 | - * @return int |
|
| 1619 | - * @throws UnexpectedEntityException |
|
| 1620 | - * @throws EE_Error |
|
| 1621 | - * @throws ReflectionException |
|
| 1622 | - * @see EEI_Event_Relation for comments |
|
| 1623 | - */ |
|
| 1624 | - public function get_event_ID() |
|
| 1625 | - { |
|
| 1626 | - $event = $this->get_related_event(); |
|
| 1627 | - return $event instanceof EE_Event ? $event->ID() : 0; |
|
| 1628 | - } |
|
| 1629 | - |
|
| 1630 | - |
|
| 1631 | - /** |
|
| 1632 | - * This simply returns whether a ticket can be permanently deleted or not. |
|
| 1633 | - * The criteria for determining this is whether the ticket has any related registrations. |
|
| 1634 | - * If there are none then it can be permanently deleted. |
|
| 1635 | - * |
|
| 1636 | - * @return bool |
|
| 1637 | - * @throws EE_Error |
|
| 1638 | - * @throws ReflectionException |
|
| 1639 | - */ |
|
| 1640 | - public function is_permanently_deleteable() |
|
| 1641 | - { |
|
| 1642 | - return $this->count_registrations() === 0; |
|
| 1643 | - } |
|
| 1644 | - |
|
| 1645 | - |
|
| 1646 | - /******************************************************************* |
|
| 16 | + /** |
|
| 17 | + * The following constants are used by the ticket_status() method to indicate whether a ticket is on sale or not. |
|
| 18 | + */ |
|
| 19 | + const sold_out = 'TKS'; |
|
| 20 | + |
|
| 21 | + /** |
|
| 22 | + * |
|
| 23 | + */ |
|
| 24 | + const expired = 'TKE'; |
|
| 25 | + |
|
| 26 | + /** |
|
| 27 | + * |
|
| 28 | + */ |
|
| 29 | + const archived = 'TKA'; |
|
| 30 | + |
|
| 31 | + /** |
|
| 32 | + * |
|
| 33 | + */ |
|
| 34 | + const pending = 'TKP'; |
|
| 35 | + |
|
| 36 | + /** |
|
| 37 | + * |
|
| 38 | + */ |
|
| 39 | + const onsale = 'TKO'; |
|
| 40 | + |
|
| 41 | + /** |
|
| 42 | + * extra meta key for tracking ticket reservations |
|
| 43 | + * |
|
| 44 | + * @type string |
|
| 45 | + */ |
|
| 46 | + const META_KEY_TICKET_RESERVATIONS = 'ticket_reservations'; |
|
| 47 | + |
|
| 48 | + /** |
|
| 49 | + * cached result from method of the same name |
|
| 50 | + * |
|
| 51 | + * @var float $_ticket_total_with_taxes |
|
| 52 | + */ |
|
| 53 | + private $_ticket_total_with_taxes; |
|
| 54 | + |
|
| 55 | + |
|
| 56 | + /** |
|
| 57 | + * @param array $props_n_values incoming values |
|
| 58 | + * @param string $timezone incoming timezone (if not set the timezone set for the website will be |
|
| 59 | + * used.) |
|
| 60 | + * @param array $date_formats incoming date_formats in an array where the first value is the |
|
| 61 | + * date_format and the second value is the time format |
|
| 62 | + * @return EE_Ticket |
|
| 63 | + * @throws EE_Error |
|
| 64 | + * @throws ReflectionException |
|
| 65 | + */ |
|
| 66 | + public static function new_instance($props_n_values = [], $timezone = null, $date_formats = []) |
|
| 67 | + { |
|
| 68 | + $ticket = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats); |
|
| 69 | + if (! $ticket instanceof EE_Ticket) { |
|
| 70 | + $ticket = new EE_Ticket($props_n_values, false, $timezone, $date_formats); |
|
| 71 | + } |
|
| 72 | + return $ticket; |
|
| 73 | + } |
|
| 74 | + |
|
| 75 | + |
|
| 76 | + /** |
|
| 77 | + * @param array $props_n_values incoming values from the database |
|
| 78 | + * @param string $timezone incoming timezone as set by the model. If not set the timezone for |
|
| 79 | + * the website will be used. |
|
| 80 | + * @return EE_Ticket |
|
| 81 | + * @throws EE_Error |
|
| 82 | + * @throws ReflectionException |
|
| 83 | + */ |
|
| 84 | + public static function new_instance_from_db($props_n_values = [], $timezone = null) |
|
| 85 | + { |
|
| 86 | + return new self($props_n_values, true, $timezone); |
|
| 87 | + } |
|
| 88 | + |
|
| 89 | + |
|
| 90 | + /** |
|
| 91 | + * @param array $props_n_values |
|
| 92 | + * @param bool $bydb |
|
| 93 | + * @param string $timezone |
|
| 94 | + * @param array $date_formats incoming date_formats in an array where the first value is the |
|
| 95 | + * date_format and the second value is the time format |
|
| 96 | + * @throws EE_Error |
|
| 97 | + * @throws ReflectionException |
|
| 98 | + */ |
|
| 99 | + protected function __construct($props_n_values = [], $bydb = false, $timezone = '', $date_formats = []) |
|
| 100 | + { |
|
| 101 | + parent::__construct($props_n_values, $bydb, $timezone, $date_formats); |
|
| 102 | + } |
|
| 103 | + |
|
| 104 | + |
|
| 105 | + /** |
|
| 106 | + * @return bool |
|
| 107 | + * @throws EE_Error|ReflectionException |
|
| 108 | + */ |
|
| 109 | + public function parent() |
|
| 110 | + { |
|
| 111 | + return $this->get('TKT_parent'); |
|
| 112 | + } |
|
| 113 | + |
|
| 114 | + |
|
| 115 | + /** |
|
| 116 | + * return if a ticket has quantities available for purchase |
|
| 117 | + * |
|
| 118 | + * @param int $DTT_ID the primary key for a particular datetime |
|
| 119 | + * @return boolean |
|
| 120 | + * @throws EE_Error |
|
| 121 | + * @throws ReflectionException |
|
| 122 | + */ |
|
| 123 | + public function available($DTT_ID = 0) |
|
| 124 | + { |
|
| 125 | + // are we checking availability for a particular datetime ? |
|
| 126 | + if ($DTT_ID) { |
|
| 127 | + // get that datetime object |
|
| 128 | + $datetime = $this->get_first_related('Datetime', [['DTT_ID' => $DTT_ID]]); |
|
| 129 | + // if ticket sales for this datetime have exceeded the reg limit... |
|
| 130 | + if ($datetime instanceof EE_Datetime && $datetime->sold_out()) { |
|
| 131 | + return false; |
|
| 132 | + } |
|
| 133 | + } |
|
| 134 | + // datetime is still open for registration, but is this ticket sold out ? |
|
| 135 | + return $this->qty() < 1 || $this->qty() > $this->sold(); |
|
| 136 | + } |
|
| 137 | + |
|
| 138 | + |
|
| 139 | + /** |
|
| 140 | + * Using the start date and end date this method calculates whether the ticket is On Sale, Pending, or Expired |
|
| 141 | + * |
|
| 142 | + * @param bool $display true = we'll return a localized string, otherwise we just return the value of the |
|
| 143 | + * relevant status const |
|
| 144 | + * @param bool | null $remaining if it is already known that tickets are available, then simply pass a bool to save |
|
| 145 | + * further processing |
|
| 146 | + * @return mixed status int if the display string isn't requested |
|
| 147 | + * @throws EE_Error |
|
| 148 | + * @throws ReflectionException |
|
| 149 | + */ |
|
| 150 | + public function ticket_status($display = false, $remaining = null) |
|
| 151 | + { |
|
| 152 | + $remaining = is_bool($remaining) ? $remaining : $this->is_remaining(); |
|
| 153 | + if (! $remaining) { |
|
| 154 | + return $display |
|
| 155 | + ? EEH_Template::pretty_status(EE_Ticket::sold_out, false, 'sentence') |
|
| 156 | + : EE_Ticket::sold_out; |
|
| 157 | + } |
|
| 158 | + if ($this->get('TKT_deleted')) { |
|
| 159 | + return $display |
|
| 160 | + ? EEH_Template::pretty_status(EE_Ticket::archived, false, 'sentence') |
|
| 161 | + : EE_Ticket::archived; |
|
| 162 | + } |
|
| 163 | + if ($this->is_expired()) { |
|
| 164 | + return $display |
|
| 165 | + ? EEH_Template::pretty_status(EE_Ticket::expired, false, 'sentence') |
|
| 166 | + : EE_Ticket::expired; |
|
| 167 | + } |
|
| 168 | + if ($this->is_pending()) { |
|
| 169 | + return $display |
|
| 170 | + ? EEH_Template::pretty_status(EE_Ticket::pending, false, 'sentence') |
|
| 171 | + : EE_Ticket::pending; |
|
| 172 | + } |
|
| 173 | + if ($this->is_on_sale()) { |
|
| 174 | + return $display |
|
| 175 | + ? EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence') |
|
| 176 | + : EE_Ticket::onsale; |
|
| 177 | + } |
|
| 178 | + return ''; |
|
| 179 | + } |
|
| 180 | + |
|
| 181 | + |
|
| 182 | + /** |
|
| 183 | + * The purpose of this method is to simply return a boolean for whether there are any tickets remaining for sale |
|
| 184 | + * considering ALL the factors used for figuring that out. |
|
| 185 | + * |
|
| 186 | + * @access public |
|
| 187 | + * @param int $DTT_ID if an int above 0 is included here then we get a specific dtt. |
|
| 188 | + * @return boolean true = tickets remaining, false not. |
|
| 189 | + * @throws EE_Error|ReflectionException |
|
| 190 | + */ |
|
| 191 | + public function is_remaining($DTT_ID = 0) |
|
| 192 | + { |
|
| 193 | + $num_remaining = $this->remaining($DTT_ID); |
|
| 194 | + if ($num_remaining === 0) { |
|
| 195 | + return false; |
|
| 196 | + } |
|
| 197 | + if ($num_remaining > 0 && $num_remaining < $this->min()) { |
|
| 198 | + return false; |
|
| 199 | + } |
|
| 200 | + return true; |
|
| 201 | + } |
|
| 202 | + |
|
| 203 | + |
|
| 204 | + /** |
|
| 205 | + * return the total number of tickets available for purchase |
|
| 206 | + * |
|
| 207 | + * @param int $DTT_ID the primary key for a particular datetime. |
|
| 208 | + * set to 0 for all related datetimes |
|
| 209 | + * @return int |
|
| 210 | + * @throws EE_Error|ReflectionException |
|
| 211 | + */ |
|
| 212 | + public function remaining($DTT_ID = 0) |
|
| 213 | + { |
|
| 214 | + return $this->real_quantity_on_ticket('saleable', $DTT_ID); |
|
| 215 | + } |
|
| 216 | + |
|
| 217 | + |
|
| 218 | + /** |
|
| 219 | + * Gets min |
|
| 220 | + * |
|
| 221 | + * @return int |
|
| 222 | + * @throws EE_Error |
|
| 223 | + * @throws ReflectionException |
|
| 224 | + */ |
|
| 225 | + public function min() |
|
| 226 | + { |
|
| 227 | + return $this->get('TKT_min'); |
|
| 228 | + } |
|
| 229 | + |
|
| 230 | + |
|
| 231 | + /** |
|
| 232 | + * return if a ticket is no longer available cause its available dates have expired. |
|
| 233 | + * |
|
| 234 | + * @return boolean |
|
| 235 | + * @throws EE_Error |
|
| 236 | + * @throws ReflectionException |
|
| 237 | + */ |
|
| 238 | + public function is_expired() |
|
| 239 | + { |
|
| 240 | + return ($this->get_raw('TKT_end_date') < time()); |
|
| 241 | + } |
|
| 242 | + |
|
| 243 | + |
|
| 244 | + /** |
|
| 245 | + * Return if a ticket is yet to go on sale or not |
|
| 246 | + * |
|
| 247 | + * @return boolean |
|
| 248 | + * @throws EE_Error |
|
| 249 | + * @throws ReflectionException |
|
| 250 | + */ |
|
| 251 | + public function is_pending() |
|
| 252 | + { |
|
| 253 | + return ($this->get_raw('TKT_start_date') > time()); |
|
| 254 | + } |
|
| 255 | + |
|
| 256 | + |
|
| 257 | + /** |
|
| 258 | + * Return if a ticket is on sale or not |
|
| 259 | + * |
|
| 260 | + * @return boolean |
|
| 261 | + * @throws EE_Error |
|
| 262 | + * @throws ReflectionException |
|
| 263 | + */ |
|
| 264 | + public function is_on_sale() |
|
| 265 | + { |
|
| 266 | + return ($this->get_raw('TKT_start_date') < time() && $this->get_raw('TKT_end_date') > time()); |
|
| 267 | + } |
|
| 268 | + |
|
| 269 | + |
|
| 270 | + /** |
|
| 271 | + * This returns the chronologically last datetime that this ticket is associated with |
|
| 272 | + * |
|
| 273 | + * @param string $date_format |
|
| 274 | + * @param string $conjunction - conjunction junction what's your function ? this string joins the start date with |
|
| 275 | + * the end date ie: Jan 01 "to" Dec 31 |
|
| 276 | + * @return string |
|
| 277 | + * @throws EE_Error |
|
| 278 | + * @throws ReflectionException |
|
| 279 | + */ |
|
| 280 | + public function date_range($date_format = '', $conjunction = ' - ') |
|
| 281 | + { |
|
| 282 | + $date_format = ! empty($date_format) ? $date_format : $this->_dt_frmt; |
|
| 283 | + $first_date = $this->first_datetime() instanceof EE_Datetime |
|
| 284 | + ? $this->first_datetime()->get_i18n_datetime('DTT_EVT_start', $date_format) |
|
| 285 | + : ''; |
|
| 286 | + $last_date = $this->last_datetime() instanceof EE_Datetime |
|
| 287 | + ? $this->last_datetime()->get_i18n_datetime('DTT_EVT_end', $date_format) |
|
| 288 | + : ''; |
|
| 289 | + |
|
| 290 | + return $first_date && $last_date ? $first_date . $conjunction . $last_date : ''; |
|
| 291 | + } |
|
| 292 | + |
|
| 293 | + |
|
| 294 | + /** |
|
| 295 | + * This returns the chronologically first datetime that this ticket is associated with |
|
| 296 | + * |
|
| 297 | + * @return EE_Datetime |
|
| 298 | + * @throws EE_Error|ReflectionException |
|
| 299 | + */ |
|
| 300 | + public function first_datetime() |
|
| 301 | + { |
|
| 302 | + $datetimes = $this->datetimes(['limit' => 1]); |
|
| 303 | + return reset($datetimes); |
|
| 304 | + } |
|
| 305 | + |
|
| 306 | + |
|
| 307 | + /** |
|
| 308 | + * Gets all the datetimes this ticket can be used for attending. |
|
| 309 | + * Unless otherwise specified, orders datetimes by start date. |
|
| 310 | + * |
|
| 311 | + * @param array $query_params |
|
| 312 | + * @return EE_Datetime[]|EE_Base_Class[] |
|
| 313 | + * @throws EE_Error |
|
| 314 | + * @throws ReflectionException |
|
| 315 | + */ |
|
| 316 | + public function datetimes($query_params = []) |
|
| 317 | + { |
|
| 318 | + if (! isset($query_params['order_by'])) { |
|
| 319 | + $query_params['order_by']['DTT_order'] = 'ASC'; |
|
| 320 | + } |
|
| 321 | + return $this->get_many_related('Datetime', $query_params); |
|
| 322 | + } |
|
| 323 | + |
|
| 324 | + |
|
| 325 | + /** |
|
| 326 | + * This returns the chronologically last datetime that this ticket is associated with |
|
| 327 | + * |
|
| 328 | + * @return EE_Datetime |
|
| 329 | + * @throws EE_Error|ReflectionException |
|
| 330 | + */ |
|
| 331 | + public function last_datetime() |
|
| 332 | + { |
|
| 333 | + $datetimes = $this->datetimes(['limit' => 1, 'order_by' => ['DTT_EVT_start' => 'DESC']]); |
|
| 334 | + return end($datetimes); |
|
| 335 | + } |
|
| 336 | + |
|
| 337 | + |
|
| 338 | + /** |
|
| 339 | + * This returns the total tickets sold depending on the given parameters. |
|
| 340 | + * |
|
| 341 | + * @param string $get_sold_for Can be one of two options: 'ticket', 'datetime'. |
|
| 342 | + * 'ticket' = total ticket sales for all datetimes this ticket is related to |
|
| 343 | + * 'datetime' = total ticket sales for a specified datetime (required $dtt_id) |
|
| 344 | + * 'datetime' = total ticket sales in the datetime_ticket table. |
|
| 345 | + * If $dtt_id is not given then we return an array of sales indexed by datetime. |
|
| 346 | + * If $dtt_id IS given then we return the tickets sold for that given datetime. |
|
| 347 | + * @param int $dtt_id [optional] include the dtt_id with $what = 'datetime'. |
|
| 348 | + * @return mixed (array|int) how many tickets have sold |
|
| 349 | + * @throws EE_Error|ReflectionException |
|
| 350 | + */ |
|
| 351 | + public function tickets_sold($get_sold_for = 'ticket', $dtt_id = null) |
|
| 352 | + { |
|
| 353 | + $total = 0; |
|
| 354 | + $tickets_sold = $this->_all_tickets_sold(); |
|
| 355 | + |
|
| 356 | + if ($get_sold_for === 'ticket') { |
|
| 357 | + return $tickets_sold['ticket']; |
|
| 358 | + } |
|
| 359 | + |
|
| 360 | + if (empty($tickets_sold['datetime'])) { |
|
| 361 | + return $total; |
|
| 362 | + } |
|
| 363 | + |
|
| 364 | + if (! empty($dtt_id) && ! isset($tickets_sold['datetime'][ $dtt_id ])) { |
|
| 365 | + EE_Error::add_error( |
|
| 366 | + esc_html__( |
|
| 367 | + 'You\'ve requested the amount of tickets sold for a given ticket and datetime, however there are no records for the datetime id you included. Are you SURE that is a datetime related to this ticket?', |
|
| 368 | + 'event_espresso' |
|
| 369 | + ), |
|
| 370 | + __FILE__, |
|
| 371 | + __FUNCTION__, |
|
| 372 | + __LINE__ |
|
| 373 | + ); |
|
| 374 | + return $total; |
|
| 375 | + } |
|
| 376 | + return empty($dtt_id) ? $tickets_sold['datetime'] : $tickets_sold['datetime'][ $dtt_id ]; |
|
| 377 | + } |
|
| 378 | + |
|
| 379 | + |
|
| 380 | + /** |
|
| 381 | + * This returns an array indexed by datetime_id for tickets sold with this ticket. |
|
| 382 | + * |
|
| 383 | + * @return EE_Ticket[] |
|
| 384 | + * @throws EE_Error |
|
| 385 | + * @throws ReflectionException |
|
| 386 | + */ |
|
| 387 | + protected function _all_tickets_sold() |
|
| 388 | + { |
|
| 389 | + $datetimes = $this->get_many_related('Datetime'); |
|
| 390 | + $tickets_sold = []; |
|
| 391 | + if (! empty($datetimes)) { |
|
| 392 | + foreach ($datetimes as $datetime) { |
|
| 393 | + $tickets_sold['datetime'][ $datetime->ID() ] = $datetime->get('DTT_sold'); |
|
| 394 | + } |
|
| 395 | + } |
|
| 396 | + // Tickets sold |
|
| 397 | + $tickets_sold['ticket'] = $this->sold(); |
|
| 398 | + return $tickets_sold; |
|
| 399 | + } |
|
| 400 | + |
|
| 401 | + |
|
| 402 | + /** |
|
| 403 | + * This returns the base price object for the ticket. |
|
| 404 | + * |
|
| 405 | + * @param bool $return_array whether to return as an array indexed by price id or just the object. |
|
| 406 | + * @return EE_Price|EE_Base_Class|EE_Price[]|EE_Base_Class[] |
|
| 407 | + * @throws EE_Error |
|
| 408 | + * @throws ReflectionException |
|
| 409 | + */ |
|
| 410 | + public function base_price($return_array = false) |
|
| 411 | + { |
|
| 412 | + $_where = ['Price_Type.PBT_ID' => EEM_Price_Type::base_type_base_price]; |
|
| 413 | + return $return_array |
|
| 414 | + ? $this->get_many_related('Price', [$_where]) |
|
| 415 | + : $this->get_first_related('Price', [$_where]); |
|
| 416 | + } |
|
| 417 | + |
|
| 418 | + |
|
| 419 | + /** |
|
| 420 | + * This returns ONLY the price modifiers for the ticket (i.e. no taxes or base price) |
|
| 421 | + * |
|
| 422 | + * @access public |
|
| 423 | + * @return EE_Price[] |
|
| 424 | + * @throws EE_Error|ReflectionException |
|
| 425 | + */ |
|
| 426 | + public function price_modifiers() |
|
| 427 | + { |
|
| 428 | + $query_params = [ |
|
| 429 | + 0 => [ |
|
| 430 | + 'Price_Type.PBT_ID' => [ |
|
| 431 | + 'NOT IN', |
|
| 432 | + [EEM_Price_Type::base_type_base_price, EEM_Price_Type::base_type_tax], |
|
| 433 | + ], |
|
| 434 | + ], |
|
| 435 | + ]; |
|
| 436 | + return $this->prices($query_params); |
|
| 437 | + } |
|
| 438 | + |
|
| 439 | + |
|
| 440 | + /** |
|
| 441 | + * Gets all the prices that combine to form the final price of this ticket |
|
| 442 | + * |
|
| 443 | + * @param array $query_params |
|
| 444 | + * @return EE_Price[]|EE_Base_Class[] |
|
| 445 | + * @throws EE_Error |
|
| 446 | + * @throws ReflectionException |
|
| 447 | + * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md |
|
| 448 | + */ |
|
| 449 | + public function prices($query_params = []) |
|
| 450 | + { |
|
| 451 | + return $this->get_many_related('Price', $query_params); |
|
| 452 | + } |
|
| 453 | + |
|
| 454 | + |
|
| 455 | + /** |
|
| 456 | + * Gets all the ticket applicabilities (ie, relations between datetimes and tickets) |
|
| 457 | + * |
|
| 458 | + * @param array $query_params |
|
| 459 | + * @return EE_Datetime_Ticket|EE_Base_Class[] |
|
| 460 | + * @throws EE_Error |
|
| 461 | + * @throws ReflectionException |
|
| 462 | + * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md |
|
| 463 | + */ |
|
| 464 | + public function datetime_tickets($query_params = []) |
|
| 465 | + { |
|
| 466 | + return $this->get_many_related('Datetime_Ticket', $query_params); |
|
| 467 | + } |
|
| 468 | + |
|
| 469 | + |
|
| 470 | + /** |
|
| 471 | + * Gets all the datetimes from the db ordered by DTT_order |
|
| 472 | + * |
|
| 473 | + * @param boolean $show_expired |
|
| 474 | + * @param boolean $show_deleted |
|
| 475 | + * @return EE_Datetime[] |
|
| 476 | + * @throws EE_Error|ReflectionException |
|
| 477 | + */ |
|
| 478 | + public function datetimes_ordered($show_expired = true, $show_deleted = false) |
|
| 479 | + { |
|
| 480 | + return EEM_Datetime::instance($this->_timezone)->get_datetimes_for_ticket_ordered_by_DTT_order( |
|
| 481 | + $this->ID(), |
|
| 482 | + $show_expired, |
|
| 483 | + $show_deleted |
|
| 484 | + ); |
|
| 485 | + } |
|
| 486 | + |
|
| 487 | + |
|
| 488 | + /** |
|
| 489 | + * Gets ID |
|
| 490 | + * |
|
| 491 | + * @return int |
|
| 492 | + * @throws EE_Error |
|
| 493 | + * @throws ReflectionException |
|
| 494 | + */ |
|
| 495 | + public function ID() |
|
| 496 | + { |
|
| 497 | + return (int) $this->get('TKT_ID'); |
|
| 498 | + } |
|
| 499 | + |
|
| 500 | + |
|
| 501 | + /** |
|
| 502 | + * get the author of the ticket. |
|
| 503 | + * |
|
| 504 | + * @return int |
|
| 505 | + * @throws EE_Error |
|
| 506 | + * @throws ReflectionException |
|
| 507 | + * @since 4.5.0 |
|
| 508 | + */ |
|
| 509 | + public function wp_user() |
|
| 510 | + { |
|
| 511 | + return $this->get('TKT_wp_user'); |
|
| 512 | + } |
|
| 513 | + |
|
| 514 | + |
|
| 515 | + /** |
|
| 516 | + * Gets the template for the ticket |
|
| 517 | + * |
|
| 518 | + * @return EE_Ticket_Template|EE_Base_Class |
|
| 519 | + * @throws EE_Error |
|
| 520 | + * @throws ReflectionException |
|
| 521 | + */ |
|
| 522 | + public function template() |
|
| 523 | + { |
|
| 524 | + return $this->get_first_related('Ticket_Template'); |
|
| 525 | + } |
|
| 526 | + |
|
| 527 | + |
|
| 528 | + /** |
|
| 529 | + * Simply returns an array of EE_Price objects that are taxes. |
|
| 530 | + * |
|
| 531 | + * @return EE_Price[] |
|
| 532 | + * @throws EE_Error |
|
| 533 | + */ |
|
| 534 | + public function get_ticket_taxes_for_admin() |
|
| 535 | + { |
|
| 536 | + return EE_Taxes::get_taxes_for_admin(); |
|
| 537 | + } |
|
| 538 | + |
|
| 539 | + |
|
| 540 | + /** |
|
| 541 | + * @return float |
|
| 542 | + * @throws EE_Error |
|
| 543 | + * @throws ReflectionException |
|
| 544 | + */ |
|
| 545 | + public function ticket_price() |
|
| 546 | + { |
|
| 547 | + return $this->get('TKT_price'); |
|
| 548 | + } |
|
| 549 | + |
|
| 550 | + |
|
| 551 | + /** |
|
| 552 | + * @param string|null $schema |
|
| 553 | + * @return mixed |
|
| 554 | + * @throws EE_Error |
|
| 555 | + * @throws ReflectionException |
|
| 556 | + */ |
|
| 557 | + public function pretty_price($schema = 'localized_currency') |
|
| 558 | + { |
|
| 559 | + return $this->get_pretty('TKT_price', $schema); |
|
| 560 | + } |
|
| 561 | + |
|
| 562 | + |
|
| 563 | + /** |
|
| 564 | + * @return bool |
|
| 565 | + * @throws EE_Error|ReflectionException |
|
| 566 | + */ |
|
| 567 | + public function is_free() |
|
| 568 | + { |
|
| 569 | + return $this->get_ticket_total_with_taxes() === (float) 0; |
|
| 570 | + } |
|
| 571 | + |
|
| 572 | + |
|
| 573 | + /** |
|
| 574 | + * get_ticket_total_with_taxes |
|
| 575 | + * |
|
| 576 | + * @param bool $no_cache |
|
| 577 | + * @return float |
|
| 578 | + * @throws EE_Error|ReflectionException |
|
| 579 | + */ |
|
| 580 | + public function get_ticket_total_with_taxes($no_cache = false) |
|
| 581 | + { |
|
| 582 | + if ($this->_ticket_total_with_taxes === null || $no_cache) { |
|
| 583 | + $this->_ticket_total_with_taxes = $this->get_ticket_subtotal() + $this->get_ticket_taxes_total_for_admin(); |
|
| 584 | + } |
|
| 585 | + return (float) $this->_ticket_total_with_taxes; |
|
| 586 | + } |
|
| 587 | + |
|
| 588 | + |
|
| 589 | + /** |
|
| 590 | + * @throws EE_Error |
|
| 591 | + * @throws ReflectionException |
|
| 592 | + */ |
|
| 593 | + public function ensure_TKT_Price_correct() |
|
| 594 | + { |
|
| 595 | + $this->set_price(EE_Taxes::get_subtotal_for_admin($this)); |
|
| 596 | + $this->save(); |
|
| 597 | + } |
|
| 598 | + |
|
| 599 | + |
|
| 600 | + /** |
|
| 601 | + * @return float |
|
| 602 | + * @throws EE_Error|ReflectionException |
|
| 603 | + */ |
|
| 604 | + public function get_ticket_subtotal() |
|
| 605 | + { |
|
| 606 | + return EE_Taxes::get_subtotal_for_admin($this); |
|
| 607 | + } |
|
| 608 | + |
|
| 609 | + |
|
| 610 | + /** |
|
| 611 | + * Returns the total taxes applied to this ticket |
|
| 612 | + * |
|
| 613 | + * @return float |
|
| 614 | + * @throws EE_Error|ReflectionException |
|
| 615 | + */ |
|
| 616 | + public function get_ticket_taxes_total_for_admin() |
|
| 617 | + { |
|
| 618 | + return EE_Taxes::get_total_taxes_for_admin($this); |
|
| 619 | + } |
|
| 620 | + |
|
| 621 | + |
|
| 622 | + /** |
|
| 623 | + * Sets name |
|
| 624 | + * |
|
| 625 | + * @param string $name |
|
| 626 | + * @throws EE_Error |
|
| 627 | + * @throws ReflectionException |
|
| 628 | + */ |
|
| 629 | + public function set_name($name) |
|
| 630 | + { |
|
| 631 | + $this->set('TKT_name', $name); |
|
| 632 | + } |
|
| 633 | + |
|
| 634 | + |
|
| 635 | + /** |
|
| 636 | + * Gets description |
|
| 637 | + * |
|
| 638 | + * @return string |
|
| 639 | + * @throws EE_Error |
|
| 640 | + * @throws ReflectionException |
|
| 641 | + */ |
|
| 642 | + public function description() |
|
| 643 | + { |
|
| 644 | + return $this->get('TKT_description'); |
|
| 645 | + } |
|
| 646 | + |
|
| 647 | + |
|
| 648 | + /** |
|
| 649 | + * Sets description |
|
| 650 | + * |
|
| 651 | + * @param string $description |
|
| 652 | + * @throws EE_Error |
|
| 653 | + * @throws ReflectionException |
|
| 654 | + */ |
|
| 655 | + public function set_description($description) |
|
| 656 | + { |
|
| 657 | + $this->set('TKT_description', $description); |
|
| 658 | + } |
|
| 659 | + |
|
| 660 | + |
|
| 661 | + /** |
|
| 662 | + * Gets start_date |
|
| 663 | + * |
|
| 664 | + * @param string $date_format |
|
| 665 | + * @param string $time_format |
|
| 666 | + * @return string |
|
| 667 | + * @throws EE_Error |
|
| 668 | + * @throws ReflectionException |
|
| 669 | + */ |
|
| 670 | + public function start_date($date_format = '', $time_format = '') |
|
| 671 | + { |
|
| 672 | + return $this->_get_datetime('TKT_start_date', $date_format, $time_format); |
|
| 673 | + } |
|
| 674 | + |
|
| 675 | + |
|
| 676 | + /** |
|
| 677 | + * Sets start_date |
|
| 678 | + * |
|
| 679 | + * @param string $start_date |
|
| 680 | + * @return void |
|
| 681 | + * @throws EE_Error |
|
| 682 | + * @throws ReflectionException |
|
| 683 | + */ |
|
| 684 | + public function set_start_date($start_date) |
|
| 685 | + { |
|
| 686 | + $this->_set_date_time('B', $start_date, 'TKT_start_date'); |
|
| 687 | + } |
|
| 688 | + |
|
| 689 | + |
|
| 690 | + /** |
|
| 691 | + * Gets end_date |
|
| 692 | + * |
|
| 693 | + * @param string $date_format |
|
| 694 | + * @param string $time_format |
|
| 695 | + * @return string |
|
| 696 | + * @throws EE_Error |
|
| 697 | + * @throws ReflectionException |
|
| 698 | + */ |
|
| 699 | + public function end_date($date_format = '', $time_format = '') |
|
| 700 | + { |
|
| 701 | + return $this->_get_datetime('TKT_end_date', $date_format, $time_format); |
|
| 702 | + } |
|
| 703 | + |
|
| 704 | + |
|
| 705 | + /** |
|
| 706 | + * Sets end_date |
|
| 707 | + * |
|
| 708 | + * @param string $end_date |
|
| 709 | + * @return void |
|
| 710 | + * @throws EE_Error |
|
| 711 | + * @throws ReflectionException |
|
| 712 | + */ |
|
| 713 | + public function set_end_date($end_date) |
|
| 714 | + { |
|
| 715 | + $this->_set_date_time('B', $end_date, 'TKT_end_date'); |
|
| 716 | + } |
|
| 717 | + |
|
| 718 | + |
|
| 719 | + /** |
|
| 720 | + * Sets sell until time |
|
| 721 | + * |
|
| 722 | + * @param string $time a string representation of the sell until time (ex 9am or 7:30pm) |
|
| 723 | + * @throws EE_Error |
|
| 724 | + * @throws ReflectionException |
|
| 725 | + * @since 4.5.0 |
|
| 726 | + */ |
|
| 727 | + public function set_end_time($time) |
|
| 728 | + { |
|
| 729 | + $this->_set_time_for($time, 'TKT_end_date'); |
|
| 730 | + } |
|
| 731 | + |
|
| 732 | + |
|
| 733 | + /** |
|
| 734 | + * Sets min |
|
| 735 | + * |
|
| 736 | + * @param int $min |
|
| 737 | + * @return void |
|
| 738 | + * @throws EE_Error |
|
| 739 | + * @throws ReflectionException |
|
| 740 | + */ |
|
| 741 | + public function set_min($min) |
|
| 742 | + { |
|
| 743 | + $this->set('TKT_min', $min); |
|
| 744 | + } |
|
| 745 | + |
|
| 746 | + |
|
| 747 | + /** |
|
| 748 | + * Gets max |
|
| 749 | + * |
|
| 750 | + * @return int |
|
| 751 | + * @throws EE_Error |
|
| 752 | + * @throws ReflectionException |
|
| 753 | + */ |
|
| 754 | + public function max() |
|
| 755 | + { |
|
| 756 | + return $this->get('TKT_max'); |
|
| 757 | + } |
|
| 758 | + |
|
| 759 | + |
|
| 760 | + /** |
|
| 761 | + * Sets max |
|
| 762 | + * |
|
| 763 | + * @param int $max |
|
| 764 | + * @return void |
|
| 765 | + * @throws EE_Error |
|
| 766 | + * @throws ReflectionException |
|
| 767 | + */ |
|
| 768 | + public function set_max($max) |
|
| 769 | + { |
|
| 770 | + $this->set('TKT_max', $max); |
|
| 771 | + } |
|
| 772 | + |
|
| 773 | + |
|
| 774 | + /** |
|
| 775 | + * Sets price |
|
| 776 | + * |
|
| 777 | + * @param float $price |
|
| 778 | + * @return void |
|
| 779 | + * @throws EE_Error |
|
| 780 | + * @throws ReflectionException |
|
| 781 | + */ |
|
| 782 | + public function set_price($price) |
|
| 783 | + { |
|
| 784 | + $this->set('TKT_price', $price); |
|
| 785 | + } |
|
| 786 | + |
|
| 787 | + |
|
| 788 | + /** |
|
| 789 | + * Gets sold |
|
| 790 | + * |
|
| 791 | + * @return int |
|
| 792 | + * @throws EE_Error |
|
| 793 | + * @throws ReflectionException |
|
| 794 | + */ |
|
| 795 | + public function sold() |
|
| 796 | + { |
|
| 797 | + return $this->get_raw('TKT_sold'); |
|
| 798 | + } |
|
| 799 | + |
|
| 800 | + |
|
| 801 | + /** |
|
| 802 | + * Sets sold |
|
| 803 | + * |
|
| 804 | + * @param int $sold |
|
| 805 | + * @return void |
|
| 806 | + * @throws EE_Error |
|
| 807 | + * @throws ReflectionException |
|
| 808 | + */ |
|
| 809 | + public function set_sold($sold) |
|
| 810 | + { |
|
| 811 | + // sold can not go below zero |
|
| 812 | + $sold = max(0, absint($sold)); |
|
| 813 | + $this->set('TKT_sold', $sold); |
|
| 814 | + } |
|
| 815 | + |
|
| 816 | + |
|
| 817 | + /** |
|
| 818 | + * Increments sold by amount passed by $qty AND decrements the reserved count on both this ticket and its |
|
| 819 | + * associated datetimes. |
|
| 820 | + * |
|
| 821 | + * @param int $qty |
|
| 822 | + * @return boolean |
|
| 823 | + * @throws EE_Error |
|
| 824 | + * @throws InvalidArgumentException |
|
| 825 | + * @throws InvalidDataTypeException |
|
| 826 | + * @throws InvalidInterfaceException |
|
| 827 | + * @throws ReflectionException |
|
| 828 | + * @since 4.9.80.p |
|
| 829 | + */ |
|
| 830 | + public function increaseSold($qty = 1) |
|
| 831 | + { |
|
| 832 | + $qty = absint($qty); |
|
| 833 | + // increment sold and decrement reserved datetime quantities simultaneously |
|
| 834 | + // don't worry about failures, because they must have already had a spot reserved |
|
| 835 | + $this->increaseSoldForDatetimes($qty); |
|
| 836 | + // Increment and decrement ticket quantities simultaneously |
|
| 837 | + $success = $this->adjustNumericFieldsInDb( |
|
| 838 | + [ |
|
| 839 | + 'TKT_reserved' => $qty * -1, |
|
| 840 | + 'TKT_sold' => $qty, |
|
| 841 | + ] |
|
| 842 | + ); |
|
| 843 | + do_action( |
|
| 844 | + 'AHEE__EE_Ticket__increase_sold', |
|
| 845 | + $this, |
|
| 846 | + $qty, |
|
| 847 | + $this->sold(), |
|
| 848 | + $success |
|
| 849 | + ); |
|
| 850 | + return $success; |
|
| 851 | + } |
|
| 852 | + |
|
| 853 | + |
|
| 854 | + /** |
|
| 855 | + * On each datetime related to this ticket, increases its sold count and decreases its reserved count by $qty. |
|
| 856 | + * |
|
| 857 | + * @param int $qty positive or negative. Positive means to increase sold counts (and decrease reserved |
|
| 858 | + * counts), Negative means to decreases old counts (and increase reserved counts). |
|
| 859 | + * @param EE_Datetime[] $datetimes |
|
| 860 | + * @throws EE_Error |
|
| 861 | + * @throws InvalidArgumentException |
|
| 862 | + * @throws InvalidDataTypeException |
|
| 863 | + * @throws InvalidInterfaceException |
|
| 864 | + * @throws ReflectionException |
|
| 865 | + * @since 4.9.80.p |
|
| 866 | + */ |
|
| 867 | + protected function increaseSoldForDatetimes($qty, array $datetimes = []) |
|
| 868 | + { |
|
| 869 | + $datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes(); |
|
| 870 | + foreach ($datetimes as $datetime) { |
|
| 871 | + $datetime->increaseSold($qty); |
|
| 872 | + } |
|
| 873 | + } |
|
| 874 | + |
|
| 875 | + |
|
| 876 | + /** |
|
| 877 | + * Decrements (subtracts) sold by amount passed by $qty on both the ticket and its related datetimes directly in the |
|
| 878 | + * DB and then updates the model objects. |
|
| 879 | + * Does not affect the reserved counts. |
|
| 880 | + * |
|
| 881 | + * @param int $qty |
|
| 882 | + * @return boolean |
|
| 883 | + * @throws EE_Error |
|
| 884 | + * @throws InvalidArgumentException |
|
| 885 | + * @throws InvalidDataTypeException |
|
| 886 | + * @throws InvalidInterfaceException |
|
| 887 | + * @throws ReflectionException |
|
| 888 | + * @since 4.9.80.p |
|
| 889 | + */ |
|
| 890 | + public function decreaseSold($qty = 1) |
|
| 891 | + { |
|
| 892 | + $qty = absint($qty); |
|
| 893 | + $this->decreaseSoldForDatetimes($qty); |
|
| 894 | + $success = $this->adjustNumericFieldsInDb( |
|
| 895 | + [ |
|
| 896 | + 'TKT_sold' => $qty * -1, |
|
| 897 | + ] |
|
| 898 | + ); |
|
| 899 | + do_action( |
|
| 900 | + 'AHEE__EE_Ticket__decrease_sold', |
|
| 901 | + $this, |
|
| 902 | + $qty, |
|
| 903 | + $this->sold(), |
|
| 904 | + $success |
|
| 905 | + ); |
|
| 906 | + return $success; |
|
| 907 | + } |
|
| 908 | + |
|
| 909 | + |
|
| 910 | + /** |
|
| 911 | + * Decreases sold on related datetimes |
|
| 912 | + * |
|
| 913 | + * @param int $qty |
|
| 914 | + * @param EE_Datetime[] $datetimes |
|
| 915 | + * @return void |
|
| 916 | + * @throws EE_Error |
|
| 917 | + * @throws InvalidArgumentException |
|
| 918 | + * @throws InvalidDataTypeException |
|
| 919 | + * @throws InvalidInterfaceException |
|
| 920 | + * @throws ReflectionException |
|
| 921 | + * @since 4.9.80.p |
|
| 922 | + */ |
|
| 923 | + protected function decreaseSoldForDatetimes($qty = 1, array $datetimes = []) |
|
| 924 | + { |
|
| 925 | + $datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes(); |
|
| 926 | + if (is_array($datetimes)) { |
|
| 927 | + foreach ($datetimes as $datetime) { |
|
| 928 | + if ($datetime instanceof EE_Datetime) { |
|
| 929 | + $datetime->decreaseSold($qty); |
|
| 930 | + } |
|
| 931 | + } |
|
| 932 | + } |
|
| 933 | + } |
|
| 934 | + |
|
| 935 | + |
|
| 936 | + /** |
|
| 937 | + * Gets qty of reserved tickets |
|
| 938 | + * |
|
| 939 | + * @return int |
|
| 940 | + * @throws EE_Error |
|
| 941 | + * @throws ReflectionException |
|
| 942 | + */ |
|
| 943 | + public function reserved() |
|
| 944 | + { |
|
| 945 | + return $this->get_raw('TKT_reserved'); |
|
| 946 | + } |
|
| 947 | + |
|
| 948 | + |
|
| 949 | + /** |
|
| 950 | + * Sets reserved |
|
| 951 | + * |
|
| 952 | + * @param int $reserved |
|
| 953 | + * @return void |
|
| 954 | + * @throws EE_Error |
|
| 955 | + * @throws ReflectionException |
|
| 956 | + */ |
|
| 957 | + public function set_reserved($reserved) |
|
| 958 | + { |
|
| 959 | + // reserved can not go below zero |
|
| 960 | + $reserved = max(0, (int) $reserved); |
|
| 961 | + $this->set('TKT_reserved', $reserved); |
|
| 962 | + } |
|
| 963 | + |
|
| 964 | + |
|
| 965 | + /** |
|
| 966 | + * Increments reserved by amount passed by $qty, and persists it immediately to the database. |
|
| 967 | + * |
|
| 968 | + * @param int $qty |
|
| 969 | + * @param string $source |
|
| 970 | + * @return bool whether we successfully reserved the ticket or not. |
|
| 971 | + * @throws EE_Error |
|
| 972 | + * @throws InvalidArgumentException |
|
| 973 | + * @throws ReflectionException |
|
| 974 | + * @throws InvalidDataTypeException |
|
| 975 | + * @throws InvalidInterfaceException |
|
| 976 | + * @since 4.9.80.p |
|
| 977 | + */ |
|
| 978 | + public function increaseReserved($qty = 1, $source = 'unknown') |
|
| 979 | + { |
|
| 980 | + $qty = absint($qty); |
|
| 981 | + do_action( |
|
| 982 | + 'AHEE__EE_Ticket__increase_reserved__begin', |
|
| 983 | + $this, |
|
| 984 | + $qty, |
|
| 985 | + $source |
|
| 986 | + ); |
|
| 987 | + $this->add_extra_meta(EE_Ticket::META_KEY_TICKET_RESERVATIONS, "{$qty} from {$source}"); |
|
| 988 | + $success = false; |
|
| 989 | + $datetimes_adjusted_successfully = $this->increaseReservedForDatetimes($qty); |
|
| 990 | + if ($datetimes_adjusted_successfully) { |
|
| 991 | + $success = $this->incrementFieldConditionallyInDb( |
|
| 992 | + 'TKT_reserved', |
|
| 993 | + 'TKT_sold', |
|
| 994 | + 'TKT_qty', |
|
| 995 | + $qty |
|
| 996 | + ); |
|
| 997 | + if (! $success) { |
|
| 998 | + // The datetimes were successfully bumped, but not the |
|
| 999 | + // ticket. So we need to manually rollback the datetimes. |
|
| 1000 | + $this->decreaseReservedForDatetimes($qty); |
|
| 1001 | + } |
|
| 1002 | + } |
|
| 1003 | + do_action( |
|
| 1004 | + 'AHEE__EE_Ticket__increase_reserved', |
|
| 1005 | + $this, |
|
| 1006 | + $qty, |
|
| 1007 | + $this->reserved(), |
|
| 1008 | + $success |
|
| 1009 | + ); |
|
| 1010 | + return $success; |
|
| 1011 | + } |
|
| 1012 | + |
|
| 1013 | + |
|
| 1014 | + /** |
|
| 1015 | + * Increases reserved counts on related datetimes |
|
| 1016 | + * |
|
| 1017 | + * @param int $qty |
|
| 1018 | + * @param EE_Datetime[] $datetimes |
|
| 1019 | + * @return boolean indicating success |
|
| 1020 | + * @throws EE_Error |
|
| 1021 | + * @throws InvalidArgumentException |
|
| 1022 | + * @throws InvalidDataTypeException |
|
| 1023 | + * @throws InvalidInterfaceException |
|
| 1024 | + * @throws ReflectionException |
|
| 1025 | + * @since 4.9.80.p |
|
| 1026 | + */ |
|
| 1027 | + protected function increaseReservedForDatetimes($qty = 1, array $datetimes = []) |
|
| 1028 | + { |
|
| 1029 | + $datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes(); |
|
| 1030 | + $datetimes_updated = []; |
|
| 1031 | + $limit_exceeded = false; |
|
| 1032 | + if (is_array($datetimes)) { |
|
| 1033 | + foreach ($datetimes as $datetime) { |
|
| 1034 | + if ($datetime instanceof EE_Datetime) { |
|
| 1035 | + if ($datetime->increaseReserved($qty)) { |
|
| 1036 | + $datetimes_updated[] = $datetime; |
|
| 1037 | + } else { |
|
| 1038 | + $limit_exceeded = true; |
|
| 1039 | + break; |
|
| 1040 | + } |
|
| 1041 | + } |
|
| 1042 | + } |
|
| 1043 | + // If somewhere along the way we detected a datetime whose |
|
| 1044 | + // limit was exceeded, do a manual rollback. |
|
| 1045 | + if ($limit_exceeded) { |
|
| 1046 | + $this->decreaseReservedForDatetimes($qty, $datetimes_updated); |
|
| 1047 | + return false; |
|
| 1048 | + } |
|
| 1049 | + } |
|
| 1050 | + return true; |
|
| 1051 | + } |
|
| 1052 | + |
|
| 1053 | + |
|
| 1054 | + /** |
|
| 1055 | + * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database. |
|
| 1056 | + * |
|
| 1057 | + * @param int $qty |
|
| 1058 | + * @param bool $adjust_datetimes |
|
| 1059 | + * @param string $source |
|
| 1060 | + * @return boolean |
|
| 1061 | + * @throws EE_Error |
|
| 1062 | + * @throws InvalidArgumentException |
|
| 1063 | + * @throws ReflectionException |
|
| 1064 | + * @throws InvalidDataTypeException |
|
| 1065 | + * @throws InvalidInterfaceException |
|
| 1066 | + * @since 4.9.80.p |
|
| 1067 | + */ |
|
| 1068 | + public function decreaseReserved($qty = 1, $adjust_datetimes = true, $source = 'unknown') |
|
| 1069 | + { |
|
| 1070 | + $qty = absint($qty); |
|
| 1071 | + $this->add_extra_meta(EE_Ticket::META_KEY_TICKET_RESERVATIONS, "-{$qty} from {$source}"); |
|
| 1072 | + if ($adjust_datetimes) { |
|
| 1073 | + $this->decreaseReservedForDatetimes($qty); |
|
| 1074 | + } |
|
| 1075 | + $success = $this->adjustNumericFieldsInDb( |
|
| 1076 | + [ |
|
| 1077 | + 'TKT_reserved' => $qty * -1, |
|
| 1078 | + ] |
|
| 1079 | + ); |
|
| 1080 | + do_action( |
|
| 1081 | + 'AHEE__EE_Ticket__decrease_reserved', |
|
| 1082 | + $this, |
|
| 1083 | + $qty, |
|
| 1084 | + $this->reserved(), |
|
| 1085 | + $success |
|
| 1086 | + ); |
|
| 1087 | + return $success; |
|
| 1088 | + } |
|
| 1089 | + |
|
| 1090 | + |
|
| 1091 | + /** |
|
| 1092 | + * Decreases the reserved count on the specified datetimes. |
|
| 1093 | + * |
|
| 1094 | + * @param int $qty |
|
| 1095 | + * @param EE_Datetime[] $datetimes |
|
| 1096 | + * @throws EE_Error |
|
| 1097 | + * @throws InvalidArgumentException |
|
| 1098 | + * @throws ReflectionException |
|
| 1099 | + * @throws InvalidDataTypeException |
|
| 1100 | + * @throws InvalidInterfaceException |
|
| 1101 | + * @since 4.9.80.p |
|
| 1102 | + */ |
|
| 1103 | + protected function decreaseReservedForDatetimes($qty = 1, array $datetimes = []) |
|
| 1104 | + { |
|
| 1105 | + $datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes(); |
|
| 1106 | + foreach ($datetimes as $datetime) { |
|
| 1107 | + if ($datetime instanceof EE_Datetime) { |
|
| 1108 | + $datetime->decreaseReserved($qty); |
|
| 1109 | + } |
|
| 1110 | + } |
|
| 1111 | + } |
|
| 1112 | + |
|
| 1113 | + |
|
| 1114 | + /** |
|
| 1115 | + * Gets ticket quantity |
|
| 1116 | + * |
|
| 1117 | + * @param string $context ticket quantity is somewhat subjective depending on the exact information sought |
|
| 1118 | + * therefore $context can be one of three values: '', 'reg_limit', or 'saleable' |
|
| 1119 | + * '' (default) quantity is the actual db value for TKT_qty, unaffected by other objects |
|
| 1120 | + * REG LIMIT: caps qty based on DTT_reg_limit for ALL related datetimes |
|
| 1121 | + * SALEABLE: also considers datetime sold and returns zero if ANY DTT is sold out, and |
|
| 1122 | + * is therefore the truest measure of tickets that can be purchased at the moment |
|
| 1123 | + * @return int |
|
| 1124 | + * @throws EE_Error |
|
| 1125 | + * @throws ReflectionException |
|
| 1126 | + */ |
|
| 1127 | + public function qty($context = '') |
|
| 1128 | + { |
|
| 1129 | + switch ($context) { |
|
| 1130 | + case 'reg_limit': |
|
| 1131 | + return $this->real_quantity_on_ticket(); |
|
| 1132 | + case 'saleable': |
|
| 1133 | + return $this->real_quantity_on_ticket('saleable'); |
|
| 1134 | + default: |
|
| 1135 | + return $this->get_raw('TKT_qty'); |
|
| 1136 | + } |
|
| 1137 | + } |
|
| 1138 | + |
|
| 1139 | + |
|
| 1140 | + /** |
|
| 1141 | + * Gets ticket quantity |
|
| 1142 | + * |
|
| 1143 | + * @param string $context ticket quantity is somewhat subjective depending on the exact information sought |
|
| 1144 | + * therefore $context can be one of two values: 'reg_limit', or 'saleable' |
|
| 1145 | + * REG LIMIT: caps qty based on DTT_reg_limit for ALL related datetimes |
|
| 1146 | + * SALEABLE: also considers datetime sold and returns zero if ANY DTT is sold out, and |
|
| 1147 | + * is therefore the truest measure of tickets that can be purchased at the moment |
|
| 1148 | + * @param int $DTT_ID the primary key for a particular datetime. |
|
| 1149 | + * set to 0 for all related datetimes |
|
| 1150 | + * @return int |
|
| 1151 | + * @throws EE_Error |
|
| 1152 | + * @throws ReflectionException |
|
| 1153 | + */ |
|
| 1154 | + public function real_quantity_on_ticket($context = 'reg_limit', $DTT_ID = 0) |
|
| 1155 | + { |
|
| 1156 | + $raw = $this->get_raw('TKT_qty'); |
|
| 1157 | + // return immediately if it's zero |
|
| 1158 | + if ($raw === 0) { |
|
| 1159 | + return $raw; |
|
| 1160 | + } |
|
| 1161 | + // echo "\n\n<br />Ticket: " . $this->name() . '<br />'; |
|
| 1162 | + // ensure qty doesn't exceed raw value for THIS ticket |
|
| 1163 | + $qty = min(EE_INF, $raw); |
|
| 1164 | + // echo "\n . qty: " . $qty . '<br />'; |
|
| 1165 | + // calculate this ticket's total sales and reservations |
|
| 1166 | + $sold_and_reserved_for_this_ticket = $this->sold() + $this->reserved(); |
|
| 1167 | + // echo "\n . sold: " . $this->sold() . '<br />'; |
|
| 1168 | + // echo "\n . reserved: " . $this->reserved() . '<br />'; |
|
| 1169 | + // echo "\n . sold_and_reserved_for_this_ticket: " . $sold_and_reserved_for_this_ticket . '<br />'; |
|
| 1170 | + // first we need to calculate the maximum number of tickets available for the datetime |
|
| 1171 | + // do we want data for one datetime or all of them ? |
|
| 1172 | + $query_params = $DTT_ID ? [['DTT_ID' => $DTT_ID]] : []; |
|
| 1173 | + $datetimes = $this->datetimes($query_params); |
|
| 1174 | + if (is_array($datetimes) && ! empty($datetimes)) { |
|
| 1175 | + foreach ($datetimes as $datetime) { |
|
| 1176 | + if ($datetime instanceof EE_Datetime) { |
|
| 1177 | + $datetime->refresh_from_db(); |
|
| 1178 | + // echo "\n . . datetime name: " . $datetime->name() . '<br />'; |
|
| 1179 | + // echo "\n . . datetime ID: " . $datetime->ID() . '<br />'; |
|
| 1180 | + // initialize with no restrictions for each datetime |
|
| 1181 | + // but adjust datetime qty based on datetime reg limit |
|
| 1182 | + $datetime_qty = min(EE_INF, $datetime->reg_limit()); |
|
| 1183 | + // echo "\n . . . datetime reg_limit: " . $datetime->reg_limit() . '<br />'; |
|
| 1184 | + // echo "\n . . . datetime_qty: " . $datetime_qty . '<br />'; |
|
| 1185 | + // if we want the actual saleable amount, then we need to consider OTHER ticket sales |
|
| 1186 | + // and reservations for this datetime, that do NOT include sales and reservations |
|
| 1187 | + // for this ticket (so we add $this->sold() and $this->reserved() back in) |
|
| 1188 | + if ($context === 'saleable') { |
|
| 1189 | + $datetime_qty = max( |
|
| 1190 | + $datetime_qty - $datetime->sold_and_reserved() + $sold_and_reserved_for_this_ticket, |
|
| 1191 | + 0 |
|
| 1192 | + ); |
|
| 1193 | + // echo "\n . . . datetime sold: " . $datetime->sold() . '<br />'; |
|
| 1194 | + // echo "\n . . . datetime reserved: " . $datetime->reserved() . '<br />'; |
|
| 1195 | + // echo "\n . . . datetime sold_and_reserved: " . $datetime->sold_and_reserved() . '<br />'; |
|
| 1196 | + // echo "\n . . . datetime_qty: " . $datetime_qty . '<br />'; |
|
| 1197 | + $datetime_qty = ! $datetime->sold_out() ? $datetime_qty : 0; |
|
| 1198 | + // echo "\n . . . datetime_qty: " . $datetime_qty . '<br />'; |
|
| 1199 | + } |
|
| 1200 | + $qty = min($datetime_qty, $qty); |
|
| 1201 | + // echo "\n . . qty: " . $qty . '<br />'; |
|
| 1202 | + } |
|
| 1203 | + } |
|
| 1204 | + } |
|
| 1205 | + // NOW that we know the maximum number of tickets available for the datetime |
|
| 1206 | + // we can finally factor in the details for this specific ticket |
|
| 1207 | + if ($qty > 0 && $context === 'saleable') { |
|
| 1208 | + // and subtract the sales for THIS ticket |
|
| 1209 | + $qty = max($qty - $sold_and_reserved_for_this_ticket, 0); |
|
| 1210 | + // echo "\n . qty: " . $qty . '<br />'; |
|
| 1211 | + } |
|
| 1212 | + // echo "\nFINAL QTY: " . $qty . "<br /><br />"; |
|
| 1213 | + return $qty; |
|
| 1214 | + } |
|
| 1215 | + |
|
| 1216 | + |
|
| 1217 | + /** |
|
| 1218 | + * Sets qty - IMPORTANT!!! Does NOT allow QTY to be set higher than the lowest reg limit of any related datetimes |
|
| 1219 | + * |
|
| 1220 | + * @param int $qty |
|
| 1221 | + * @return void |
|
| 1222 | + * @throws EE_Error |
|
| 1223 | + * @throws ReflectionException |
|
| 1224 | + */ |
|
| 1225 | + public function set_qty($qty) |
|
| 1226 | + { |
|
| 1227 | + $datetimes = $this->datetimes(); |
|
| 1228 | + foreach ($datetimes as $datetime) { |
|
| 1229 | + if ($datetime instanceof EE_Datetime) { |
|
| 1230 | + $qty = min($qty, $datetime->reg_limit()); |
|
| 1231 | + } |
|
| 1232 | + } |
|
| 1233 | + $this->set('TKT_qty', $qty); |
|
| 1234 | + } |
|
| 1235 | + |
|
| 1236 | + |
|
| 1237 | + /** |
|
| 1238 | + * Gets uses |
|
| 1239 | + * |
|
| 1240 | + * @return int |
|
| 1241 | + * @throws EE_Error |
|
| 1242 | + * @throws ReflectionException |
|
| 1243 | + */ |
|
| 1244 | + public function uses() |
|
| 1245 | + { |
|
| 1246 | + return $this->get('TKT_uses'); |
|
| 1247 | + } |
|
| 1248 | + |
|
| 1249 | + |
|
| 1250 | + /** |
|
| 1251 | + * Sets uses |
|
| 1252 | + * |
|
| 1253 | + * @param int $uses |
|
| 1254 | + * @return void |
|
| 1255 | + * @throws EE_Error |
|
| 1256 | + * @throws ReflectionException |
|
| 1257 | + */ |
|
| 1258 | + public function set_uses($uses) |
|
| 1259 | + { |
|
| 1260 | + $this->set('TKT_uses', $uses); |
|
| 1261 | + } |
|
| 1262 | + |
|
| 1263 | + |
|
| 1264 | + /** |
|
| 1265 | + * returns whether ticket is required or not. |
|
| 1266 | + * |
|
| 1267 | + * @return boolean |
|
| 1268 | + * @throws EE_Error |
|
| 1269 | + * @throws ReflectionException |
|
| 1270 | + */ |
|
| 1271 | + public function required() |
|
| 1272 | + { |
|
| 1273 | + return $this->get('TKT_required'); |
|
| 1274 | + } |
|
| 1275 | + |
|
| 1276 | + |
|
| 1277 | + /** |
|
| 1278 | + * sets the TKT_required property |
|
| 1279 | + * |
|
| 1280 | + * @param boolean $required |
|
| 1281 | + * @return void |
|
| 1282 | + * @throws EE_Error |
|
| 1283 | + * @throws ReflectionException |
|
| 1284 | + */ |
|
| 1285 | + public function set_required($required) |
|
| 1286 | + { |
|
| 1287 | + $this->set('TKT_required', $required); |
|
| 1288 | + } |
|
| 1289 | + |
|
| 1290 | + |
|
| 1291 | + /** |
|
| 1292 | + * Gets taxable |
|
| 1293 | + * |
|
| 1294 | + * @return boolean |
|
| 1295 | + * @throws EE_Error |
|
| 1296 | + * @throws ReflectionException |
|
| 1297 | + */ |
|
| 1298 | + public function taxable() |
|
| 1299 | + { |
|
| 1300 | + return $this->get('TKT_taxable'); |
|
| 1301 | + } |
|
| 1302 | + |
|
| 1303 | + |
|
| 1304 | + /** |
|
| 1305 | + * Sets taxable |
|
| 1306 | + * |
|
| 1307 | + * @param boolean $taxable |
|
| 1308 | + * @return void |
|
| 1309 | + * @throws EE_Error |
|
| 1310 | + * @throws ReflectionException |
|
| 1311 | + */ |
|
| 1312 | + public function set_taxable($taxable) |
|
| 1313 | + { |
|
| 1314 | + $this->set('TKT_taxable', $taxable); |
|
| 1315 | + } |
|
| 1316 | + |
|
| 1317 | + |
|
| 1318 | + /** |
|
| 1319 | + * Gets is_default |
|
| 1320 | + * |
|
| 1321 | + * @return boolean |
|
| 1322 | + * @throws EE_Error |
|
| 1323 | + * @throws ReflectionException |
|
| 1324 | + */ |
|
| 1325 | + public function is_default() |
|
| 1326 | + { |
|
| 1327 | + return $this->get('TKT_is_default'); |
|
| 1328 | + } |
|
| 1329 | + |
|
| 1330 | + |
|
| 1331 | + /** |
|
| 1332 | + * Sets is_default |
|
| 1333 | + * |
|
| 1334 | + * @param boolean $is_default |
|
| 1335 | + * @return void |
|
| 1336 | + * @throws EE_Error |
|
| 1337 | + * @throws ReflectionException |
|
| 1338 | + */ |
|
| 1339 | + public function set_is_default($is_default) |
|
| 1340 | + { |
|
| 1341 | + $this->set('TKT_is_default', $is_default); |
|
| 1342 | + } |
|
| 1343 | + |
|
| 1344 | + |
|
| 1345 | + /** |
|
| 1346 | + * Gets order |
|
| 1347 | + * |
|
| 1348 | + * @return int |
|
| 1349 | + * @throws EE_Error |
|
| 1350 | + * @throws ReflectionException |
|
| 1351 | + */ |
|
| 1352 | + public function order() |
|
| 1353 | + { |
|
| 1354 | + return $this->get('TKT_order'); |
|
| 1355 | + } |
|
| 1356 | + |
|
| 1357 | + |
|
| 1358 | + /** |
|
| 1359 | + * Sets order |
|
| 1360 | + * |
|
| 1361 | + * @param int $order |
|
| 1362 | + * @return void |
|
| 1363 | + * @throws EE_Error |
|
| 1364 | + * @throws ReflectionException |
|
| 1365 | + */ |
|
| 1366 | + public function set_order($order) |
|
| 1367 | + { |
|
| 1368 | + $this->set('TKT_order', $order); |
|
| 1369 | + } |
|
| 1370 | + |
|
| 1371 | + |
|
| 1372 | + /** |
|
| 1373 | + * Gets row |
|
| 1374 | + * |
|
| 1375 | + * @return int |
|
| 1376 | + * @throws EE_Error |
|
| 1377 | + * @throws ReflectionException |
|
| 1378 | + */ |
|
| 1379 | + public function row() |
|
| 1380 | + { |
|
| 1381 | + return $this->get('TKT_row'); |
|
| 1382 | + } |
|
| 1383 | + |
|
| 1384 | + |
|
| 1385 | + /** |
|
| 1386 | + * Sets row |
|
| 1387 | + * |
|
| 1388 | + * @param int $row |
|
| 1389 | + * @return void |
|
| 1390 | + * @throws EE_Error |
|
| 1391 | + * @throws ReflectionException |
|
| 1392 | + */ |
|
| 1393 | + public function set_row($row) |
|
| 1394 | + { |
|
| 1395 | + $this->set('TKT_row', $row); |
|
| 1396 | + } |
|
| 1397 | + |
|
| 1398 | + |
|
| 1399 | + /** |
|
| 1400 | + * Gets deleted |
|
| 1401 | + * |
|
| 1402 | + * @return boolean |
|
| 1403 | + * @throws EE_Error |
|
| 1404 | + * @throws ReflectionException |
|
| 1405 | + */ |
|
| 1406 | + public function deleted() |
|
| 1407 | + { |
|
| 1408 | + return $this->get('TKT_deleted'); |
|
| 1409 | + } |
|
| 1410 | + |
|
| 1411 | + |
|
| 1412 | + /** |
|
| 1413 | + * Sets deleted |
|
| 1414 | + * |
|
| 1415 | + * @param boolean $deleted |
|
| 1416 | + * @return void |
|
| 1417 | + * @throws EE_Error |
|
| 1418 | + * @throws ReflectionException |
|
| 1419 | + */ |
|
| 1420 | + public function set_deleted($deleted) |
|
| 1421 | + { |
|
| 1422 | + $this->set('TKT_deleted', $deleted); |
|
| 1423 | + } |
|
| 1424 | + |
|
| 1425 | + |
|
| 1426 | + /** |
|
| 1427 | + * Gets parent |
|
| 1428 | + * |
|
| 1429 | + * @return int |
|
| 1430 | + * @throws EE_Error |
|
| 1431 | + * @throws ReflectionException |
|
| 1432 | + */ |
|
| 1433 | + public function parent_ID() |
|
| 1434 | + { |
|
| 1435 | + return $this->get('TKT_parent'); |
|
| 1436 | + } |
|
| 1437 | + |
|
| 1438 | + |
|
| 1439 | + /** |
|
| 1440 | + * Sets parent |
|
| 1441 | + * |
|
| 1442 | + * @param int $parent |
|
| 1443 | + * @return void |
|
| 1444 | + * @throws EE_Error |
|
| 1445 | + * @throws ReflectionException |
|
| 1446 | + */ |
|
| 1447 | + public function set_parent_ID($parent) |
|
| 1448 | + { |
|
| 1449 | + $this->set('TKT_parent', $parent); |
|
| 1450 | + } |
|
| 1451 | + |
|
| 1452 | + |
|
| 1453 | + /** |
|
| 1454 | + * Gets a string which is handy for showing in gateways etc that describes the ticket. |
|
| 1455 | + * |
|
| 1456 | + * @return string |
|
| 1457 | + * @throws EE_Error |
|
| 1458 | + * @throws ReflectionException |
|
| 1459 | + */ |
|
| 1460 | + public function name_and_info() |
|
| 1461 | + { |
|
| 1462 | + $times = []; |
|
| 1463 | + foreach ($this->datetimes() as $datetime) { |
|
| 1464 | + $times[] = $datetime->start_date_and_time(); |
|
| 1465 | + } |
|
| 1466 | + return $this->name() . ' @ ' . implode(', ', $times) . ' for ' . $this->pretty_price(); |
|
| 1467 | + } |
|
| 1468 | + |
|
| 1469 | + |
|
| 1470 | + /** |
|
| 1471 | + * Gets name |
|
| 1472 | + * |
|
| 1473 | + * @return string |
|
| 1474 | + * @throws EE_Error |
|
| 1475 | + * @throws ReflectionException |
|
| 1476 | + */ |
|
| 1477 | + public function name() |
|
| 1478 | + { |
|
| 1479 | + return $this->get('TKT_name'); |
|
| 1480 | + } |
|
| 1481 | + |
|
| 1482 | + |
|
| 1483 | + /** |
|
| 1484 | + * Gets price |
|
| 1485 | + * |
|
| 1486 | + * @return float |
|
| 1487 | + * @throws EE_Error |
|
| 1488 | + * @throws ReflectionException |
|
| 1489 | + */ |
|
| 1490 | + public function price() |
|
| 1491 | + { |
|
| 1492 | + return $this->get('TKT_price'); |
|
| 1493 | + } |
|
| 1494 | + |
|
| 1495 | + |
|
| 1496 | + /** |
|
| 1497 | + * Gets all the registrations for this ticket |
|
| 1498 | + * |
|
| 1499 | + * @param array $query_params |
|
| 1500 | + * @return EE_Registration[]|EE_Base_Class[] |
|
| 1501 | + * @throws EE_Error |
|
| 1502 | + * @throws ReflectionException |
|
| 1503 | + * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md |
|
| 1504 | + */ |
|
| 1505 | + public function registrations($query_params = []) |
|
| 1506 | + { |
|
| 1507 | + return $this->get_many_related('Registration', $query_params); |
|
| 1508 | + } |
|
| 1509 | + |
|
| 1510 | + |
|
| 1511 | + /** |
|
| 1512 | + * Updates the TKT_sold attribute (and saves) based on the number of APPROVED registrations for this ticket. |
|
| 1513 | + * |
|
| 1514 | + * @return int |
|
| 1515 | + * @throws EE_Error |
|
| 1516 | + * @throws ReflectionException |
|
| 1517 | + */ |
|
| 1518 | + public function update_tickets_sold() |
|
| 1519 | + { |
|
| 1520 | + $count_regs_for_this_ticket = $this->count_registrations( |
|
| 1521 | + [ |
|
| 1522 | + [ |
|
| 1523 | + 'STS_ID' => EEM_Registration::status_id_approved, |
|
| 1524 | + 'REG_deleted' => 0, |
|
| 1525 | + ], |
|
| 1526 | + ] |
|
| 1527 | + ); |
|
| 1528 | + $this->set_sold($count_regs_for_this_ticket); |
|
| 1529 | + $this->save(); |
|
| 1530 | + return $count_regs_for_this_ticket; |
|
| 1531 | + } |
|
| 1532 | + |
|
| 1533 | + |
|
| 1534 | + /** |
|
| 1535 | + * Counts the registrations for this ticket |
|
| 1536 | + * |
|
| 1537 | + * @param array $query_params |
|
| 1538 | + * @return int |
|
| 1539 | + * @throws EE_Error |
|
| 1540 | + * @throws ReflectionException |
|
| 1541 | + * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md |
|
| 1542 | + */ |
|
| 1543 | + public function count_registrations($query_params = []) |
|
| 1544 | + { |
|
| 1545 | + return $this->count_related('Registration', $query_params); |
|
| 1546 | + } |
|
| 1547 | + |
|
| 1548 | + |
|
| 1549 | + /** |
|
| 1550 | + * Implementation for EEI_Has_Icon interface method. |
|
| 1551 | + * |
|
| 1552 | + * @return string |
|
| 1553 | + * @see EEI_Visual_Representation for comments |
|
| 1554 | + */ |
|
| 1555 | + public function get_icon() |
|
| 1556 | + { |
|
| 1557 | + return '<span class="dashicons dashicons-tickets-alt"></span>'; |
|
| 1558 | + } |
|
| 1559 | + |
|
| 1560 | + |
|
| 1561 | + /** |
|
| 1562 | + * Implementation of the EEI_Event_Relation interface method |
|
| 1563 | + * |
|
| 1564 | + * @return EE_Event |
|
| 1565 | + * @throws EE_Error |
|
| 1566 | + * @throws UnexpectedEntityException |
|
| 1567 | + * @throws ReflectionException |
|
| 1568 | + * @see EEI_Event_Relation for comments |
|
| 1569 | + */ |
|
| 1570 | + public function get_related_event() |
|
| 1571 | + { |
|
| 1572 | + // get one datetime to use for getting the event |
|
| 1573 | + $datetime = $this->first_datetime(); |
|
| 1574 | + if (! $datetime instanceof EE_Datetime) { |
|
| 1575 | + throw new UnexpectedEntityException( |
|
| 1576 | + $datetime, |
|
| 1577 | + 'EE_Datetime', |
|
| 1578 | + sprintf( |
|
| 1579 | + esc_html__('The ticket (%s) is not associated with any valid datetimes.', 'event_espresso'), |
|
| 1580 | + $this->name() |
|
| 1581 | + ) |
|
| 1582 | + ); |
|
| 1583 | + } |
|
| 1584 | + $event = $datetime->event(); |
|
| 1585 | + if (! $event instanceof EE_Event) { |
|
| 1586 | + throw new UnexpectedEntityException( |
|
| 1587 | + $event, |
|
| 1588 | + 'EE_Event', |
|
| 1589 | + sprintf( |
|
| 1590 | + esc_html__('The ticket (%s) is not associated with a valid event.', 'event_espresso'), |
|
| 1591 | + $this->name() |
|
| 1592 | + ) |
|
| 1593 | + ); |
|
| 1594 | + } |
|
| 1595 | + return $event; |
|
| 1596 | + } |
|
| 1597 | + |
|
| 1598 | + |
|
| 1599 | + /** |
|
| 1600 | + * Implementation of the EEI_Event_Relation interface method |
|
| 1601 | + * |
|
| 1602 | + * @return string |
|
| 1603 | + * @throws UnexpectedEntityException |
|
| 1604 | + * @throws EE_Error |
|
| 1605 | + * @throws ReflectionException |
|
| 1606 | + * @see EEI_Event_Relation for comments |
|
| 1607 | + */ |
|
| 1608 | + public function get_event_name() |
|
| 1609 | + { |
|
| 1610 | + $event = $this->get_related_event(); |
|
| 1611 | + return $event instanceof EE_Event ? $event->name() : ''; |
|
| 1612 | + } |
|
| 1613 | + |
|
| 1614 | + |
|
| 1615 | + /** |
|
| 1616 | + * Implementation of the EEI_Event_Relation interface method |
|
| 1617 | + * |
|
| 1618 | + * @return int |
|
| 1619 | + * @throws UnexpectedEntityException |
|
| 1620 | + * @throws EE_Error |
|
| 1621 | + * @throws ReflectionException |
|
| 1622 | + * @see EEI_Event_Relation for comments |
|
| 1623 | + */ |
|
| 1624 | + public function get_event_ID() |
|
| 1625 | + { |
|
| 1626 | + $event = $this->get_related_event(); |
|
| 1627 | + return $event instanceof EE_Event ? $event->ID() : 0; |
|
| 1628 | + } |
|
| 1629 | + |
|
| 1630 | + |
|
| 1631 | + /** |
|
| 1632 | + * This simply returns whether a ticket can be permanently deleted or not. |
|
| 1633 | + * The criteria for determining this is whether the ticket has any related registrations. |
|
| 1634 | + * If there are none then it can be permanently deleted. |
|
| 1635 | + * |
|
| 1636 | + * @return bool |
|
| 1637 | + * @throws EE_Error |
|
| 1638 | + * @throws ReflectionException |
|
| 1639 | + */ |
|
| 1640 | + public function is_permanently_deleteable() |
|
| 1641 | + { |
|
| 1642 | + return $this->count_registrations() === 0; |
|
| 1643 | + } |
|
| 1644 | + |
|
| 1645 | + |
|
| 1646 | + /******************************************************************* |
|
| 1647 | 1647 | *********************** DEPRECATED METHODS ********************** |
| 1648 | 1648 | *******************************************************************/ |
| 1649 | 1649 | |
| 1650 | 1650 | |
| 1651 | - /** |
|
| 1652 | - * Increments sold by amount passed by $qty AND decrements the reserved count on both this ticket and its |
|
| 1653 | - * associated datetimes. |
|
| 1654 | - * |
|
| 1655 | - * @param int $qty |
|
| 1656 | - * @return void |
|
| 1657 | - * @throws EE_Error |
|
| 1658 | - * @throws InvalidArgumentException |
|
| 1659 | - * @throws InvalidDataTypeException |
|
| 1660 | - * @throws InvalidInterfaceException |
|
| 1661 | - * @throws ReflectionException |
|
| 1662 | - * @deprecated 4.9.80.p |
|
| 1663 | - */ |
|
| 1664 | - public function increase_sold($qty = 1) |
|
| 1665 | - { |
|
| 1666 | - EE_Error::doing_it_wrong( |
|
| 1667 | - __FUNCTION__, |
|
| 1668 | - esc_html__('Please use EE_Ticket::increaseSold() instead', 'event_espresso'), |
|
| 1669 | - '4.9.80.p', |
|
| 1670 | - '5.0.0.p' |
|
| 1671 | - ); |
|
| 1672 | - $this->increaseSold($qty); |
|
| 1673 | - } |
|
| 1674 | - |
|
| 1675 | - |
|
| 1676 | - /** |
|
| 1677 | - * On each datetime related to this ticket, increases its sold count and decreases its reserved count by $qty. |
|
| 1678 | - * |
|
| 1679 | - * @param int $qty positive or negative. Positive means to increase sold counts (and decrease reserved counts), |
|
| 1680 | - * Negative means to decreases old counts (and increase reserved counts). |
|
| 1681 | - * @throws EE_Error |
|
| 1682 | - * @throws InvalidArgumentException |
|
| 1683 | - * @throws InvalidDataTypeException |
|
| 1684 | - * @throws InvalidInterfaceException |
|
| 1685 | - * @throws ReflectionException |
|
| 1686 | - * @deprecated 4.9.80.p |
|
| 1687 | - */ |
|
| 1688 | - protected function _increase_sold_for_datetimes($qty) |
|
| 1689 | - { |
|
| 1690 | - EE_Error::doing_it_wrong( |
|
| 1691 | - __FUNCTION__, |
|
| 1692 | - esc_html__('Please use EE_Ticket::increaseSoldForDatetimes() instead', 'event_espresso'), |
|
| 1693 | - '4.9.80.p', |
|
| 1694 | - '5.0.0.p' |
|
| 1695 | - ); |
|
| 1696 | - $this->increaseSoldForDatetimes($qty); |
|
| 1697 | - } |
|
| 1698 | - |
|
| 1699 | - |
|
| 1700 | - /** |
|
| 1701 | - * Decrements (subtracts) sold by amount passed by $qty on both the ticket and its related datetimes directly in the |
|
| 1702 | - * DB and then updates the model objects. |
|
| 1703 | - * Does not affect the reserved counts. |
|
| 1704 | - * |
|
| 1705 | - * @param int $qty |
|
| 1706 | - * @return void |
|
| 1707 | - * @throws EE_Error |
|
| 1708 | - * @throws InvalidArgumentException |
|
| 1709 | - * @throws InvalidDataTypeException |
|
| 1710 | - * @throws InvalidInterfaceException |
|
| 1711 | - * @throws ReflectionException |
|
| 1712 | - * @deprecated 4.9.80.p |
|
| 1713 | - */ |
|
| 1714 | - public function decrease_sold($qty = 1) |
|
| 1715 | - { |
|
| 1716 | - EE_Error::doing_it_wrong( |
|
| 1717 | - __FUNCTION__, |
|
| 1718 | - esc_html__('Please use EE_Ticket::decreaseSold() instead', 'event_espresso'), |
|
| 1719 | - '4.9.80.p', |
|
| 1720 | - '5.0.0.p' |
|
| 1721 | - ); |
|
| 1722 | - $this->decreaseSold($qty); |
|
| 1723 | - } |
|
| 1724 | - |
|
| 1725 | - |
|
| 1726 | - /** |
|
| 1727 | - * Decreases sold on related datetimes |
|
| 1728 | - * |
|
| 1729 | - * @param int $qty |
|
| 1730 | - * @return void |
|
| 1731 | - * @throws EE_Error |
|
| 1732 | - * @throws InvalidArgumentException |
|
| 1733 | - * @throws InvalidDataTypeException |
|
| 1734 | - * @throws InvalidInterfaceException |
|
| 1735 | - * @throws ReflectionException |
|
| 1736 | - * @deprecated 4.9.80.p |
|
| 1737 | - */ |
|
| 1738 | - protected function _decrease_sold_for_datetimes($qty = 1) |
|
| 1739 | - { |
|
| 1740 | - EE_Error::doing_it_wrong( |
|
| 1741 | - __FUNCTION__, |
|
| 1742 | - esc_html__('Please use EE_Ticket::decreaseSoldForDatetimes() instead', 'event_espresso'), |
|
| 1743 | - '4.9.80.p', |
|
| 1744 | - '5.0.0.p' |
|
| 1745 | - ); |
|
| 1746 | - $this->decreaseSoldForDatetimes($qty); |
|
| 1747 | - } |
|
| 1748 | - |
|
| 1749 | - |
|
| 1750 | - /** |
|
| 1751 | - * Increments reserved by amount passed by $qty, and persists it immediately to the database. |
|
| 1752 | - * |
|
| 1753 | - * @param int $qty |
|
| 1754 | - * @param string $source |
|
| 1755 | - * @return bool whether we successfully reserved the ticket or not. |
|
| 1756 | - * @throws EE_Error |
|
| 1757 | - * @throws InvalidArgumentException |
|
| 1758 | - * @throws ReflectionException |
|
| 1759 | - * @throws InvalidDataTypeException |
|
| 1760 | - * @throws InvalidInterfaceException |
|
| 1761 | - * @deprecated 4.9.80.p |
|
| 1762 | - */ |
|
| 1763 | - public function increase_reserved($qty = 1, $source = 'unknown') |
|
| 1764 | - { |
|
| 1765 | - EE_Error::doing_it_wrong( |
|
| 1766 | - __FUNCTION__, |
|
| 1767 | - esc_html__('Please use EE_Ticket::increaseReserved() instead', 'event_espresso'), |
|
| 1768 | - '4.9.80.p', |
|
| 1769 | - '5.0.0.p' |
|
| 1770 | - ); |
|
| 1771 | - return $this->increaseReserved($qty); |
|
| 1772 | - } |
|
| 1773 | - |
|
| 1774 | - |
|
| 1775 | - /** |
|
| 1776 | - * Increases sold on related datetimes |
|
| 1777 | - * |
|
| 1778 | - * @param int $qty |
|
| 1779 | - * @return boolean indicating success |
|
| 1780 | - * @throws EE_Error |
|
| 1781 | - * @throws InvalidArgumentException |
|
| 1782 | - * @throws InvalidDataTypeException |
|
| 1783 | - * @throws InvalidInterfaceException |
|
| 1784 | - * @throws ReflectionException |
|
| 1785 | - * @deprecated 4.9.80.p |
|
| 1786 | - */ |
|
| 1787 | - protected function _increase_reserved_for_datetimes($qty = 1) |
|
| 1788 | - { |
|
| 1789 | - EE_Error::doing_it_wrong( |
|
| 1790 | - __FUNCTION__, |
|
| 1791 | - esc_html__('Please use EE_Ticket::increaseReservedForDatetimes() instead', 'event_espresso'), |
|
| 1792 | - '4.9.80.p', |
|
| 1793 | - '5.0.0.p' |
|
| 1794 | - ); |
|
| 1795 | - return $this->increaseReservedForDatetimes($qty); |
|
| 1796 | - } |
|
| 1797 | - |
|
| 1798 | - |
|
| 1799 | - /** |
|
| 1800 | - * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database. |
|
| 1801 | - * |
|
| 1802 | - * @param int $qty |
|
| 1803 | - * @param bool $adjust_datetimes |
|
| 1804 | - * @param string $source |
|
| 1805 | - * @return void |
|
| 1806 | - * @throws EE_Error |
|
| 1807 | - * @throws InvalidArgumentException |
|
| 1808 | - * @throws ReflectionException |
|
| 1809 | - * @throws InvalidDataTypeException |
|
| 1810 | - * @throws InvalidInterfaceException |
|
| 1811 | - * @deprecated 4.9.80.p |
|
| 1812 | - */ |
|
| 1813 | - public function decrease_reserved($qty = 1, $adjust_datetimes = true, $source = 'unknown') |
|
| 1814 | - { |
|
| 1815 | - EE_Error::doing_it_wrong( |
|
| 1816 | - __FUNCTION__, |
|
| 1817 | - esc_html__('Please use EE_Ticket::decreaseReserved() instead', 'event_espresso'), |
|
| 1818 | - '4.9.80.p', |
|
| 1819 | - '5.0.0.p' |
|
| 1820 | - ); |
|
| 1821 | - $this->decreaseReserved($qty); |
|
| 1822 | - } |
|
| 1823 | - |
|
| 1824 | - |
|
| 1825 | - /** |
|
| 1826 | - * Decreases reserved on related datetimes |
|
| 1827 | - * |
|
| 1828 | - * @param int $qty |
|
| 1829 | - * @return void |
|
| 1830 | - * @throws EE_Error |
|
| 1831 | - * @throws InvalidArgumentException |
|
| 1832 | - * @throws ReflectionException |
|
| 1833 | - * @throws InvalidDataTypeException |
|
| 1834 | - * @throws InvalidInterfaceException |
|
| 1835 | - * @deprecated 4.9.80.p |
|
| 1836 | - */ |
|
| 1837 | - protected function _decrease_reserved_for_datetimes($qty = 1) |
|
| 1838 | - { |
|
| 1839 | - EE_Error::doing_it_wrong( |
|
| 1840 | - __FUNCTION__, |
|
| 1841 | - esc_html__('Please use EE_Ticket::decreaseReservedForDatetimes() instead', 'event_espresso'), |
|
| 1842 | - '4.9.80.p', |
|
| 1843 | - '5.0.0.p' |
|
| 1844 | - ); |
|
| 1845 | - $this->decreaseReservedForDatetimes($qty); |
|
| 1846 | - } |
|
| 1651 | + /** |
|
| 1652 | + * Increments sold by amount passed by $qty AND decrements the reserved count on both this ticket and its |
|
| 1653 | + * associated datetimes. |
|
| 1654 | + * |
|
| 1655 | + * @param int $qty |
|
| 1656 | + * @return void |
|
| 1657 | + * @throws EE_Error |
|
| 1658 | + * @throws InvalidArgumentException |
|
| 1659 | + * @throws InvalidDataTypeException |
|
| 1660 | + * @throws InvalidInterfaceException |
|
| 1661 | + * @throws ReflectionException |
|
| 1662 | + * @deprecated 4.9.80.p |
|
| 1663 | + */ |
|
| 1664 | + public function increase_sold($qty = 1) |
|
| 1665 | + { |
|
| 1666 | + EE_Error::doing_it_wrong( |
|
| 1667 | + __FUNCTION__, |
|
| 1668 | + esc_html__('Please use EE_Ticket::increaseSold() instead', 'event_espresso'), |
|
| 1669 | + '4.9.80.p', |
|
| 1670 | + '5.0.0.p' |
|
| 1671 | + ); |
|
| 1672 | + $this->increaseSold($qty); |
|
| 1673 | + } |
|
| 1674 | + |
|
| 1675 | + |
|
| 1676 | + /** |
|
| 1677 | + * On each datetime related to this ticket, increases its sold count and decreases its reserved count by $qty. |
|
| 1678 | + * |
|
| 1679 | + * @param int $qty positive or negative. Positive means to increase sold counts (and decrease reserved counts), |
|
| 1680 | + * Negative means to decreases old counts (and increase reserved counts). |
|
| 1681 | + * @throws EE_Error |
|
| 1682 | + * @throws InvalidArgumentException |
|
| 1683 | + * @throws InvalidDataTypeException |
|
| 1684 | + * @throws InvalidInterfaceException |
|
| 1685 | + * @throws ReflectionException |
|
| 1686 | + * @deprecated 4.9.80.p |
|
| 1687 | + */ |
|
| 1688 | + protected function _increase_sold_for_datetimes($qty) |
|
| 1689 | + { |
|
| 1690 | + EE_Error::doing_it_wrong( |
|
| 1691 | + __FUNCTION__, |
|
| 1692 | + esc_html__('Please use EE_Ticket::increaseSoldForDatetimes() instead', 'event_espresso'), |
|
| 1693 | + '4.9.80.p', |
|
| 1694 | + '5.0.0.p' |
|
| 1695 | + ); |
|
| 1696 | + $this->increaseSoldForDatetimes($qty); |
|
| 1697 | + } |
|
| 1698 | + |
|
| 1699 | + |
|
| 1700 | + /** |
|
| 1701 | + * Decrements (subtracts) sold by amount passed by $qty on both the ticket and its related datetimes directly in the |
|
| 1702 | + * DB and then updates the model objects. |
|
| 1703 | + * Does not affect the reserved counts. |
|
| 1704 | + * |
|
| 1705 | + * @param int $qty |
|
| 1706 | + * @return void |
|
| 1707 | + * @throws EE_Error |
|
| 1708 | + * @throws InvalidArgumentException |
|
| 1709 | + * @throws InvalidDataTypeException |
|
| 1710 | + * @throws InvalidInterfaceException |
|
| 1711 | + * @throws ReflectionException |
|
| 1712 | + * @deprecated 4.9.80.p |
|
| 1713 | + */ |
|
| 1714 | + public function decrease_sold($qty = 1) |
|
| 1715 | + { |
|
| 1716 | + EE_Error::doing_it_wrong( |
|
| 1717 | + __FUNCTION__, |
|
| 1718 | + esc_html__('Please use EE_Ticket::decreaseSold() instead', 'event_espresso'), |
|
| 1719 | + '4.9.80.p', |
|
| 1720 | + '5.0.0.p' |
|
| 1721 | + ); |
|
| 1722 | + $this->decreaseSold($qty); |
|
| 1723 | + } |
|
| 1724 | + |
|
| 1725 | + |
|
| 1726 | + /** |
|
| 1727 | + * Decreases sold on related datetimes |
|
| 1728 | + * |
|
| 1729 | + * @param int $qty |
|
| 1730 | + * @return void |
|
| 1731 | + * @throws EE_Error |
|
| 1732 | + * @throws InvalidArgumentException |
|
| 1733 | + * @throws InvalidDataTypeException |
|
| 1734 | + * @throws InvalidInterfaceException |
|
| 1735 | + * @throws ReflectionException |
|
| 1736 | + * @deprecated 4.9.80.p |
|
| 1737 | + */ |
|
| 1738 | + protected function _decrease_sold_for_datetimes($qty = 1) |
|
| 1739 | + { |
|
| 1740 | + EE_Error::doing_it_wrong( |
|
| 1741 | + __FUNCTION__, |
|
| 1742 | + esc_html__('Please use EE_Ticket::decreaseSoldForDatetimes() instead', 'event_espresso'), |
|
| 1743 | + '4.9.80.p', |
|
| 1744 | + '5.0.0.p' |
|
| 1745 | + ); |
|
| 1746 | + $this->decreaseSoldForDatetimes($qty); |
|
| 1747 | + } |
|
| 1748 | + |
|
| 1749 | + |
|
| 1750 | + /** |
|
| 1751 | + * Increments reserved by amount passed by $qty, and persists it immediately to the database. |
|
| 1752 | + * |
|
| 1753 | + * @param int $qty |
|
| 1754 | + * @param string $source |
|
| 1755 | + * @return bool whether we successfully reserved the ticket or not. |
|
| 1756 | + * @throws EE_Error |
|
| 1757 | + * @throws InvalidArgumentException |
|
| 1758 | + * @throws ReflectionException |
|
| 1759 | + * @throws InvalidDataTypeException |
|
| 1760 | + * @throws InvalidInterfaceException |
|
| 1761 | + * @deprecated 4.9.80.p |
|
| 1762 | + */ |
|
| 1763 | + public function increase_reserved($qty = 1, $source = 'unknown') |
|
| 1764 | + { |
|
| 1765 | + EE_Error::doing_it_wrong( |
|
| 1766 | + __FUNCTION__, |
|
| 1767 | + esc_html__('Please use EE_Ticket::increaseReserved() instead', 'event_espresso'), |
|
| 1768 | + '4.9.80.p', |
|
| 1769 | + '5.0.0.p' |
|
| 1770 | + ); |
|
| 1771 | + return $this->increaseReserved($qty); |
|
| 1772 | + } |
|
| 1773 | + |
|
| 1774 | + |
|
| 1775 | + /** |
|
| 1776 | + * Increases sold on related datetimes |
|
| 1777 | + * |
|
| 1778 | + * @param int $qty |
|
| 1779 | + * @return boolean indicating success |
|
| 1780 | + * @throws EE_Error |
|
| 1781 | + * @throws InvalidArgumentException |
|
| 1782 | + * @throws InvalidDataTypeException |
|
| 1783 | + * @throws InvalidInterfaceException |
|
| 1784 | + * @throws ReflectionException |
|
| 1785 | + * @deprecated 4.9.80.p |
|
| 1786 | + */ |
|
| 1787 | + protected function _increase_reserved_for_datetimes($qty = 1) |
|
| 1788 | + { |
|
| 1789 | + EE_Error::doing_it_wrong( |
|
| 1790 | + __FUNCTION__, |
|
| 1791 | + esc_html__('Please use EE_Ticket::increaseReservedForDatetimes() instead', 'event_espresso'), |
|
| 1792 | + '4.9.80.p', |
|
| 1793 | + '5.0.0.p' |
|
| 1794 | + ); |
|
| 1795 | + return $this->increaseReservedForDatetimes($qty); |
|
| 1796 | + } |
|
| 1797 | + |
|
| 1798 | + |
|
| 1799 | + /** |
|
| 1800 | + * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database. |
|
| 1801 | + * |
|
| 1802 | + * @param int $qty |
|
| 1803 | + * @param bool $adjust_datetimes |
|
| 1804 | + * @param string $source |
|
| 1805 | + * @return void |
|
| 1806 | + * @throws EE_Error |
|
| 1807 | + * @throws InvalidArgumentException |
|
| 1808 | + * @throws ReflectionException |
|
| 1809 | + * @throws InvalidDataTypeException |
|
| 1810 | + * @throws InvalidInterfaceException |
|
| 1811 | + * @deprecated 4.9.80.p |
|
| 1812 | + */ |
|
| 1813 | + public function decrease_reserved($qty = 1, $adjust_datetimes = true, $source = 'unknown') |
|
| 1814 | + { |
|
| 1815 | + EE_Error::doing_it_wrong( |
|
| 1816 | + __FUNCTION__, |
|
| 1817 | + esc_html__('Please use EE_Ticket::decreaseReserved() instead', 'event_espresso'), |
|
| 1818 | + '4.9.80.p', |
|
| 1819 | + '5.0.0.p' |
|
| 1820 | + ); |
|
| 1821 | + $this->decreaseReserved($qty); |
|
| 1822 | + } |
|
| 1823 | + |
|
| 1824 | + |
|
| 1825 | + /** |
|
| 1826 | + * Decreases reserved on related datetimes |
|
| 1827 | + * |
|
| 1828 | + * @param int $qty |
|
| 1829 | + * @return void |
|
| 1830 | + * @throws EE_Error |
|
| 1831 | + * @throws InvalidArgumentException |
|
| 1832 | + * @throws ReflectionException |
|
| 1833 | + * @throws InvalidDataTypeException |
|
| 1834 | + * @throws InvalidInterfaceException |
|
| 1835 | + * @deprecated 4.9.80.p |
|
| 1836 | + */ |
|
| 1837 | + protected function _decrease_reserved_for_datetimes($qty = 1) |
|
| 1838 | + { |
|
| 1839 | + EE_Error::doing_it_wrong( |
|
| 1840 | + __FUNCTION__, |
|
| 1841 | + esc_html__('Please use EE_Ticket::decreaseReservedForDatetimes() instead', 'event_espresso'), |
|
| 1842 | + '4.9.80.p', |
|
| 1843 | + '5.0.0.p' |
|
| 1844 | + ); |
|
| 1845 | + $this->decreaseReservedForDatetimes($qty); |
|
| 1846 | + } |
|
| 1847 | 1847 | } |
@@ -66,7 +66,7 @@ discard block |
||
| 66 | 66 | public static function new_instance($props_n_values = [], $timezone = null, $date_formats = []) |
| 67 | 67 | { |
| 68 | 68 | $ticket = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats); |
| 69 | - if (! $ticket instanceof EE_Ticket) { |
|
| 69 | + if ( ! $ticket instanceof EE_Ticket) { |
|
| 70 | 70 | $ticket = new EE_Ticket($props_n_values, false, $timezone, $date_formats); |
| 71 | 71 | } |
| 72 | 72 | return $ticket; |
@@ -150,7 +150,7 @@ discard block |
||
| 150 | 150 | public function ticket_status($display = false, $remaining = null) |
| 151 | 151 | { |
| 152 | 152 | $remaining = is_bool($remaining) ? $remaining : $this->is_remaining(); |
| 153 | - if (! $remaining) { |
|
| 153 | + if ( ! $remaining) { |
|
| 154 | 154 | return $display |
| 155 | 155 | ? EEH_Template::pretty_status(EE_Ticket::sold_out, false, 'sentence') |
| 156 | 156 | : EE_Ticket::sold_out; |
@@ -287,7 +287,7 @@ discard block |
||
| 287 | 287 | ? $this->last_datetime()->get_i18n_datetime('DTT_EVT_end', $date_format) |
| 288 | 288 | : ''; |
| 289 | 289 | |
| 290 | - return $first_date && $last_date ? $first_date . $conjunction . $last_date : ''; |
|
| 290 | + return $first_date && $last_date ? $first_date.$conjunction.$last_date : ''; |
|
| 291 | 291 | } |
| 292 | 292 | |
| 293 | 293 | |
@@ -315,7 +315,7 @@ discard block |
||
| 315 | 315 | */ |
| 316 | 316 | public function datetimes($query_params = []) |
| 317 | 317 | { |
| 318 | - if (! isset($query_params['order_by'])) { |
|
| 318 | + if ( ! isset($query_params['order_by'])) { |
|
| 319 | 319 | $query_params['order_by']['DTT_order'] = 'ASC'; |
| 320 | 320 | } |
| 321 | 321 | return $this->get_many_related('Datetime', $query_params); |
@@ -361,7 +361,7 @@ discard block |
||
| 361 | 361 | return $total; |
| 362 | 362 | } |
| 363 | 363 | |
| 364 | - if (! empty($dtt_id) && ! isset($tickets_sold['datetime'][ $dtt_id ])) { |
|
| 364 | + if ( ! empty($dtt_id) && ! isset($tickets_sold['datetime'][$dtt_id])) { |
|
| 365 | 365 | EE_Error::add_error( |
| 366 | 366 | esc_html__( |
| 367 | 367 | 'You\'ve requested the amount of tickets sold for a given ticket and datetime, however there are no records for the datetime id you included. Are you SURE that is a datetime related to this ticket?', |
@@ -373,7 +373,7 @@ discard block |
||
| 373 | 373 | ); |
| 374 | 374 | return $total; |
| 375 | 375 | } |
| 376 | - return empty($dtt_id) ? $tickets_sold['datetime'] : $tickets_sold['datetime'][ $dtt_id ]; |
|
| 376 | + return empty($dtt_id) ? $tickets_sold['datetime'] : $tickets_sold['datetime'][$dtt_id]; |
|
| 377 | 377 | } |
| 378 | 378 | |
| 379 | 379 | |
@@ -388,9 +388,9 @@ discard block |
||
| 388 | 388 | { |
| 389 | 389 | $datetimes = $this->get_many_related('Datetime'); |
| 390 | 390 | $tickets_sold = []; |
| 391 | - if (! empty($datetimes)) { |
|
| 391 | + if ( ! empty($datetimes)) { |
|
| 392 | 392 | foreach ($datetimes as $datetime) { |
| 393 | - $tickets_sold['datetime'][ $datetime->ID() ] = $datetime->get('DTT_sold'); |
|
| 393 | + $tickets_sold['datetime'][$datetime->ID()] = $datetime->get('DTT_sold'); |
|
| 394 | 394 | } |
| 395 | 395 | } |
| 396 | 396 | // Tickets sold |
@@ -994,7 +994,7 @@ discard block |
||
| 994 | 994 | 'TKT_qty', |
| 995 | 995 | $qty |
| 996 | 996 | ); |
| 997 | - if (! $success) { |
|
| 997 | + if ( ! $success) { |
|
| 998 | 998 | // The datetimes were successfully bumped, but not the |
| 999 | 999 | // ticket. So we need to manually rollback the datetimes. |
| 1000 | 1000 | $this->decreaseReservedForDatetimes($qty); |
@@ -1463,7 +1463,7 @@ discard block |
||
| 1463 | 1463 | foreach ($this->datetimes() as $datetime) { |
| 1464 | 1464 | $times[] = $datetime->start_date_and_time(); |
| 1465 | 1465 | } |
| 1466 | - return $this->name() . ' @ ' . implode(', ', $times) . ' for ' . $this->pretty_price(); |
|
| 1466 | + return $this->name().' @ '.implode(', ', $times).' for '.$this->pretty_price(); |
|
| 1467 | 1467 | } |
| 1468 | 1468 | |
| 1469 | 1469 | |
@@ -1571,7 +1571,7 @@ discard block |
||
| 1571 | 1571 | { |
| 1572 | 1572 | // get one datetime to use for getting the event |
| 1573 | 1573 | $datetime = $this->first_datetime(); |
| 1574 | - if (! $datetime instanceof EE_Datetime) { |
|
| 1574 | + if ( ! $datetime instanceof EE_Datetime) { |
|
| 1575 | 1575 | throw new UnexpectedEntityException( |
| 1576 | 1576 | $datetime, |
| 1577 | 1577 | 'EE_Datetime', |
@@ -1582,7 +1582,7 @@ discard block |
||
| 1582 | 1582 | ); |
| 1583 | 1583 | } |
| 1584 | 1584 | $event = $datetime->event(); |
| 1585 | - if (! $event instanceof EE_Event) { |
|
| 1585 | + if ( ! $event instanceof EE_Event) { |
|
| 1586 | 1586 | throw new UnexpectedEntityException( |
| 1587 | 1587 | $event, |
| 1588 | 1588 | 'EE_Event', |
@@ -13,568 +13,568 @@ |
||
| 13 | 13 | */ |
| 14 | 14 | class EE_CSV |
| 15 | 15 | { |
| 16 | - /** |
|
| 17 | - * string used for 1st cell in exports, which indicates that the following 2 rows will be metadata keys and values |
|
| 18 | - */ |
|
| 19 | - const metadata_header = 'Event Espresso Export Meta Data'; |
|
| 20 | - |
|
| 21 | - /** |
|
| 22 | - * @var EE_CSV |
|
| 23 | - */ |
|
| 24 | - private static $_instance = null; |
|
| 25 | - |
|
| 26 | - |
|
| 27 | - /** |
|
| 28 | - * @return void |
|
| 29 | - */ |
|
| 30 | - private function __construct() |
|
| 31 | - { |
|
| 32 | - } |
|
| 33 | - |
|
| 34 | - |
|
| 35 | - /** |
|
| 36 | - * singleton method used to instantiate class object |
|
| 37 | - * |
|
| 38 | - * @return EE_CSV |
|
| 39 | - */ |
|
| 40 | - public static function instance(): EE_CSV |
|
| 41 | - { |
|
| 42 | - // check if class object is instantiated |
|
| 43 | - if (self::$_instance === null or ! is_object(self::$_instance) or ! (self::$_instance instanceof EE_CSV)) { |
|
| 44 | - self::$_instance = new self(); |
|
| 45 | - } |
|
| 46 | - return self::$_instance; |
|
| 47 | - } |
|
| 48 | - |
|
| 49 | - |
|
| 50 | - /** |
|
| 51 | - * Opens a unicode or utf file (normal file_get_contents has difficulty reading a unicode file. @param string |
|
| 52 | - * $file_path |
|
| 53 | - * |
|
| 54 | - * @return string |
|
| 55 | - * @throws EE_Error |
|
| 56 | - * @see http://stackoverflow.com/questions/15092764/how-to-read-unicode-text-file-in-php |
|
| 57 | - * |
|
| 58 | - */ |
|
| 59 | - private function read_unicode_file(string $file_path): string |
|
| 60 | - { |
|
| 61 | - $fc = ""; |
|
| 62 | - $fh = fopen($file_path, "rb"); |
|
| 63 | - if (! $fh) { |
|
| 64 | - throw new EE_Error( |
|
| 65 | - sprintf(esc_html__("Cannot open file for read: %s<br>\n", 'event_espresso'), $file_path) |
|
| 66 | - ); |
|
| 67 | - } |
|
| 68 | - $file_length = filesize($file_path); |
|
| 69 | - $bc = fread($fh, $file_length); |
|
| 70 | - for ($i = 0; $i < $file_length; $i++) { |
|
| 71 | - $c = substr($bc, $i, 1); |
|
| 72 | - if ((ord($c) != 0) && (ord($c) != 13)) { |
|
| 73 | - $fc = $fc . $c; |
|
| 74 | - } |
|
| 75 | - } |
|
| 76 | - if ((ord(substr($fc, 0, 1)) == 255) && (ord(substr($fc, 1, 1)) == 254)) { |
|
| 77 | - $fc = substr($fc, 2); |
|
| 78 | - } |
|
| 79 | - return ($fc); |
|
| 80 | - } |
|
| 81 | - |
|
| 82 | - |
|
| 83 | - /** |
|
| 84 | - * Generic CSV-functionality to turn an entire CSV file into a single array that's |
|
| 85 | - * NOT in a specific format to EE. It's just a 2-level array, with top-level arrays |
|
| 86 | - * representing each row in the CSV file, and the second-level arrays being each column in that row |
|
| 87 | - * |
|
| 88 | - * @param string $path_to_file |
|
| 89 | - * @return array of arrays. Top-level array has rows, second-level array has each item |
|
| 90 | - * @throws EE_Error |
|
| 91 | - */ |
|
| 92 | - public function import_csv_to_multi_dimensional_array(string $path_to_file): array |
|
| 93 | - { |
|
| 94 | - // needed to deal with Mac line endings |
|
| 95 | - ini_set('auto_detect_line_endings', true); |
|
| 96 | - // because fgetcsv does not correctly deal with backslashed quotes such as \" |
|
| 97 | - // we'll read the file into a string |
|
| 98 | - $file_contents = $this->read_unicode_file($path_to_file); |
|
| 99 | - // replace backslashed quotes with CSV enclosures |
|
| 100 | - $file_contents = str_replace('\\"', '"""', $file_contents); |
|
| 101 | - // HEY YOU! PUT THAT FILE BACK!!! |
|
| 102 | - file_put_contents($path_to_file, $file_contents); |
|
| 103 | - |
|
| 104 | - if (($file_handle = fopen($path_to_file, "r")) !== false) { |
|
| 105 | - $csv_array = []; |
|
| 106 | - // loop through each row of the file |
|
| 107 | - while (($data = fgetcsv($file_handle, 0)) !== false) { |
|
| 108 | - $csv_array[] = $data; |
|
| 109 | - } |
|
| 110 | - # Close the File. |
|
| 111 | - fclose($file_handle); |
|
| 112 | - return $csv_array; |
|
| 113 | - } |
|
| 114 | - EE_Error::add_error( |
|
| 115 | - sprintf( |
|
| 116 | - esc_html__('An error occurred - the file: %s could not opened.', 'event_espresso'), |
|
| 117 | - $path_to_file |
|
| 118 | - ), |
|
| 119 | - __FILE__, |
|
| 120 | - __FUNCTION__, |
|
| 121 | - __LINE__ |
|
| 122 | - ); |
|
| 123 | - return []; |
|
| 124 | - } |
|
| 125 | - |
|
| 126 | - |
|
| 127 | - /** |
|
| 128 | - * import contents of csv file and store values in an array to be manipulated by other functions |
|
| 129 | - * |
|
| 130 | - * @param string $path_to_file - the csv file to be imported including the path to it's location. |
|
| 131 | - * If $model_name is provided, assumes that each row in the CSV represents a |
|
| 132 | - * model object for that model If $model_name ISN'T provided, assumes that |
|
| 133 | - * before model object data, there is a row where the first entry is simply |
|
| 134 | - * 'MODEL', and next entry is the model's name, (untranslated) like Event, and |
|
| 135 | - * then maybe a row of headers, and then the model data. Eg. |
|
| 136 | - * '<br>MODEL,Event,<br>EVT_ID,EVT_name,...<br>1,Monkey |
|
| 137 | - * Party,...<br>2,Llamarama,...<br>MODEL,Venue,<br>VNU_ID,VNU_name<br>1,The |
|
| 138 | - * Forest |
|
| 139 | - * @param string $model_name model name if we know what model we're importing |
|
| 140 | - * @param boolean $first_row_is_headers - whether the first row of data is headers or not - TRUE = headers, FALSE = |
|
| 141 | - * data |
|
| 142 | - * @return array|false - array on success - multi dimensional with headers as keys |
|
| 143 | - * (if headers exist) OR string on fail - |
|
| 144 | - * error message like the following array('Event'=>array( |
|
| 145 | - * array('EVT_ID'=>1,'EVT_name'=>'bob party',...), |
|
| 146 | - * array('EVT_ID'=>2,'EVT_name'=>'llamarama',...), |
|
| 147 | - * ... |
|
| 148 | - * ) |
|
| 149 | - * 'Venue'=>array( |
|
| 150 | - * array('VNU_ID'=>1,'VNU_name'=>'the shack',...), |
|
| 151 | - * array('VNU_ID'=>2,'VNU_name'=>'tree house',...), |
|
| 152 | - * ... |
|
| 153 | - * ) |
|
| 154 | - * ... |
|
| 155 | - * ) |
|
| 156 | - * @throws EE_Error |
|
| 157 | - */ |
|
| 158 | - public function import_csv_to_model_data_array( |
|
| 159 | - string $path_to_file, |
|
| 160 | - string $model_name = '', |
|
| 161 | - bool $first_row_is_headers = true |
|
| 162 | - ) { |
|
| 163 | - $multi_dimensional_array = $this->import_csv_to_multi_dimensional_array($path_to_file); |
|
| 164 | - if (empty($multi_dimensional_array)) { |
|
| 165 | - return false; |
|
| 166 | - } |
|
| 167 | - // gotta start somewhere |
|
| 168 | - $row = 1; |
|
| 169 | - // array to store csv data in |
|
| 170 | - $ee_formatted_data = []; |
|
| 171 | - // array to store headers (column names) |
|
| 172 | - $headers = []; |
|
| 173 | - foreach ($multi_dimensional_array as $data) { |
|
| 174 | - // if first cell is MODEL, then second cell is the MODEL name |
|
| 175 | - if ($data[0] == 'MODEL') { |
|
| 176 | - $model_name = $data[1]; |
|
| 177 | - // don't bother looking for model data in this row. The rest of this |
|
| 178 | - // row should be blank |
|
| 179 | - // AND pretend this is the first row again |
|
| 180 | - $row = 1; |
|
| 181 | - // reset headers |
|
| 182 | - $headers = []; |
|
| 183 | - continue; |
|
| 184 | - } |
|
| 185 | - if (strpos($data[0], EE_CSV::metadata_header) !== false) { |
|
| 186 | - $model_name = EE_CSV::metadata_header; |
|
| 187 | - // store like model data, we just won't try importing it etc. |
|
| 188 | - $row = 1; |
|
| 189 | - continue; |
|
| 190 | - } |
|
| 191 | - |
|
| 192 | - |
|
| 193 | - // how many columns are there? |
|
| 194 | - $columns = count($data); |
|
| 195 | - |
|
| 196 | - $model_entry = []; |
|
| 197 | - // loop through each column |
|
| 198 | - for ($i = 0; $i < $columns; $i++) { |
|
| 199 | - // replace csv_enclosures with backslashed quotes |
|
| 200 | - $data[ $i ] = str_replace('"""', '\\"', $data[ $i ]); |
|
| 201 | - // do we need to grab the column names? |
|
| 202 | - if ($row === 1) { |
|
| 203 | - if ($first_row_is_headers) { |
|
| 204 | - // store the column names to use for keys |
|
| 205 | - $column_name = $data[ $i ]; |
|
| 206 | - // check it's not blank... sometimes CSV editing programs adda bunch of empty columns onto the end... |
|
| 207 | - if (! $column_name) { |
|
| 208 | - continue; |
|
| 209 | - } |
|
| 210 | - $matches = []; |
|
| 211 | - if ($model_name == EE_CSV::metadata_header) { |
|
| 212 | - $headers[ $i ] = $column_name; |
|
| 213 | - } else { |
|
| 214 | - // now get the db table name from it (the part between square brackets) |
|
| 215 | - $success = preg_match('~(.*)\[(.*)\]~', $column_name, $matches); |
|
| 216 | - if (! $success) { |
|
| 217 | - EE_Error::add_error( |
|
| 218 | - sprintf( |
|
| 219 | - esc_html__( |
|
| 220 | - "The column titled %s is invalid for importing. It must be be in the format of 'Nice Name[model_field_name]' in row %s", |
|
| 221 | - "event_espresso" |
|
| 222 | - ), |
|
| 223 | - $column_name, |
|
| 224 | - implode(",", $data) |
|
| 225 | - ), |
|
| 226 | - __FILE__, |
|
| 227 | - __FUNCTION__, |
|
| 228 | - __LINE__ |
|
| 229 | - ); |
|
| 230 | - return false; |
|
| 231 | - } |
|
| 232 | - $headers[ $i ] = $matches[2]; |
|
| 233 | - } |
|
| 234 | - } else { |
|
| 235 | - // no column names means our final array will just use counters for keys |
|
| 236 | - $model_entry[ $headers[ $i ] ] = $data[ $i ]; |
|
| 237 | - $headers[ $i ] = $i; |
|
| 238 | - } |
|
| 239 | - // and we need to store csv data |
|
| 240 | - } else { |
|
| 241 | - // this column isn' ta header, store it if there is a header for it |
|
| 242 | - if (isset($headers[ $i ])) { |
|
| 243 | - $model_entry[ $headers[ $i ] ] = $data[ $i ]; |
|
| 244 | - } |
|
| 245 | - } |
|
| 246 | - } |
|
| 247 | - // save the row's data IF it's a non-header-row |
|
| 248 | - if (! $first_row_is_headers || $row > 1) { |
|
| 249 | - $ee_formatted_data[ $model_name ][] = $model_entry; |
|
| 250 | - } |
|
| 251 | - // advance to next row |
|
| 252 | - $row++; |
|
| 253 | - } |
|
| 254 | - |
|
| 255 | - // delete the uploaded file |
|
| 256 | - unlink($path_to_file); |
|
| 257 | - // echo '<pre style="height:auto;border:2px solid lightblue;">' . print_r( $ee_formatted_data, TRUE ) . '</pre><br /><span style="font-size:10px;font-weight:normal;">' . __FILE__ . '<br />line no: ' . __LINE__ . '</span>'; |
|
| 258 | - // die(); |
|
| 259 | - |
|
| 260 | - // it's good to give back |
|
| 261 | - return $ee_formatted_data; |
|
| 262 | - } |
|
| 263 | - |
|
| 264 | - |
|
| 265 | - /** |
|
| 266 | - * @throws EE_Error |
|
| 267 | - */ |
|
| 268 | - public function save_csv_to_db($csv_data_array, $model_name = false): bool |
|
| 269 | - { |
|
| 270 | - EE_Error::doing_it_wrong( |
|
| 271 | - 'save_csv_to_db', |
|
| 272 | - esc_html__( |
|
| 273 | - 'Function moved to EE_Import and renamed to save_csv_data_array_to_db', |
|
| 274 | - 'event_espresso' |
|
| 275 | - ), |
|
| 276 | - '4.6.7' |
|
| 277 | - ); |
|
| 278 | - return EE_Import::instance()->save_csv_data_array_to_db($csv_data_array, $model_name); |
|
| 279 | - } |
|
| 280 | - |
|
| 281 | - |
|
| 282 | - /** |
|
| 283 | - * Sends HTTP headers to indicate that the browser should download a file, |
|
| 284 | - * and starts writing the file to PHP's output. Returns the file handle so other functions can |
|
| 285 | - * also write to it |
|
| 286 | - * |
|
| 287 | - * @param string $filename the name of the file that the user will download |
|
| 288 | - * @return resource, like the results of fopen(), which can be used for fwrite, fputcsv2, etc. |
|
| 289 | - */ |
|
| 290 | - public function begin_sending_csv(string $filename) |
|
| 291 | - { |
|
| 292 | - // grab file extension |
|
| 293 | - $ext = substr(strrchr($filename, '.'), 1); |
|
| 294 | - if ($ext == '.csv' or $ext == '.xls') { |
|
| 295 | - str_replace($ext, '', $filename); |
|
| 296 | - } |
|
| 297 | - $filename .= '.csv'; |
|
| 298 | - |
|
| 299 | - // if somebody's been naughty and already started outputting stuff, trash it |
|
| 300 | - // and start writing our stuff. |
|
| 301 | - if (ob_get_length()) { |
|
| 302 | - @ob_flush(); |
|
| 303 | - @flush(); |
|
| 304 | - @ob_end_flush(); |
|
| 305 | - } |
|
| 306 | - @ob_start(); |
|
| 307 | - header("Pragma: public"); |
|
| 308 | - header("Expires: 0"); |
|
| 309 | - header("Pragma: no-cache"); |
|
| 310 | - header("Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0"); |
|
| 311 | - // header("Content-Type: application/force-download"); |
|
| 312 | - // header("Content-Type: application/octet-stream"); |
|
| 313 | - // header("Content-Type: application/download"); |
|
| 314 | - header('Content-disposition: attachment; filename=' . $filename); |
|
| 315 | - header("Content-Type: text/csv; charset=utf-8"); |
|
| 316 | - do_action('AHEE__EE_CSV__begin_sending_csv__headers'); |
|
| 317 | - echo apply_filters( |
|
| 318 | - 'FHEE__EE_CSV__begin_sending_csv__start_writing', |
|
| 319 | - "\xEF\xBB\xBF" |
|
| 320 | - ); |
|
| 321 | - // makes excel open it as UTF-8. UTF-8 BOM, see http://stackoverflow.com/a/4440143/2773835 |
|
| 322 | - return fopen('php://output', 'w'); |
|
| 323 | - } |
|
| 324 | - |
|
| 325 | - |
|
| 326 | - /** |
|
| 327 | - * Writes some meta data to the CSV as a bunch of columns. Initially we're only |
|
| 328 | - * mentioning the version and timezone |
|
| 329 | - * |
|
| 330 | - * @param resource $file_handle |
|
| 331 | - * @throws EE_Error |
|
| 332 | - * @throws EE_Error |
|
| 333 | - */ |
|
| 334 | - public function write_metadata_to_csv($file_handle) |
|
| 335 | - { |
|
| 336 | - $data_row = [EE_CSV::metadata_header];// do NOT translate because this exact string is used when importing |
|
| 337 | - $this->fputcsv2($file_handle, $data_row); |
|
| 338 | - $meta_data = [ |
|
| 339 | - 0 => [ |
|
| 340 | - 'version' => espresso_version(), |
|
| 341 | - 'timezone' => EEH_DTT_Helper::get_timezone(), |
|
| 342 | - 'time_of_export' => current_time('mysql'), |
|
| 343 | - 'site_url' => site_url(), |
|
| 344 | - ], |
|
| 345 | - ]; |
|
| 346 | - $this->write_data_array_to_csv($file_handle, $meta_data); |
|
| 347 | - } |
|
| 348 | - |
|
| 349 | - |
|
| 350 | - /** |
|
| 351 | - * Writes $data to the csv file open in $file_handle. uses the array indices of $data for column headers |
|
| 352 | - * |
|
| 353 | - * @param resource $file_handle |
|
| 354 | - * @param array|null $data 2D array, first numerically-indexed, |
|
| 355 | - * and next-level-down preferably indexed by string |
|
| 356 | - * @return boolean if we successfully wrote to the CSV or not. |
|
| 357 | - * If there's no $data, we consider that a success |
|
| 358 | - * (because we wrote everything there was...nothing) |
|
| 359 | - * @throws EE_Error |
|
| 360 | - */ |
|
| 361 | - public function write_data_array_to_csv($file_handle, ?array $data): bool |
|
| 362 | - { |
|
| 363 | - // determine if $data is actually a 2d array |
|
| 364 | - if ($data && is_array(EEH_Array::get_one_item_from_array($data))) { |
|
| 365 | - // make sure top level is numerically indexed, |
|
| 366 | - if (EEH_Array::is_associative_array($data)) { |
|
| 367 | - throw new EE_Error( |
|
| 368 | - sprintf( |
|
| 369 | - esc_html__( |
|
| 370 | - "top-level array must be numerically indexed. Does these look like numbers to you? %s", |
|
| 371 | - "event_espresso" |
|
| 372 | - ), |
|
| 373 | - implode(",", array_keys($data)) |
|
| 374 | - ) |
|
| 375 | - ); |
|
| 376 | - } |
|
| 377 | - $item_in_top_level_array = EEH_Array::get_one_item_from_array($data); |
|
| 378 | - // now, is the last item in the top-level array of $data an associative or numeric array? |
|
| 379 | - if (EEH_Array::is_associative_array($item_in_top_level_array)) { |
|
| 380 | - // its associative, so we want to output its keys as column headers |
|
| 381 | - $keys = array_keys($item_in_top_level_array); |
|
| 382 | - $this->fputcsv2($file_handle, $keys); |
|
| 383 | - } |
|
| 384 | - // start writing data |
|
| 385 | - foreach ($data as $data_row) { |
|
| 386 | - $this->fputcsv2($file_handle, $data_row); |
|
| 387 | - } |
|
| 388 | - return true; |
|
| 389 | - } |
|
| 390 | - // no data TO write... so we can assume that's a success |
|
| 391 | - return true; |
|
| 392 | - } |
|
| 393 | - |
|
| 394 | - |
|
| 395 | - /** |
|
| 396 | - * Should be called after begin_sending_csv(), and one or more write_data_array_to_csv()s. |
|
| 397 | - * Calls exit to prevent polluting the CSV file with other junk |
|
| 398 | - * |
|
| 399 | - * @param resource $fh file handle where we're writing the CSV to |
|
| 400 | - */ |
|
| 401 | - public function end_sending_csv($fh) |
|
| 402 | - { |
|
| 403 | - fclose($fh); |
|
| 404 | - exit(0); |
|
| 405 | - } |
|
| 406 | - |
|
| 407 | - |
|
| 408 | - /** |
|
| 409 | - * Given an open file, writes all the model data to it in the format the importer expects. |
|
| 410 | - * Usually preceded by begin_sending_csv($filename), and followed by end_sending_csv($file_handle). |
|
| 411 | - * |
|
| 412 | - * @param resource $file_handle |
|
| 413 | - * @param array $model_data_array is assumed to be a 3d array: 1st layer has keys of model names (eg 'Event'), |
|
| 414 | - * next layer is numerically indexed to represent each model object (eg, each |
|
| 415 | - * individual event), and the last layer has all the attributes of that model |
|
| 416 | - * object (eg, the event's id, name, etc) |
|
| 417 | - * @return void |
|
| 418 | - * @throws EE_Error |
|
| 419 | - * @throws ReflectionException |
|
| 420 | - */ |
|
| 421 | - public function write_model_data_to_csv($file_handle, array $model_data_array) |
|
| 422 | - { |
|
| 423 | - $this->write_metadata_to_csv($file_handle); |
|
| 424 | - foreach ($model_data_array as $model_name => $model_instance_arrays) { |
|
| 425 | - // first: output a special row stating the model |
|
| 426 | - $this->fputcsv2($file_handle, ['MODEL', $model_name]); |
|
| 427 | - // if we have items to put in the CSV, do it normally |
|
| 428 | - |
|
| 429 | - if (! empty($model_instance_arrays)) { |
|
| 430 | - $this->write_data_array_to_csv($file_handle, $model_instance_arrays); |
|
| 431 | - } else { |
|
| 432 | - // echo "no data to write... so just write the headers"; |
|
| 433 | - // so there's actually NO model objects for that model. |
|
| 434 | - // probably still want to show the columns |
|
| 435 | - $model = EE_Registry::instance()->load_model($model_name); |
|
| 436 | - $column_names = []; |
|
| 437 | - foreach ($model->field_settings() as $field) { |
|
| 438 | - $column_names[ $field->get_nicename() . "[" . $field->get_name() . "]" ] = null; |
|
| 439 | - } |
|
| 440 | - $this->write_data_array_to_csv($file_handle, [$column_names]); |
|
| 441 | - } |
|
| 442 | - } |
|
| 443 | - } |
|
| 444 | - |
|
| 445 | - |
|
| 446 | - /** |
|
| 447 | - * Writes the CSV file to the output buffer, with rows corresponding to $model_data_array, |
|
| 448 | - * and dies (in order to avoid other plugins from messing up the csv output) |
|
| 449 | - * |
|
| 450 | - * @param string $filename the filename you want to give the file |
|
| 451 | - * @param array $model_data_array 3d array, as described in EE_CSV::write_model_data_to_csv() |
|
| 452 | - * @return void |
|
| 453 | - * @throws EE_Error |
|
| 454 | - * @throws ReflectionException |
|
| 455 | - */ |
|
| 456 | - public function export_multiple_model_data_to_csv(string $filename, array $model_data_array) |
|
| 457 | - { |
|
| 458 | - $file_handle = $this->begin_sending_csv($filename); |
|
| 459 | - $this->write_model_data_to_csv($file_handle, $model_data_array); |
|
| 460 | - $this->end_sending_csv($file_handle); |
|
| 461 | - } |
|
| 462 | - |
|
| 463 | - |
|
| 464 | - /** |
|
| 465 | - * export contents of an array to csv file |
|
| 466 | - * |
|
| 467 | - * @param array|null $data - the array of data to be converted to csv and exported |
|
| 468 | - * @param string $filename - name for newly created csv file |
|
| 469 | - * @return void |
|
| 470 | - */ |
|
| 471 | - public function export_array_to_csv(?array $data, string $filename = '') |
|
| 472 | - { |
|
| 473 | - // no data file or filename?? get outta here |
|
| 474 | - if (empty($data) || ! $filename) { |
|
| 475 | - return; |
|
| 476 | - } |
|
| 477 | - $fh = $this->begin_sending_csv($filename); |
|
| 478 | - $this->end_sending_csv($fh); |
|
| 479 | - } |
|
| 480 | - |
|
| 481 | - |
|
| 482 | - /** |
|
| 483 | - * @Determine the maximum upload file size based on php.ini settings |
|
| 484 | - * @access public |
|
| 485 | - * @param int $percent_of_max - desired percentage of the max upload_mb |
|
| 486 | - * @return int KB |
|
| 487 | - */ |
|
| 488 | - public function get_max_upload_size(int $percent_of_max = 0) |
|
| 489 | - { |
|
| 490 | - $max_upload = (int) (ini_get('upload_max_filesize')); |
|
| 491 | - $max_post = (int) (ini_get('post_max_size')); |
|
| 492 | - $memory_limit = (int) (ini_get('memory_limit')); |
|
| 493 | - |
|
| 494 | - // determine the smallest of the three values from above |
|
| 495 | - $upload_mb = min($max_upload, $max_post, $memory_limit); |
|
| 496 | - |
|
| 497 | - // convert MB to KB |
|
| 498 | - $upload_mb = $upload_mb * 1024; |
|
| 499 | - |
|
| 500 | - // don't want the full monty? then reduce the max upload size |
|
| 501 | - if ($percent_of_max) { |
|
| 502 | - // is percent_of_max like this -> 50 or like this -> 0.50 ? |
|
| 503 | - if ($percent_of_max > 1) { |
|
| 504 | - // changes 50 to 0.50 |
|
| 505 | - $percent_of_max = $percent_of_max / 100; |
|
| 506 | - } |
|
| 507 | - // make upload_mb a percentage of the max upload_mb |
|
| 508 | - $upload_mb = $upload_mb * $percent_of_max; |
|
| 509 | - } |
|
| 510 | - |
|
| 511 | - return $upload_mb; |
|
| 512 | - } |
|
| 513 | - |
|
| 514 | - |
|
| 515 | - /** |
|
| 516 | - * drop in replacement for PHP's fputcsv function - but this one works!!! |
|
| 517 | - * |
|
| 518 | - * @param resource $fh - file handle - what we are writing to |
|
| 519 | - * @param array $row - individual row of csv data |
|
| 520 | - * @param string $delimiter - csv delimiter |
|
| 521 | - * @param string $enclosure - csv enclosure |
|
| 522 | - * @param bool $mysql_null - allows php NULL to be overridden with MySQL's insertable NULL value |
|
| 523 | - * @return void |
|
| 524 | - */ |
|
| 525 | - private function fputcsv2( |
|
| 526 | - $fh, |
|
| 527 | - array $row, |
|
| 528 | - string $delimiter = ',', |
|
| 529 | - string $enclosure = '"', |
|
| 530 | - bool $mysql_null = false |
|
| 531 | - ) { |
|
| 532 | - // Allow user to filter the csv delimiter and enclosure for other countries csv standards |
|
| 533 | - $delimiter = apply_filters('FHEE__EE_CSV__fputcsv2__delimiter', $delimiter); |
|
| 534 | - $enclosure = apply_filters('FHEE__EE_CSV__fputcsv2__enclosure', $enclosure); |
|
| 535 | - |
|
| 536 | - $delimiter_esc = preg_quote($delimiter, '/'); |
|
| 537 | - $enclosure_esc = preg_quote($enclosure, '/'); |
|
| 538 | - |
|
| 539 | - $output = []; |
|
| 540 | - foreach ($row as $field_value) { |
|
| 541 | - if (is_object($field_value) || is_array($field_value)) { |
|
| 542 | - $field_value = serialize($field_value); |
|
| 543 | - } |
|
| 544 | - if ($field_value === null && $mysql_null) { |
|
| 545 | - $output[] = 'NULL'; |
|
| 546 | - continue; |
|
| 547 | - } |
|
| 548 | - |
|
| 549 | - $output[] = preg_match("/(?:${delimiter_esc}|${enclosure_esc}|\s)/", $field_value) |
|
| 550 | - ? ($enclosure . str_replace($enclosure, $enclosure . $enclosure, $field_value) . $enclosure) |
|
| 551 | - : $field_value; |
|
| 552 | - } |
|
| 553 | - |
|
| 554 | - fwrite($fh, join($delimiter, $output) . PHP_EOL); |
|
| 555 | - } |
|
| 556 | - |
|
| 557 | - |
|
| 558 | - /** |
|
| 559 | - * Gets the date format to use in teh csv. filterable |
|
| 560 | - * |
|
| 561 | - * @param string $current_format |
|
| 562 | - * @return string |
|
| 563 | - */ |
|
| 564 | - public function get_date_format_for_csv(string $current_format = ''): string |
|
| 565 | - { |
|
| 566 | - return apply_filters('FHEE__EE_CSV__get_date_format_for_csv__format', 'Y-m-d', $current_format); |
|
| 567 | - } |
|
| 568 | - |
|
| 569 | - |
|
| 570 | - /** |
|
| 571 | - * Gets the time format we want to use in CSV reports. Filterable |
|
| 572 | - * |
|
| 573 | - * @param string $current_format |
|
| 574 | - * @return string |
|
| 575 | - */ |
|
| 576 | - public function get_time_format_for_csv(string $current_format = ''): string |
|
| 577 | - { |
|
| 578 | - return apply_filters('FHEE__EE_CSV__get_time_format_for_csv__format', 'H:i:s', $current_format); |
|
| 579 | - } |
|
| 16 | + /** |
|
| 17 | + * string used for 1st cell in exports, which indicates that the following 2 rows will be metadata keys and values |
|
| 18 | + */ |
|
| 19 | + const metadata_header = 'Event Espresso Export Meta Data'; |
|
| 20 | + |
|
| 21 | + /** |
|
| 22 | + * @var EE_CSV |
|
| 23 | + */ |
|
| 24 | + private static $_instance = null; |
|
| 25 | + |
|
| 26 | + |
|
| 27 | + /** |
|
| 28 | + * @return void |
|
| 29 | + */ |
|
| 30 | + private function __construct() |
|
| 31 | + { |
|
| 32 | + } |
|
| 33 | + |
|
| 34 | + |
|
| 35 | + /** |
|
| 36 | + * singleton method used to instantiate class object |
|
| 37 | + * |
|
| 38 | + * @return EE_CSV |
|
| 39 | + */ |
|
| 40 | + public static function instance(): EE_CSV |
|
| 41 | + { |
|
| 42 | + // check if class object is instantiated |
|
| 43 | + if (self::$_instance === null or ! is_object(self::$_instance) or ! (self::$_instance instanceof EE_CSV)) { |
|
| 44 | + self::$_instance = new self(); |
|
| 45 | + } |
|
| 46 | + return self::$_instance; |
|
| 47 | + } |
|
| 48 | + |
|
| 49 | + |
|
| 50 | + /** |
|
| 51 | + * Opens a unicode or utf file (normal file_get_contents has difficulty reading a unicode file. @param string |
|
| 52 | + * $file_path |
|
| 53 | + * |
|
| 54 | + * @return string |
|
| 55 | + * @throws EE_Error |
|
| 56 | + * @see http://stackoverflow.com/questions/15092764/how-to-read-unicode-text-file-in-php |
|
| 57 | + * |
|
| 58 | + */ |
|
| 59 | + private function read_unicode_file(string $file_path): string |
|
| 60 | + { |
|
| 61 | + $fc = ""; |
|
| 62 | + $fh = fopen($file_path, "rb"); |
|
| 63 | + if (! $fh) { |
|
| 64 | + throw new EE_Error( |
|
| 65 | + sprintf(esc_html__("Cannot open file for read: %s<br>\n", 'event_espresso'), $file_path) |
|
| 66 | + ); |
|
| 67 | + } |
|
| 68 | + $file_length = filesize($file_path); |
|
| 69 | + $bc = fread($fh, $file_length); |
|
| 70 | + for ($i = 0; $i < $file_length; $i++) { |
|
| 71 | + $c = substr($bc, $i, 1); |
|
| 72 | + if ((ord($c) != 0) && (ord($c) != 13)) { |
|
| 73 | + $fc = $fc . $c; |
|
| 74 | + } |
|
| 75 | + } |
|
| 76 | + if ((ord(substr($fc, 0, 1)) == 255) && (ord(substr($fc, 1, 1)) == 254)) { |
|
| 77 | + $fc = substr($fc, 2); |
|
| 78 | + } |
|
| 79 | + return ($fc); |
|
| 80 | + } |
|
| 81 | + |
|
| 82 | + |
|
| 83 | + /** |
|
| 84 | + * Generic CSV-functionality to turn an entire CSV file into a single array that's |
|
| 85 | + * NOT in a specific format to EE. It's just a 2-level array, with top-level arrays |
|
| 86 | + * representing each row in the CSV file, and the second-level arrays being each column in that row |
|
| 87 | + * |
|
| 88 | + * @param string $path_to_file |
|
| 89 | + * @return array of arrays. Top-level array has rows, second-level array has each item |
|
| 90 | + * @throws EE_Error |
|
| 91 | + */ |
|
| 92 | + public function import_csv_to_multi_dimensional_array(string $path_to_file): array |
|
| 93 | + { |
|
| 94 | + // needed to deal with Mac line endings |
|
| 95 | + ini_set('auto_detect_line_endings', true); |
|
| 96 | + // because fgetcsv does not correctly deal with backslashed quotes such as \" |
|
| 97 | + // we'll read the file into a string |
|
| 98 | + $file_contents = $this->read_unicode_file($path_to_file); |
|
| 99 | + // replace backslashed quotes with CSV enclosures |
|
| 100 | + $file_contents = str_replace('\\"', '"""', $file_contents); |
|
| 101 | + // HEY YOU! PUT THAT FILE BACK!!! |
|
| 102 | + file_put_contents($path_to_file, $file_contents); |
|
| 103 | + |
|
| 104 | + if (($file_handle = fopen($path_to_file, "r")) !== false) { |
|
| 105 | + $csv_array = []; |
|
| 106 | + // loop through each row of the file |
|
| 107 | + while (($data = fgetcsv($file_handle, 0)) !== false) { |
|
| 108 | + $csv_array[] = $data; |
|
| 109 | + } |
|
| 110 | + # Close the File. |
|
| 111 | + fclose($file_handle); |
|
| 112 | + return $csv_array; |
|
| 113 | + } |
|
| 114 | + EE_Error::add_error( |
|
| 115 | + sprintf( |
|
| 116 | + esc_html__('An error occurred - the file: %s could not opened.', 'event_espresso'), |
|
| 117 | + $path_to_file |
|
| 118 | + ), |
|
| 119 | + __FILE__, |
|
| 120 | + __FUNCTION__, |
|
| 121 | + __LINE__ |
|
| 122 | + ); |
|
| 123 | + return []; |
|
| 124 | + } |
|
| 125 | + |
|
| 126 | + |
|
| 127 | + /** |
|
| 128 | + * import contents of csv file and store values in an array to be manipulated by other functions |
|
| 129 | + * |
|
| 130 | + * @param string $path_to_file - the csv file to be imported including the path to it's location. |
|
| 131 | + * If $model_name is provided, assumes that each row in the CSV represents a |
|
| 132 | + * model object for that model If $model_name ISN'T provided, assumes that |
|
| 133 | + * before model object data, there is a row where the first entry is simply |
|
| 134 | + * 'MODEL', and next entry is the model's name, (untranslated) like Event, and |
|
| 135 | + * then maybe a row of headers, and then the model data. Eg. |
|
| 136 | + * '<br>MODEL,Event,<br>EVT_ID,EVT_name,...<br>1,Monkey |
|
| 137 | + * Party,...<br>2,Llamarama,...<br>MODEL,Venue,<br>VNU_ID,VNU_name<br>1,The |
|
| 138 | + * Forest |
|
| 139 | + * @param string $model_name model name if we know what model we're importing |
|
| 140 | + * @param boolean $first_row_is_headers - whether the first row of data is headers or not - TRUE = headers, FALSE = |
|
| 141 | + * data |
|
| 142 | + * @return array|false - array on success - multi dimensional with headers as keys |
|
| 143 | + * (if headers exist) OR string on fail - |
|
| 144 | + * error message like the following array('Event'=>array( |
|
| 145 | + * array('EVT_ID'=>1,'EVT_name'=>'bob party',...), |
|
| 146 | + * array('EVT_ID'=>2,'EVT_name'=>'llamarama',...), |
|
| 147 | + * ... |
|
| 148 | + * ) |
|
| 149 | + * 'Venue'=>array( |
|
| 150 | + * array('VNU_ID'=>1,'VNU_name'=>'the shack',...), |
|
| 151 | + * array('VNU_ID'=>2,'VNU_name'=>'tree house',...), |
|
| 152 | + * ... |
|
| 153 | + * ) |
|
| 154 | + * ... |
|
| 155 | + * ) |
|
| 156 | + * @throws EE_Error |
|
| 157 | + */ |
|
| 158 | + public function import_csv_to_model_data_array( |
|
| 159 | + string $path_to_file, |
|
| 160 | + string $model_name = '', |
|
| 161 | + bool $first_row_is_headers = true |
|
| 162 | + ) { |
|
| 163 | + $multi_dimensional_array = $this->import_csv_to_multi_dimensional_array($path_to_file); |
|
| 164 | + if (empty($multi_dimensional_array)) { |
|
| 165 | + return false; |
|
| 166 | + } |
|
| 167 | + // gotta start somewhere |
|
| 168 | + $row = 1; |
|
| 169 | + // array to store csv data in |
|
| 170 | + $ee_formatted_data = []; |
|
| 171 | + // array to store headers (column names) |
|
| 172 | + $headers = []; |
|
| 173 | + foreach ($multi_dimensional_array as $data) { |
|
| 174 | + // if first cell is MODEL, then second cell is the MODEL name |
|
| 175 | + if ($data[0] == 'MODEL') { |
|
| 176 | + $model_name = $data[1]; |
|
| 177 | + // don't bother looking for model data in this row. The rest of this |
|
| 178 | + // row should be blank |
|
| 179 | + // AND pretend this is the first row again |
|
| 180 | + $row = 1; |
|
| 181 | + // reset headers |
|
| 182 | + $headers = []; |
|
| 183 | + continue; |
|
| 184 | + } |
|
| 185 | + if (strpos($data[0], EE_CSV::metadata_header) !== false) { |
|
| 186 | + $model_name = EE_CSV::metadata_header; |
|
| 187 | + // store like model data, we just won't try importing it etc. |
|
| 188 | + $row = 1; |
|
| 189 | + continue; |
|
| 190 | + } |
|
| 191 | + |
|
| 192 | + |
|
| 193 | + // how many columns are there? |
|
| 194 | + $columns = count($data); |
|
| 195 | + |
|
| 196 | + $model_entry = []; |
|
| 197 | + // loop through each column |
|
| 198 | + for ($i = 0; $i < $columns; $i++) { |
|
| 199 | + // replace csv_enclosures with backslashed quotes |
|
| 200 | + $data[ $i ] = str_replace('"""', '\\"', $data[ $i ]); |
|
| 201 | + // do we need to grab the column names? |
|
| 202 | + if ($row === 1) { |
|
| 203 | + if ($first_row_is_headers) { |
|
| 204 | + // store the column names to use for keys |
|
| 205 | + $column_name = $data[ $i ]; |
|
| 206 | + // check it's not blank... sometimes CSV editing programs adda bunch of empty columns onto the end... |
|
| 207 | + if (! $column_name) { |
|
| 208 | + continue; |
|
| 209 | + } |
|
| 210 | + $matches = []; |
|
| 211 | + if ($model_name == EE_CSV::metadata_header) { |
|
| 212 | + $headers[ $i ] = $column_name; |
|
| 213 | + } else { |
|
| 214 | + // now get the db table name from it (the part between square brackets) |
|
| 215 | + $success = preg_match('~(.*)\[(.*)\]~', $column_name, $matches); |
|
| 216 | + if (! $success) { |
|
| 217 | + EE_Error::add_error( |
|
| 218 | + sprintf( |
|
| 219 | + esc_html__( |
|
| 220 | + "The column titled %s is invalid for importing. It must be be in the format of 'Nice Name[model_field_name]' in row %s", |
|
| 221 | + "event_espresso" |
|
| 222 | + ), |
|
| 223 | + $column_name, |
|
| 224 | + implode(",", $data) |
|
| 225 | + ), |
|
| 226 | + __FILE__, |
|
| 227 | + __FUNCTION__, |
|
| 228 | + __LINE__ |
|
| 229 | + ); |
|
| 230 | + return false; |
|
| 231 | + } |
|
| 232 | + $headers[ $i ] = $matches[2]; |
|
| 233 | + } |
|
| 234 | + } else { |
|
| 235 | + // no column names means our final array will just use counters for keys |
|
| 236 | + $model_entry[ $headers[ $i ] ] = $data[ $i ]; |
|
| 237 | + $headers[ $i ] = $i; |
|
| 238 | + } |
|
| 239 | + // and we need to store csv data |
|
| 240 | + } else { |
|
| 241 | + // this column isn' ta header, store it if there is a header for it |
|
| 242 | + if (isset($headers[ $i ])) { |
|
| 243 | + $model_entry[ $headers[ $i ] ] = $data[ $i ]; |
|
| 244 | + } |
|
| 245 | + } |
|
| 246 | + } |
|
| 247 | + // save the row's data IF it's a non-header-row |
|
| 248 | + if (! $first_row_is_headers || $row > 1) { |
|
| 249 | + $ee_formatted_data[ $model_name ][] = $model_entry; |
|
| 250 | + } |
|
| 251 | + // advance to next row |
|
| 252 | + $row++; |
|
| 253 | + } |
|
| 254 | + |
|
| 255 | + // delete the uploaded file |
|
| 256 | + unlink($path_to_file); |
|
| 257 | + // echo '<pre style="height:auto;border:2px solid lightblue;">' . print_r( $ee_formatted_data, TRUE ) . '</pre><br /><span style="font-size:10px;font-weight:normal;">' . __FILE__ . '<br />line no: ' . __LINE__ . '</span>'; |
|
| 258 | + // die(); |
|
| 259 | + |
|
| 260 | + // it's good to give back |
|
| 261 | + return $ee_formatted_data; |
|
| 262 | + } |
|
| 263 | + |
|
| 264 | + |
|
| 265 | + /** |
|
| 266 | + * @throws EE_Error |
|
| 267 | + */ |
|
| 268 | + public function save_csv_to_db($csv_data_array, $model_name = false): bool |
|
| 269 | + { |
|
| 270 | + EE_Error::doing_it_wrong( |
|
| 271 | + 'save_csv_to_db', |
|
| 272 | + esc_html__( |
|
| 273 | + 'Function moved to EE_Import and renamed to save_csv_data_array_to_db', |
|
| 274 | + 'event_espresso' |
|
| 275 | + ), |
|
| 276 | + '4.6.7' |
|
| 277 | + ); |
|
| 278 | + return EE_Import::instance()->save_csv_data_array_to_db($csv_data_array, $model_name); |
|
| 279 | + } |
|
| 280 | + |
|
| 281 | + |
|
| 282 | + /** |
|
| 283 | + * Sends HTTP headers to indicate that the browser should download a file, |
|
| 284 | + * and starts writing the file to PHP's output. Returns the file handle so other functions can |
|
| 285 | + * also write to it |
|
| 286 | + * |
|
| 287 | + * @param string $filename the name of the file that the user will download |
|
| 288 | + * @return resource, like the results of fopen(), which can be used for fwrite, fputcsv2, etc. |
|
| 289 | + */ |
|
| 290 | + public function begin_sending_csv(string $filename) |
|
| 291 | + { |
|
| 292 | + // grab file extension |
|
| 293 | + $ext = substr(strrchr($filename, '.'), 1); |
|
| 294 | + if ($ext == '.csv' or $ext == '.xls') { |
|
| 295 | + str_replace($ext, '', $filename); |
|
| 296 | + } |
|
| 297 | + $filename .= '.csv'; |
|
| 298 | + |
|
| 299 | + // if somebody's been naughty and already started outputting stuff, trash it |
|
| 300 | + // and start writing our stuff. |
|
| 301 | + if (ob_get_length()) { |
|
| 302 | + @ob_flush(); |
|
| 303 | + @flush(); |
|
| 304 | + @ob_end_flush(); |
|
| 305 | + } |
|
| 306 | + @ob_start(); |
|
| 307 | + header("Pragma: public"); |
|
| 308 | + header("Expires: 0"); |
|
| 309 | + header("Pragma: no-cache"); |
|
| 310 | + header("Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0"); |
|
| 311 | + // header("Content-Type: application/force-download"); |
|
| 312 | + // header("Content-Type: application/octet-stream"); |
|
| 313 | + // header("Content-Type: application/download"); |
|
| 314 | + header('Content-disposition: attachment; filename=' . $filename); |
|
| 315 | + header("Content-Type: text/csv; charset=utf-8"); |
|
| 316 | + do_action('AHEE__EE_CSV__begin_sending_csv__headers'); |
|
| 317 | + echo apply_filters( |
|
| 318 | + 'FHEE__EE_CSV__begin_sending_csv__start_writing', |
|
| 319 | + "\xEF\xBB\xBF" |
|
| 320 | + ); |
|
| 321 | + // makes excel open it as UTF-8. UTF-8 BOM, see http://stackoverflow.com/a/4440143/2773835 |
|
| 322 | + return fopen('php://output', 'w'); |
|
| 323 | + } |
|
| 324 | + |
|
| 325 | + |
|
| 326 | + /** |
|
| 327 | + * Writes some meta data to the CSV as a bunch of columns. Initially we're only |
|
| 328 | + * mentioning the version and timezone |
|
| 329 | + * |
|
| 330 | + * @param resource $file_handle |
|
| 331 | + * @throws EE_Error |
|
| 332 | + * @throws EE_Error |
|
| 333 | + */ |
|
| 334 | + public function write_metadata_to_csv($file_handle) |
|
| 335 | + { |
|
| 336 | + $data_row = [EE_CSV::metadata_header];// do NOT translate because this exact string is used when importing |
|
| 337 | + $this->fputcsv2($file_handle, $data_row); |
|
| 338 | + $meta_data = [ |
|
| 339 | + 0 => [ |
|
| 340 | + 'version' => espresso_version(), |
|
| 341 | + 'timezone' => EEH_DTT_Helper::get_timezone(), |
|
| 342 | + 'time_of_export' => current_time('mysql'), |
|
| 343 | + 'site_url' => site_url(), |
|
| 344 | + ], |
|
| 345 | + ]; |
|
| 346 | + $this->write_data_array_to_csv($file_handle, $meta_data); |
|
| 347 | + } |
|
| 348 | + |
|
| 349 | + |
|
| 350 | + /** |
|
| 351 | + * Writes $data to the csv file open in $file_handle. uses the array indices of $data for column headers |
|
| 352 | + * |
|
| 353 | + * @param resource $file_handle |
|
| 354 | + * @param array|null $data 2D array, first numerically-indexed, |
|
| 355 | + * and next-level-down preferably indexed by string |
|
| 356 | + * @return boolean if we successfully wrote to the CSV or not. |
|
| 357 | + * If there's no $data, we consider that a success |
|
| 358 | + * (because we wrote everything there was...nothing) |
|
| 359 | + * @throws EE_Error |
|
| 360 | + */ |
|
| 361 | + public function write_data_array_to_csv($file_handle, ?array $data): bool |
|
| 362 | + { |
|
| 363 | + // determine if $data is actually a 2d array |
|
| 364 | + if ($data && is_array(EEH_Array::get_one_item_from_array($data))) { |
|
| 365 | + // make sure top level is numerically indexed, |
|
| 366 | + if (EEH_Array::is_associative_array($data)) { |
|
| 367 | + throw new EE_Error( |
|
| 368 | + sprintf( |
|
| 369 | + esc_html__( |
|
| 370 | + "top-level array must be numerically indexed. Does these look like numbers to you? %s", |
|
| 371 | + "event_espresso" |
|
| 372 | + ), |
|
| 373 | + implode(",", array_keys($data)) |
|
| 374 | + ) |
|
| 375 | + ); |
|
| 376 | + } |
|
| 377 | + $item_in_top_level_array = EEH_Array::get_one_item_from_array($data); |
|
| 378 | + // now, is the last item in the top-level array of $data an associative or numeric array? |
|
| 379 | + if (EEH_Array::is_associative_array($item_in_top_level_array)) { |
|
| 380 | + // its associative, so we want to output its keys as column headers |
|
| 381 | + $keys = array_keys($item_in_top_level_array); |
|
| 382 | + $this->fputcsv2($file_handle, $keys); |
|
| 383 | + } |
|
| 384 | + // start writing data |
|
| 385 | + foreach ($data as $data_row) { |
|
| 386 | + $this->fputcsv2($file_handle, $data_row); |
|
| 387 | + } |
|
| 388 | + return true; |
|
| 389 | + } |
|
| 390 | + // no data TO write... so we can assume that's a success |
|
| 391 | + return true; |
|
| 392 | + } |
|
| 393 | + |
|
| 394 | + |
|
| 395 | + /** |
|
| 396 | + * Should be called after begin_sending_csv(), and one or more write_data_array_to_csv()s. |
|
| 397 | + * Calls exit to prevent polluting the CSV file with other junk |
|
| 398 | + * |
|
| 399 | + * @param resource $fh file handle where we're writing the CSV to |
|
| 400 | + */ |
|
| 401 | + public function end_sending_csv($fh) |
|
| 402 | + { |
|
| 403 | + fclose($fh); |
|
| 404 | + exit(0); |
|
| 405 | + } |
|
| 406 | + |
|
| 407 | + |
|
| 408 | + /** |
|
| 409 | + * Given an open file, writes all the model data to it in the format the importer expects. |
|
| 410 | + * Usually preceded by begin_sending_csv($filename), and followed by end_sending_csv($file_handle). |
|
| 411 | + * |
|
| 412 | + * @param resource $file_handle |
|
| 413 | + * @param array $model_data_array is assumed to be a 3d array: 1st layer has keys of model names (eg 'Event'), |
|
| 414 | + * next layer is numerically indexed to represent each model object (eg, each |
|
| 415 | + * individual event), and the last layer has all the attributes of that model |
|
| 416 | + * object (eg, the event's id, name, etc) |
|
| 417 | + * @return void |
|
| 418 | + * @throws EE_Error |
|
| 419 | + * @throws ReflectionException |
|
| 420 | + */ |
|
| 421 | + public function write_model_data_to_csv($file_handle, array $model_data_array) |
|
| 422 | + { |
|
| 423 | + $this->write_metadata_to_csv($file_handle); |
|
| 424 | + foreach ($model_data_array as $model_name => $model_instance_arrays) { |
|
| 425 | + // first: output a special row stating the model |
|
| 426 | + $this->fputcsv2($file_handle, ['MODEL', $model_name]); |
|
| 427 | + // if we have items to put in the CSV, do it normally |
|
| 428 | + |
|
| 429 | + if (! empty($model_instance_arrays)) { |
|
| 430 | + $this->write_data_array_to_csv($file_handle, $model_instance_arrays); |
|
| 431 | + } else { |
|
| 432 | + // echo "no data to write... so just write the headers"; |
|
| 433 | + // so there's actually NO model objects for that model. |
|
| 434 | + // probably still want to show the columns |
|
| 435 | + $model = EE_Registry::instance()->load_model($model_name); |
|
| 436 | + $column_names = []; |
|
| 437 | + foreach ($model->field_settings() as $field) { |
|
| 438 | + $column_names[ $field->get_nicename() . "[" . $field->get_name() . "]" ] = null; |
|
| 439 | + } |
|
| 440 | + $this->write_data_array_to_csv($file_handle, [$column_names]); |
|
| 441 | + } |
|
| 442 | + } |
|
| 443 | + } |
|
| 444 | + |
|
| 445 | + |
|
| 446 | + /** |
|
| 447 | + * Writes the CSV file to the output buffer, with rows corresponding to $model_data_array, |
|
| 448 | + * and dies (in order to avoid other plugins from messing up the csv output) |
|
| 449 | + * |
|
| 450 | + * @param string $filename the filename you want to give the file |
|
| 451 | + * @param array $model_data_array 3d array, as described in EE_CSV::write_model_data_to_csv() |
|
| 452 | + * @return void |
|
| 453 | + * @throws EE_Error |
|
| 454 | + * @throws ReflectionException |
|
| 455 | + */ |
|
| 456 | + public function export_multiple_model_data_to_csv(string $filename, array $model_data_array) |
|
| 457 | + { |
|
| 458 | + $file_handle = $this->begin_sending_csv($filename); |
|
| 459 | + $this->write_model_data_to_csv($file_handle, $model_data_array); |
|
| 460 | + $this->end_sending_csv($file_handle); |
|
| 461 | + } |
|
| 462 | + |
|
| 463 | + |
|
| 464 | + /** |
|
| 465 | + * export contents of an array to csv file |
|
| 466 | + * |
|
| 467 | + * @param array|null $data - the array of data to be converted to csv and exported |
|
| 468 | + * @param string $filename - name for newly created csv file |
|
| 469 | + * @return void |
|
| 470 | + */ |
|
| 471 | + public function export_array_to_csv(?array $data, string $filename = '') |
|
| 472 | + { |
|
| 473 | + // no data file or filename?? get outta here |
|
| 474 | + if (empty($data) || ! $filename) { |
|
| 475 | + return; |
|
| 476 | + } |
|
| 477 | + $fh = $this->begin_sending_csv($filename); |
|
| 478 | + $this->end_sending_csv($fh); |
|
| 479 | + } |
|
| 480 | + |
|
| 481 | + |
|
| 482 | + /** |
|
| 483 | + * @Determine the maximum upload file size based on php.ini settings |
|
| 484 | + * @access public |
|
| 485 | + * @param int $percent_of_max - desired percentage of the max upload_mb |
|
| 486 | + * @return int KB |
|
| 487 | + */ |
|
| 488 | + public function get_max_upload_size(int $percent_of_max = 0) |
|
| 489 | + { |
|
| 490 | + $max_upload = (int) (ini_get('upload_max_filesize')); |
|
| 491 | + $max_post = (int) (ini_get('post_max_size')); |
|
| 492 | + $memory_limit = (int) (ini_get('memory_limit')); |
|
| 493 | + |
|
| 494 | + // determine the smallest of the three values from above |
|
| 495 | + $upload_mb = min($max_upload, $max_post, $memory_limit); |
|
| 496 | + |
|
| 497 | + // convert MB to KB |
|
| 498 | + $upload_mb = $upload_mb * 1024; |
|
| 499 | + |
|
| 500 | + // don't want the full monty? then reduce the max upload size |
|
| 501 | + if ($percent_of_max) { |
|
| 502 | + // is percent_of_max like this -> 50 or like this -> 0.50 ? |
|
| 503 | + if ($percent_of_max > 1) { |
|
| 504 | + // changes 50 to 0.50 |
|
| 505 | + $percent_of_max = $percent_of_max / 100; |
|
| 506 | + } |
|
| 507 | + // make upload_mb a percentage of the max upload_mb |
|
| 508 | + $upload_mb = $upload_mb * $percent_of_max; |
|
| 509 | + } |
|
| 510 | + |
|
| 511 | + return $upload_mb; |
|
| 512 | + } |
|
| 513 | + |
|
| 514 | + |
|
| 515 | + /** |
|
| 516 | + * drop in replacement for PHP's fputcsv function - but this one works!!! |
|
| 517 | + * |
|
| 518 | + * @param resource $fh - file handle - what we are writing to |
|
| 519 | + * @param array $row - individual row of csv data |
|
| 520 | + * @param string $delimiter - csv delimiter |
|
| 521 | + * @param string $enclosure - csv enclosure |
|
| 522 | + * @param bool $mysql_null - allows php NULL to be overridden with MySQL's insertable NULL value |
|
| 523 | + * @return void |
|
| 524 | + */ |
|
| 525 | + private function fputcsv2( |
|
| 526 | + $fh, |
|
| 527 | + array $row, |
|
| 528 | + string $delimiter = ',', |
|
| 529 | + string $enclosure = '"', |
|
| 530 | + bool $mysql_null = false |
|
| 531 | + ) { |
|
| 532 | + // Allow user to filter the csv delimiter and enclosure for other countries csv standards |
|
| 533 | + $delimiter = apply_filters('FHEE__EE_CSV__fputcsv2__delimiter', $delimiter); |
|
| 534 | + $enclosure = apply_filters('FHEE__EE_CSV__fputcsv2__enclosure', $enclosure); |
|
| 535 | + |
|
| 536 | + $delimiter_esc = preg_quote($delimiter, '/'); |
|
| 537 | + $enclosure_esc = preg_quote($enclosure, '/'); |
|
| 538 | + |
|
| 539 | + $output = []; |
|
| 540 | + foreach ($row as $field_value) { |
|
| 541 | + if (is_object($field_value) || is_array($field_value)) { |
|
| 542 | + $field_value = serialize($field_value); |
|
| 543 | + } |
|
| 544 | + if ($field_value === null && $mysql_null) { |
|
| 545 | + $output[] = 'NULL'; |
|
| 546 | + continue; |
|
| 547 | + } |
|
| 548 | + |
|
| 549 | + $output[] = preg_match("/(?:${delimiter_esc}|${enclosure_esc}|\s)/", $field_value) |
|
| 550 | + ? ($enclosure . str_replace($enclosure, $enclosure . $enclosure, $field_value) . $enclosure) |
|
| 551 | + : $field_value; |
|
| 552 | + } |
|
| 553 | + |
|
| 554 | + fwrite($fh, join($delimiter, $output) . PHP_EOL); |
|
| 555 | + } |
|
| 556 | + |
|
| 557 | + |
|
| 558 | + /** |
|
| 559 | + * Gets the date format to use in teh csv. filterable |
|
| 560 | + * |
|
| 561 | + * @param string $current_format |
|
| 562 | + * @return string |
|
| 563 | + */ |
|
| 564 | + public function get_date_format_for_csv(string $current_format = ''): string |
|
| 565 | + { |
|
| 566 | + return apply_filters('FHEE__EE_CSV__get_date_format_for_csv__format', 'Y-m-d', $current_format); |
|
| 567 | + } |
|
| 568 | + |
|
| 569 | + |
|
| 570 | + /** |
|
| 571 | + * Gets the time format we want to use in CSV reports. Filterable |
|
| 572 | + * |
|
| 573 | + * @param string $current_format |
|
| 574 | + * @return string |
|
| 575 | + */ |
|
| 576 | + public function get_time_format_for_csv(string $current_format = ''): string |
|
| 577 | + { |
|
| 578 | + return apply_filters('FHEE__EE_CSV__get_time_format_for_csv__format', 'H:i:s', $current_format); |
|
| 579 | + } |
|
| 580 | 580 | } |
@@ -13,94 +13,94 @@ discard block |
||
| 13 | 13 | */ |
| 14 | 14 | class EE_Import implements ResettableInterface |
| 15 | 15 | { |
| 16 | - const do_insert = 'insert'; |
|
| 17 | - |
|
| 18 | - const do_update = 'update'; |
|
| 19 | - |
|
| 20 | - const do_nothing = 'nothing'; |
|
| 21 | - |
|
| 22 | - /** |
|
| 23 | - * @var EE_Import |
|
| 24 | - */ |
|
| 25 | - private static $_instance; |
|
| 26 | - |
|
| 27 | - /** |
|
| 28 | - * @var int |
|
| 29 | - */ |
|
| 30 | - protected $_total_inserts = 0; |
|
| 31 | - |
|
| 32 | - /** |
|
| 33 | - * @var int |
|
| 34 | - */ |
|
| 35 | - protected $_total_updates = 0; |
|
| 36 | - |
|
| 37 | - /** |
|
| 38 | - * @var int |
|
| 39 | - */ |
|
| 40 | - protected $_total_insert_errors = 0; |
|
| 41 | - |
|
| 42 | - /** |
|
| 43 | - * @var int |
|
| 44 | - */ |
|
| 45 | - protected $_total_update_errors = 0; |
|
| 46 | - |
|
| 47 | - |
|
| 48 | - /** |
|
| 49 | - * @return void |
|
| 50 | - */ |
|
| 51 | - private function __construct() |
|
| 52 | - { |
|
| 53 | - $this->_total_inserts = 0; |
|
| 54 | - $this->_total_updates = 0; |
|
| 55 | - $this->_total_insert_errors = 0; |
|
| 56 | - $this->_total_update_errors = 0; |
|
| 57 | - } |
|
| 58 | - |
|
| 59 | - |
|
| 60 | - /** |
|
| 61 | - * @ singleton method used to instantiate class object |
|
| 62 | - * @ access public |
|
| 63 | - * |
|
| 64 | - * @return EE_Import |
|
| 65 | - */ |
|
| 66 | - public static function instance(): EE_Import |
|
| 67 | - { |
|
| 68 | - // check if class object is instantiated |
|
| 69 | - if (! self::$_instance instanceof EE_Import) { |
|
| 70 | - self::$_instance = new self(); |
|
| 71 | - } |
|
| 72 | - return self::$_instance; |
|
| 73 | - } |
|
| 74 | - |
|
| 75 | - |
|
| 76 | - /** |
|
| 77 | - * Resets the importer |
|
| 78 | - * |
|
| 79 | - * @return EE_Import |
|
| 80 | - */ |
|
| 81 | - public static function reset(): EE_Import |
|
| 82 | - { |
|
| 83 | - self::$_instance = null; |
|
| 84 | - return self::instance(); |
|
| 85 | - } |
|
| 86 | - |
|
| 87 | - |
|
| 88 | - /** |
|
| 89 | - * generates HTML for a file upload input and form |
|
| 90 | - * |
|
| 91 | - * @param string $title - heading for the form |
|
| 92 | - * @param string $intro - additional text explaining what to do |
|
| 93 | - * @param string $form_url - EE Admin page to direct form to - in the form "espresso_{pageslug}" |
|
| 94 | - * @param string $action - EE Admin page route array "action" that form will direct to |
|
| 95 | - * @param string $type - type of file to import |
|
| 96 | - * @return string |
|
| 97 | - */ |
|
| 98 | - public function upload_form(string $title, string $intro, string $form_url, string $action, string $type): string |
|
| 99 | - { |
|
| 100 | - $form_url = EE_Admin_Page::add_query_args_and_nonce(['action' => $action], $form_url); |
|
| 101 | - |
|
| 102 | - ob_start(); |
|
| 103 | - ?> |
|
| 16 | + const do_insert = 'insert'; |
|
| 17 | + |
|
| 18 | + const do_update = 'update'; |
|
| 19 | + |
|
| 20 | + const do_nothing = 'nothing'; |
|
| 21 | + |
|
| 22 | + /** |
|
| 23 | + * @var EE_Import |
|
| 24 | + */ |
|
| 25 | + private static $_instance; |
|
| 26 | + |
|
| 27 | + /** |
|
| 28 | + * @var int |
|
| 29 | + */ |
|
| 30 | + protected $_total_inserts = 0; |
|
| 31 | + |
|
| 32 | + /** |
|
| 33 | + * @var int |
|
| 34 | + */ |
|
| 35 | + protected $_total_updates = 0; |
|
| 36 | + |
|
| 37 | + /** |
|
| 38 | + * @var int |
|
| 39 | + */ |
|
| 40 | + protected $_total_insert_errors = 0; |
|
| 41 | + |
|
| 42 | + /** |
|
| 43 | + * @var int |
|
| 44 | + */ |
|
| 45 | + protected $_total_update_errors = 0; |
|
| 46 | + |
|
| 47 | + |
|
| 48 | + /** |
|
| 49 | + * @return void |
|
| 50 | + */ |
|
| 51 | + private function __construct() |
|
| 52 | + { |
|
| 53 | + $this->_total_inserts = 0; |
|
| 54 | + $this->_total_updates = 0; |
|
| 55 | + $this->_total_insert_errors = 0; |
|
| 56 | + $this->_total_update_errors = 0; |
|
| 57 | + } |
|
| 58 | + |
|
| 59 | + |
|
| 60 | + /** |
|
| 61 | + * @ singleton method used to instantiate class object |
|
| 62 | + * @ access public |
|
| 63 | + * |
|
| 64 | + * @return EE_Import |
|
| 65 | + */ |
|
| 66 | + public static function instance(): EE_Import |
|
| 67 | + { |
|
| 68 | + // check if class object is instantiated |
|
| 69 | + if (! self::$_instance instanceof EE_Import) { |
|
| 70 | + self::$_instance = new self(); |
|
| 71 | + } |
|
| 72 | + return self::$_instance; |
|
| 73 | + } |
|
| 74 | + |
|
| 75 | + |
|
| 76 | + /** |
|
| 77 | + * Resets the importer |
|
| 78 | + * |
|
| 79 | + * @return EE_Import |
|
| 80 | + */ |
|
| 81 | + public static function reset(): EE_Import |
|
| 82 | + { |
|
| 83 | + self::$_instance = null; |
|
| 84 | + return self::instance(); |
|
| 85 | + } |
|
| 86 | + |
|
| 87 | + |
|
| 88 | + /** |
|
| 89 | + * generates HTML for a file upload input and form |
|
| 90 | + * |
|
| 91 | + * @param string $title - heading for the form |
|
| 92 | + * @param string $intro - additional text explaining what to do |
|
| 93 | + * @param string $form_url - EE Admin page to direct form to - in the form "espresso_{pageslug}" |
|
| 94 | + * @param string $action - EE Admin page route array "action" that form will direct to |
|
| 95 | + * @param string $type - type of file to import |
|
| 96 | + * @return string |
|
| 97 | + */ |
|
| 98 | + public function upload_form(string $title, string $intro, string $form_url, string $action, string $type): string |
|
| 99 | + { |
|
| 100 | + $form_url = EE_Admin_Page::add_query_args_and_nonce(['action' => $action], $form_url); |
|
| 101 | + |
|
| 102 | + ob_start(); |
|
| 103 | + ?> |
|
| 104 | 104 | <div class="ee-upload-form-dv"> |
| 105 | 105 | <h3><?php echo esc_html($title); ?></h3> |
| 106 | 106 | <p><?php echo esc_html($intro); ?></p> |
@@ -119,906 +119,906 @@ discard block |
||
| 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 | - return ob_get_clean(); |
|
| 131 | - } |
|
| 132 | - |
|
| 133 | - |
|
| 134 | - /** |
|
| 135 | - * @Import Event Espresso data - some code "borrowed" from event espresso csv_import.php |
|
| 136 | - * @access public |
|
| 137 | - * @return boolean success |
|
| 138 | - * @throws EE_Error |
|
| 139 | - * @throws ReflectionException |
|
| 140 | - */ |
|
| 141 | - public function import(): bool |
|
| 142 | - { |
|
| 143 | - require_once(EE_CLASSES . 'EE_CSV.class.php'); |
|
| 144 | - $EE_CSV = EE_CSV::instance(); |
|
| 145 | - /** @var RequestInterface $request */ |
|
| 146 | - $request = LoaderFactory::getLoader()->getShared(RequestInterface::class); |
|
| 147 | - $files = $this->getFileParams($request); |
|
| 148 | - |
|
| 149 | - $filename = $files['file']['name'][0]; |
|
| 150 | - $file_ext = substr(strrchr($filename, '.'), 1); |
|
| 151 | - $temp_file = $files['file']['tmp_name'][0]; |
|
| 152 | - $filesize = $files['file']['size'][0] / 1024;// convert from bytes to KB |
|
| 153 | - |
|
| 154 | - if ($file_ext !== 'csv') { |
|
| 155 | - EE_Error::add_error( |
|
| 156 | - sprintf( |
|
| 157 | - esc_html__('%s had an invalid file extension, not uploaded', 'event_espresso'), |
|
| 158 | - $filename |
|
| 159 | - ), |
|
| 160 | - __FILE__, |
|
| 161 | - __FUNCTION__, |
|
| 162 | - __LINE__ |
|
| 163 | - ); |
|
| 164 | - return false; |
|
| 165 | - } |
|
| 166 | - |
|
| 167 | - $ignore_max_file_size = (bool) apply_filters('FHEE__EE_Import__import__ignore_max_file_size', true, $files); |
|
| 168 | - if (! $ignore_max_file_size) { |
|
| 169 | - // max upload size in KB |
|
| 170 | - $max_upload = $EE_CSV->get_max_upload_size(); |
|
| 171 | - if ($filesize > $max_upload) { |
|
| 172 | - EE_Error::add_error( |
|
| 173 | - sprintf( |
|
| 174 | - esc_html__( |
|
| 175 | - "%s was too large of a file and could not be uploaded. The max filesize is %s' KB.", |
|
| 176 | - 'event_espresso' |
|
| 177 | - ), |
|
| 178 | - $filename, |
|
| 179 | - $max_upload |
|
| 180 | - ), |
|
| 181 | - __FILE__, |
|
| 182 | - __FUNCTION__, |
|
| 183 | - __LINE__ |
|
| 184 | - ); |
|
| 185 | - return false; |
|
| 186 | - } |
|
| 187 | - } |
|
| 188 | - |
|
| 189 | - $wp_upload_dir = str_replace(['\\', '/'], '/', wp_upload_dir()); |
|
| 190 | - $path_to_file = $wp_upload_dir['basedir'] . '/espresso/' . $filename; |
|
| 191 | - |
|
| 192 | - if (! move_uploaded_file($temp_file, $path_to_file)) { |
|
| 193 | - EE_Error::add_error( |
|
| 194 | - sprintf( |
|
| 195 | - esc_html__('%s was not successfully uploaded', 'event_espresso'), |
|
| 196 | - $filename |
|
| 197 | - ), |
|
| 198 | - __FILE__, |
|
| 199 | - __FUNCTION__, |
|
| 200 | - __LINE__ |
|
| 201 | - ); |
|
| 202 | - return false; |
|
| 203 | - } |
|
| 204 | - |
|
| 205 | - // convert csv to array |
|
| 206 | - $csv_array = $EE_CSV->import_csv_to_model_data_array($path_to_file); |
|
| 207 | - |
|
| 208 | - // was data successfully stored in an array? |
|
| 209 | - if (! is_array($csv_array)) { |
|
| 210 | - // no array? must be an error |
|
| 211 | - EE_Error::add_error( |
|
| 212 | - esc_html__('No file seems to have been uploaded', 'event_espresso'), |
|
| 213 | - __FILE__, |
|
| 214 | - __FUNCTION__, |
|
| 215 | - __LINE__ |
|
| 216 | - ); |
|
| 217 | - return false; |
|
| 218 | - } |
|
| 219 | - |
|
| 220 | - // save processed codes to db |
|
| 221 | - if ($this->save_csv_data_array_to_db($csv_array)) { |
|
| 222 | - return true; |
|
| 223 | - } |
|
| 224 | - return false; |
|
| 225 | - } |
|
| 226 | - |
|
| 227 | - |
|
| 228 | - /** |
|
| 229 | - * @param RequestInterface $request |
|
| 230 | - * @return array|null |
|
| 231 | - * @since $VID:$ |
|
| 232 | - */ |
|
| 233 | - private function getFileParams(RequestInterface $request): ?array |
|
| 234 | - { |
|
| 235 | - $error = null; |
|
| 236 | - if (! ($request->requestParamIsSet('import') && $request->requestParamIsSet('csv_submitted'))) { |
|
| 237 | - $error = esc_html__( |
|
| 238 | - "Invalid request. Expected request params 'import' or 'csv_submitted' were missing.", |
|
| 239 | - 'event_espresso' |
|
| 240 | - ); |
|
| 241 | - } |
|
| 242 | - $files = $request->filesParams(); |
|
| 243 | - |
|
| 244 | - if (! $error) { |
|
| 245 | - switch ($files['file']['error'][0]) { |
|
| 246 | - case UPLOAD_ERR_OK: |
|
| 247 | - break; |
|
| 248 | - case UPLOAD_ERR_INI_SIZE: |
|
| 249 | - $error = esc_html__( |
|
| 250 | - "'The uploaded file exceeds the upload_max_filesize directive in php.ini.'", |
|
| 251 | - 'event_espresso' |
|
| 252 | - ); |
|
| 253 | - break; |
|
| 254 | - case UPLOAD_ERR_FORM_SIZE: |
|
| 255 | - $error = esc_html__( |
|
| 256 | - 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.', |
|
| 257 | - 'event_espresso' |
|
| 258 | - ); |
|
| 259 | - break; |
|
| 260 | - case UPLOAD_ERR_PARTIAL: |
|
| 261 | - $error = esc_html__('The uploaded file was only partially uploaded.', 'event_espresso'); |
|
| 262 | - break; |
|
| 263 | - case UPLOAD_ERR_NO_FILE: |
|
| 264 | - $error = esc_html__('No file was uploaded.', 'event_espresso'); |
|
| 265 | - break; |
|
| 266 | - case UPLOAD_ERR_NO_TMP_DIR: |
|
| 267 | - $error = esc_html__('Missing a temporary folder.', 'event_espresso'); |
|
| 268 | - break; |
|
| 269 | - case UPLOAD_ERR_CANT_WRITE: |
|
| 270 | - $error = esc_html__('Failed to write file to disk.', 'event_espresso'); |
|
| 271 | - break; |
|
| 272 | - case UPLOAD_ERR_EXTENSION: |
|
| 273 | - $error = esc_html__('File upload stopped by extension.', 'event_espresso'); |
|
| 274 | - break; |
|
| 275 | - default: |
|
| 276 | - $error = esc_html__( |
|
| 277 | - 'An unknown error occurred and the file could not be uploaded', |
|
| 278 | - 'event_espresso' |
|
| 279 | - ); |
|
| 280 | - } |
|
| 281 | - } |
|
| 282 | - |
|
| 283 | - if ($error) { |
|
| 284 | - EE_Error::add_error($error, __FILE__, __FUNCTION__, __LINE__); |
|
| 285 | - return null; |
|
| 286 | - } |
|
| 287 | - |
|
| 288 | - return $files; |
|
| 289 | - } |
|
| 290 | - |
|
| 291 | - |
|
| 292 | - /** |
|
| 293 | - * Given an array of data (usually from a CSV import) attempts to save that data to the db. |
|
| 294 | - * If $model_name ISN'T provided, assumes that this is a 3d array, with toplevel keys being model names, |
|
| 295 | - * next level being numeric indexes adn each value representing a model object, and the last layer down |
|
| 296 | - * being keys of model fields and their proposed values. |
|
| 297 | - * If $model_name IS provided, assumes a 2d array of the bottom two layers previously mentioned. |
|
| 298 | - * If the CSV data says (in the metadata row) that it's from the SAME database, |
|
| 299 | - * we treat the IDs in the CSV as the normal IDs, and try to update those records. However, if those |
|
| 300 | - * IDs DON'T exist in the database, they're treated as temporary IDs, |
|
| 301 | - * which can used elsewhere to refer to the same object. Once an item |
|
| 302 | - * with a temporary ID gets inserted, we record its mapping from temporary |
|
| 303 | - * ID to real ID, and use the real ID in place of the temporary ID |
|
| 304 | - * when that temporary ID was used as a foreign key. |
|
| 305 | - * If the CSV data says (in the metadata again) that it's from a DIFFERENT database, |
|
| 306 | - * we treat all the IDs in the CSV as temporary ID- eg, if the CSV specifies an event with |
|
| 307 | - * ID 1, and the database already has an event with ID 1, we assume that's just a coincidence, |
|
| 308 | - * and insert a new event, and map it's temporary ID of 1 over to its new real ID. |
|
| 309 | - * An important exception are non-auto-increment primary keys. If one entry in the |
|
| 310 | - * CSV file has the same ID as one in the DB, we assume they are meant to be |
|
| 311 | - * the same item, and instead update the item in the DB with that same ID. |
|
| 312 | - * Also note, we remember the mappings permanently. So the 2nd, 3rd, and 10000th |
|
| 313 | - * time you import a CSV from a different site, we remember their mappings, and |
|
| 314 | - * will try to update the item in the DB instead of inserting another item (eg |
|
| 315 | - * if we previously imported an event with temporary ID 1, and then it got a |
|
| 316 | - * real ID of 123, we remember that. So the next time we import an event with |
|
| 317 | - * temporary ID, from the same site, we know that it's real ID is 123, and will |
|
| 318 | - * update that event, instead of adding a new event). |
|
| 319 | - * |
|
| 320 | - * @param array $csv_data_array the array containing the csv data produced from |
|
| 321 | - * EE_CSV::import_csv_to_model_data_array() |
|
| 322 | - * @param string $model_name |
|
| 323 | - * @return bool TRUE on success, FALSE on fail |
|
| 324 | - * @throws EE_Error |
|
| 325 | - * @throws ReflectionException |
|
| 326 | - */ |
|
| 327 | - public function save_csv_data_array_to_db(array $csv_data_array, string $model_name = ''): bool |
|
| 328 | - { |
|
| 329 | - $success = false; |
|
| 330 | - // whether to treat this import as if it's data from a different database or not |
|
| 331 | - // ie, if it IS from a different database, ignore foreign keys with |
|
| 332 | - $export_from_site_a_to_b = true; |
|
| 333 | - // first level of array is not table information but a table name was passed to the function |
|
| 334 | - // array is only two levels deep, so let's fix that by adding a level, else the next steps will fail |
|
| 335 | - if ($model_name) { |
|
| 336 | - $csv_data_array = [$csv_data_array]; |
|
| 337 | - } |
|
| 338 | - // begin looking through the $csv_data_array, expecting the toplevel key to be the model's name... |
|
| 339 | - $old_site_url = 'none-specified'; |
|
| 340 | - // handle metadata |
|
| 341 | - if (isset($csv_data_array[ EE_CSV::metadata_header ])) { |
|
| 342 | - $csv_metadata = array_shift($csv_data_array[ EE_CSV::metadata_header ]); |
|
| 343 | - // ok so its metadata, dont try to save it to the db obviously... |
|
| 344 | - if (isset($csv_metadata['site_url']) && $csv_metadata['site_url'] == site_url()) { |
|
| 345 | - EE_Error::add_attention( |
|
| 346 | - esc_html__( |
|
| 347 | - 'CSV Data appears to be from the same database, so attempting to update data', |
|
| 348 | - 'event_espresso' |
|
| 349 | - ) |
|
| 350 | - ); |
|
| 351 | - $export_from_site_a_to_b = false; |
|
| 352 | - } else { |
|
| 353 | - $old_site_url = $csv_metadata['site_url'] ?? $old_site_url; |
|
| 354 | - EE_Error::add_attention( |
|
| 355 | - sprintf( |
|
| 356 | - esc_html__( |
|
| 357 | - "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", |
|
| 358 | - "event_espresso" |
|
| 359 | - ), |
|
| 360 | - $old_site_url, |
|
| 361 | - site_url() |
|
| 362 | - ) |
|
| 363 | - ); |
|
| 364 | - } |
|
| 365 | - unset($csv_data_array[ EE_CSV::metadata_header ]); |
|
| 366 | - } |
|
| 367 | - |
|
| 368 | - $id_mapping_option_name = 'ee_id_mapping_from' . sanitize_title($old_site_url); |
|
| 369 | - /** |
|
| 370 | - * @var $old_db_to_new_db_mapping 2d array: toplevel keys being model names, |
|
| 371 | - * bottom-level keys being the original key, |
|
| 372 | - * and the value will be the newly-inserted ID. |
|
| 373 | - * If we have already imported data from the same website via CSV, |
|
| 374 | - * it should be kept in this wp option |
|
| 375 | - */ |
|
| 376 | - $old_db_to_new_db_mapping = (array) get_option($id_mapping_option_name, []); |
|
| 377 | - |
|
| 378 | - if ($old_db_to_new_db_mapping) { |
|
| 379 | - EE_Error::add_attention( |
|
| 380 | - sprintf( |
|
| 381 | - esc_html__( |
|
| 382 | - "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", |
|
| 383 | - "event_espresso" |
|
| 384 | - ), |
|
| 385 | - $old_site_url, |
|
| 386 | - site_url() |
|
| 387 | - ) |
|
| 388 | - ); |
|
| 389 | - } |
|
| 390 | - $old_db_to_new_db_mapping = $this->save_data_rows_to_db( |
|
| 391 | - $csv_data_array, |
|
| 392 | - $export_from_site_a_to_b, |
|
| 393 | - $old_db_to_new_db_mapping |
|
| 394 | - ); |
|
| 395 | - |
|
| 396 | - // save the mapping from old db to new db in case they try re-importing the same data from the same website again |
|
| 397 | - update_option($id_mapping_option_name, $old_db_to_new_db_mapping); |
|
| 398 | - |
|
| 399 | - if ($this->_total_updates > 0) { |
|
| 400 | - EE_Error::add_success( |
|
| 401 | - sprintf( |
|
| 402 | - esc_html__("%s existing records in the database were updated.", "event_espresso"), |
|
| 403 | - $this->_total_updates |
|
| 404 | - ) |
|
| 405 | - ); |
|
| 406 | - $success = true; |
|
| 407 | - } |
|
| 408 | - if ($this->_total_inserts > 0) { |
|
| 409 | - EE_Error::add_success( |
|
| 410 | - sprintf( |
|
| 411 | - esc_html__("%s new records were added to the database.", "event_espresso"), |
|
| 412 | - $this->_total_inserts |
|
| 413 | - ) |
|
| 414 | - ); |
|
| 415 | - $success = true; |
|
| 416 | - } |
|
| 417 | - |
|
| 418 | - if ($this->_total_update_errors > 0) { |
|
| 419 | - EE_Error::add_error( |
|
| 420 | - sprintf( |
|
| 421 | - esc_html__( |
|
| 422 | - "'One or more errors occurred, and a total of %s existing records in the database were <strong>not</strong> updated.'", |
|
| 423 | - "event_espresso" |
|
| 424 | - ), |
|
| 425 | - $this->_total_update_errors |
|
| 426 | - ), |
|
| 427 | - __FILE__, |
|
| 428 | - __FUNCTION__, |
|
| 429 | - __LINE__ |
|
| 430 | - ); |
|
| 431 | - $success = false; |
|
| 432 | - } |
|
| 433 | - if ($this->_total_insert_errors > 0) { |
|
| 434 | - EE_Error::add_error( |
|
| 435 | - sprintf( |
|
| 436 | - esc_html__( |
|
| 437 | - "One or more errors occurred, and a total of %s new records were <strong>not</strong> added to the database.'", |
|
| 438 | - "event_espresso" |
|
| 439 | - ), |
|
| 440 | - $this->_total_insert_errors |
|
| 441 | - ), |
|
| 442 | - __FILE__, |
|
| 443 | - __FUNCTION__, |
|
| 444 | - __LINE__ |
|
| 445 | - ); |
|
| 446 | - $success = false; |
|
| 447 | - } |
|
| 448 | - |
|
| 449 | - // lastly, we need to update the datetime and ticket sold amounts |
|
| 450 | - // as those may have been affected by this |
|
| 451 | - EEM_Ticket::instance()->update_tickets_sold(EEM_Ticket::instance()->get_all()); |
|
| 452 | - |
|
| 453 | - // if there was at least one success and absolutely no errors |
|
| 454 | - return $success; |
|
| 455 | - } |
|
| 456 | - |
|
| 457 | - |
|
| 458 | - /** |
|
| 459 | - * Processes the array of data, given the knowledge that it's from the same database or a different one, |
|
| 460 | - * and the mapping from temporary IDs to real IDs. |
|
| 461 | - * If the data is from a different database, we treat the primary keys and their corresponding |
|
| 462 | - * foreign keys as "temp Ids", basically identifiers that get mapped to real primary keys |
|
| 463 | - * in the real target database. As items are inserted, their temporary primary keys |
|
| 464 | - * are mapped to the real IDs in the target database. Also, before doing any update or |
|
| 465 | - * insert, we replace all the temp ID which are foreign keys with their mapped real IDs. |
|
| 466 | - * An exception: string primary keys are treated as real IDs, or else we'd need to |
|
| 467 | - * dynamically generate new string primary keys which would be very awkward for the country table etc. |
|
| 468 | - * Also, models with no primary key are strange too. We combine use their primary key INDEX (a |
|
| 469 | - * combination of fields) to create a unique string identifying the row and store |
|
| 470 | - * those in the mapping. |
|
| 471 | - * |
|
| 472 | - * If the data is from the same database, we usually treat primary keys as real IDs. |
|
| 473 | - * An exception is if there is nothing in the database for that ID. If that's the case, |
|
| 474 | - * we need to insert a new row for that ID, and then map from the non-existent ID |
|
| 475 | - * to the newly-inserted real ID. |
|
| 476 | - * |
|
| 477 | - * @param array $csv_data_array |
|
| 478 | - * @param bool $export_from_site_a_to_b |
|
| 479 | - * @param array $old_db_to_new_db_mapping |
|
| 480 | - * @return array|bool updated $old_db_to_new_db_mapping |
|
| 481 | - * @throws EE_Error |
|
| 482 | - * @throws ReflectionException |
|
| 483 | - */ |
|
| 484 | - public function save_data_rows_to_db( |
|
| 485 | - array $csv_data_array, |
|
| 486 | - bool $export_from_site_a_to_b, |
|
| 487 | - array $old_db_to_new_db_mapping |
|
| 488 | - ) { |
|
| 489 | - foreach ($csv_data_array as $model_name => $model_data_from_import) { |
|
| 490 | - // check that assumption was correct. If |
|
| 491 | - if (! EE_Registry::instance()->is_model_name($model_name)) { |
|
| 492 | - // no table info in the array and no table name passed to the function?? FAIL |
|
| 493 | - EE_Error::add_error( |
|
| 494 | - esc_html__( |
|
| 495 | - 'No table information was specified and/or found, therefore the import could not be completed', |
|
| 496 | - 'event_espresso' |
|
| 497 | - ), |
|
| 498 | - __FILE__, |
|
| 499 | - __FUNCTION__, |
|
| 500 | - __LINE__ |
|
| 501 | - ); |
|
| 502 | - return false; |
|
| 503 | - } |
|
| 504 | - |
|
| 505 | - /* @var $model EEM_Base */ |
|
| 506 | - $model = EE_Registry::instance()->load_model($model_name); |
|
| 507 | - |
|
| 508 | - // so without further ado, scanning all the data provided for primary keys and their initial values |
|
| 509 | - foreach ($model_data_from_import as $model_object_data) { |
|
| 510 | - // before we do ANYTHING, make sure the csv row wasn't just completely blank |
|
| 511 | - $row_is_completely_empty = true; |
|
| 512 | - foreach ($model_object_data as $field) { |
|
| 513 | - if ($field) { |
|
| 514 | - $row_is_completely_empty = false; |
|
| 515 | - } |
|
| 516 | - } |
|
| 517 | - if ($row_is_completely_empty) { |
|
| 518 | - continue; |
|
| 519 | - } |
|
| 520 | - // find the PK in the row of data (or a combined key if |
|
| 521 | - // there is no primary key) |
|
| 522 | - if ($model->has_primary_key_field()) { |
|
| 523 | - $id_in_csv = $model_object_data[ $model->primary_key_name() ]; |
|
| 524 | - } else { |
|
| 525 | - $id_in_csv = $model->get_index_primary_key_string($model_object_data); |
|
| 526 | - } |
|
| 527 | - |
|
| 528 | - $model_object_data = $this->_replace_temp_ids_with_mappings( |
|
| 529 | - $model_object_data, |
|
| 530 | - $model, |
|
| 531 | - $old_db_to_new_db_mapping, |
|
| 532 | - $export_from_site_a_to_b |
|
| 533 | - ); |
|
| 534 | - // now we need to decide if we're going to |
|
| 535 | - $what_to_do = $export_from_site_a_to_b |
|
| 536 | - // add a new model object given the $model_object_data |
|
| 537 | - ? $this->_decide_whether_to_insert_or_update_given_data_from_other_db( |
|
| 538 | - $id_in_csv, |
|
| 539 | - $model_name, |
|
| 540 | - $old_db_to_new_db_mapping |
|
| 541 | - ) |
|
| 542 | - // or just update cuz this is just a re-import |
|
| 543 | - : $this->_decide_whether_to_insert_or_update_given_data_from_same_db( |
|
| 544 | - $model_object_data, |
|
| 545 | - $model |
|
| 546 | - ); |
|
| 547 | - |
|
| 548 | - if ($what_to_do === EE_Import::do_nothing) { |
|
| 549 | - continue; |
|
| 550 | - } |
|
| 551 | - |
|
| 552 | - // double-check we actually want to insert, if that's what we're planning |
|
| 553 | - // based on whether this item would be unique in the DB or not |
|
| 554 | - if ($what_to_do == EE_Import::do_insert) { |
|
| 555 | - // we're supposed to be inserting. But wait, will this thing |
|
| 556 | - // be acceptable if inserted? |
|
| 557 | - $conflicting = $model->get_one_conflicting($model_object_data, false); |
|
| 558 | - if ($conflicting) { |
|
| 559 | - // ok, this item would conflict if inserted. Just update the item that it conflicts with. |
|
| 560 | - $what_to_do = EE_Import::do_update; |
|
| 561 | - // and if this model has a primary key, remember its mapping |
|
| 562 | - if ($model->has_primary_key_field()) { |
|
| 563 | - $old_db_to_new_db_mapping[ $model_name ][ $id_in_csv ] = $conflicting->ID(); |
|
| 564 | - $model_object_data[ $model->primary_key_name() ] = $conflicting->ID(); |
|
| 565 | - } else { |
|
| 566 | - // we want to update this conflicting item, instead of inserting a conflicting item |
|
| 567 | - // so we need to make sure they match entirely |
|
| 568 | - // (it's possible that they only conflicted on one field, |
|
| 569 | - // but we need them to match on other fields for the WHERE conditions in the update). |
|
| 570 | - // At the time of this comment, there were no models like this |
|
| 571 | - foreach ($model->get_combined_primary_key_fields() as $key_field) { |
|
| 572 | - $model_object_data[ $key_field->get_name() ] = $conflicting->get( |
|
| 573 | - $key_field->get_name() |
|
| 574 | - ); |
|
| 575 | - } |
|
| 576 | - } |
|
| 577 | - } |
|
| 578 | - } |
|
| 579 | - if ($what_to_do == EE_Import::do_insert) { |
|
| 580 | - $old_db_to_new_db_mapping = $this->_insert_from_data_array( |
|
| 581 | - $id_in_csv, |
|
| 582 | - $model_object_data, |
|
| 583 | - $model, |
|
| 584 | - $old_db_to_new_db_mapping |
|
| 585 | - ); |
|
| 586 | - } elseif ($what_to_do == EE_Import::do_update) { |
|
| 587 | - $old_db_to_new_db_mapping = $this->_update_from_data_array( |
|
| 588 | - $id_in_csv, |
|
| 589 | - $model_object_data, |
|
| 590 | - $model, |
|
| 591 | - $old_db_to_new_db_mapping |
|
| 592 | - ); |
|
| 593 | - } else { |
|
| 594 | - throw new EE_Error( |
|
| 595 | - sprintf( |
|
| 596 | - esc_html__( |
|
| 597 | - 'Programming error. We should be inserting or updating, but instead we are being told to "%s", whifh is invalid', |
|
| 598 | - 'event_espresso' |
|
| 599 | - ), |
|
| 600 | - $what_to_do |
|
| 601 | - ) |
|
| 602 | - ); |
|
| 603 | - } |
|
| 604 | - } |
|
| 605 | - } |
|
| 606 | - return $old_db_to_new_db_mapping; |
|
| 607 | - } |
|
| 608 | - |
|
| 609 | - |
|
| 610 | - /** |
|
| 611 | - * Decides whether or not to insert, given that this data is from another database. |
|
| 612 | - * So, if the primary key of this $model_object_data already exists in the database, |
|
| 613 | - * it's just a coincidence and we should still insert. The only time we should |
|
| 614 | - * update is when we know what it maps to, or there's something that would |
|
| 615 | - * conflict (and we should instead just update that conflicting thing) |
|
| 616 | - * |
|
| 617 | - * @param string $id_in_csv |
|
| 618 | - * @param string $model_name |
|
| 619 | - * @param array $old_db_to_new_db_mapping by reference so it can be modified |
|
| 620 | - * @return string one of the constants on this class that starts with do_* |
|
| 621 | - */ |
|
| 622 | - protected function _decide_whether_to_insert_or_update_given_data_from_other_db( |
|
| 623 | - string $id_in_csv, |
|
| 624 | - string $model_name, |
|
| 625 | - array $old_db_to_new_db_mapping |
|
| 626 | - ): string { |
|
| 627 | - // if it's a site-to-site export-and-import, see if this model object's id |
|
| 628 | - // in the old data that we know of |
|
| 629 | - if (isset($old_db_to_new_db_mapping[ $model_name ][ $id_in_csv ])) { |
|
| 630 | - return EE_Import::do_update; |
|
| 631 | - } |
|
| 632 | - return EE_Import::do_insert; |
|
| 633 | - } |
|
| 634 | - |
|
| 635 | - |
|
| 636 | - /** |
|
| 637 | - * If this thing basically already exists in the database, we want to update it; |
|
| 638 | - * otherwise insert it (ie, someone tweaked the CSV file, or the item was |
|
| 639 | - * deleted in the database so it should be re-inserted) |
|
| 640 | - * |
|
| 641 | - * @param array $model_object_data |
|
| 642 | - * @param EEM_Base $model |
|
| 643 | - * @return string |
|
| 644 | - * @throws EE_Error |
|
| 645 | - */ |
|
| 646 | - protected function _decide_whether_to_insert_or_update_given_data_from_same_db( |
|
| 647 | - array $model_object_data, |
|
| 648 | - EEM_Base $model |
|
| 649 | - ): string { |
|
| 650 | - // in this case, check if this thing ACTUALLY exists in the database |
|
| 651 | - if ($model->get_one_conflicting($model_object_data)) { |
|
| 652 | - return EE_Import::do_update; |
|
| 653 | - } |
|
| 654 | - return EE_Import::do_insert; |
|
| 655 | - } |
|
| 656 | - |
|
| 657 | - |
|
| 658 | - /** |
|
| 659 | - * Using the $old_db_to_new_db_mapping array, replaces all the temporary IDs |
|
| 660 | - * with their mapped real IDs. Eg, if importing from site A to B, the mapping |
|
| 661 | - * file may indicate that the ID "my_event_id" maps to an actual event ID of 123. |
|
| 662 | - * So this function searches for any event temp Ids called "my_event_id" and |
|
| 663 | - * replaces them with 123. |
|
| 664 | - * Also, if there is no temp ID for the INT foreign keys from another database, |
|
| 665 | - * replaces them with 0 or the field's default. |
|
| 666 | - * |
|
| 667 | - * @param array $model_object_data |
|
| 668 | - * @param EEM_Base $model |
|
| 669 | - * @param array $old_db_to_new_db_mapping |
|
| 670 | - * @param bool $export_from_site_a_to_b |
|
| 671 | - * @return array updated model object data with temp IDs removed |
|
| 672 | - * @throws EE_Error |
|
| 673 | - */ |
|
| 674 | - protected function _replace_temp_ids_with_mappings( |
|
| 675 | - array $model_object_data, |
|
| 676 | - EEM_Base $model, |
|
| 677 | - array $old_db_to_new_db_mapping, |
|
| 678 | - bool $export_from_site_a_to_b |
|
| 679 | - ): array { |
|
| 680 | - // if this model object's primary key is in the mapping, replace it |
|
| 681 | - if ( |
|
| 682 | - $model->has_primary_key_field() |
|
| 683 | - && $model->get_primary_key_field()->is_auto_increment() |
|
| 684 | - && isset($old_db_to_new_db_mapping[ $model->get_this_model_name() ]) |
|
| 685 | - && isset( |
|
| 686 | - $old_db_to_new_db_mapping[ $model->get_this_model_name( |
|
| 687 | - ) ][ $model_object_data[ $model->primary_key_name() ] ] |
|
| 688 | - ) |
|
| 689 | - ) { |
|
| 690 | - $model_object_data[ $model->primary_key_name() ] = $old_db_to_new_db_mapping[ $model->get_this_model_name( |
|
| 691 | - ) ][ $model_object_data[ $model->primary_key_name() ] ]; |
|
| 692 | - } |
|
| 693 | - |
|
| 694 | - try { |
|
| 695 | - $model_name_field = $model->get_field_containing_related_model_name(); |
|
| 696 | - } catch (EE_Error $e) { |
|
| 697 | - $model_name_field = null; |
|
| 698 | - } |
|
| 699 | - |
|
| 700 | - foreach ($model->field_settings(true) as $field_obj) { |
|
| 701 | - if (! $field_obj instanceof EE_Foreign_Key_Int_Field) { |
|
| 702 | - // not a foreign key, or it's a string foreign key |
|
| 703 | - // which we leave alone, because those are things like country names, |
|
| 704 | - // which we'd really rather not make 2 USAs etc (we'd actually prefer to just update one) |
|
| 705 | - // or it's just a regular value that ought to be replaced |
|
| 706 | - continue; |
|
| 707 | - } |
|
| 708 | - $models_pointed_to = $field_obj->get_model_names_pointed_to(); |
|
| 709 | - foreach ($models_pointed_to as $model_pointed_to_by_fk) { |
|
| 710 | - if ($model_name_field) { |
|
| 711 | - $value_of_model_name_field = $model_object_data[ $model_name_field->get_name() ]; |
|
| 712 | - if ($value_of_model_name_field == $model_pointed_to_by_fk) { |
|
| 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 | - // once we've found a mapping for this field no need to continue |
|
| 720 | - // break; |
|
| 721 | - } |
|
| 722 | - } else { |
|
| 723 | - $model_object_data[ $field_obj->get_name() ] = $this->_find_mapping_in( |
|
| 724 | - $model_object_data[ $field_obj->get_name() ], |
|
| 725 | - $model_pointed_to_by_fk, |
|
| 726 | - $old_db_to_new_db_mapping, |
|
| 727 | - $export_from_site_a_to_b |
|
| 728 | - ); |
|
| 729 | - // once we've found a mapping for this field no need to continue |
|
| 730 | - // break; |
|
| 731 | - } |
|
| 732 | - } |
|
| 733 | - } |
|
| 734 | - |
|
| 735 | - if ($model instanceof EEM_Term_Taxonomy) { |
|
| 736 | - $model_object_data = $this->_handle_split_term_ids($model_object_data); |
|
| 737 | - } |
|
| 738 | - return $model_object_data; |
|
| 739 | - } |
|
| 740 | - |
|
| 741 | - |
|
| 742 | - /** |
|
| 743 | - * If the data was exported PRE-4.2, but then imported POST-4.2, then the term_id |
|
| 744 | - * this term-taxonomy refers to may be out-of-date so we need to update it. |
|
| 745 | - * see https://make.wordpress.org/core/2015/02/16/taxonomy-term-splitting-in-4-2-a-developer-guide/ |
|
| 746 | - * |
|
| 747 | - * @param array $model_object_data |
|
| 748 | - * @return array new model object data |
|
| 749 | - */ |
|
| 750 | - protected function _handle_split_term_ids(array $model_object_data): array |
|
| 751 | - { |
|
| 752 | - if ( |
|
| 753 | - isset($model_object_data['term_id']) |
|
| 754 | - && isset($model_object_data['taxonomy']) |
|
| 755 | - && apply_filters( |
|
| 756 | - 'FHEE__EE_Import__handle_split_term_ids__function_exists', |
|
| 757 | - function_exists('wp_get_split_term'), |
|
| 758 | - $model_object_data |
|
| 759 | - ) |
|
| 760 | - ) { |
|
| 761 | - $new_term_id = wp_get_split_term($model_object_data['term_id'], $model_object_data['taxonomy']); |
|
| 762 | - if ($new_term_id) { |
|
| 763 | - $model_object_data['term_id'] = $new_term_id; |
|
| 764 | - } |
|
| 765 | - } |
|
| 766 | - return $model_object_data; |
|
| 767 | - } |
|
| 768 | - |
|
| 769 | - |
|
| 770 | - /** |
|
| 771 | - * Given the object's ID and its model's name, find it int he mapping data, |
|
| 772 | - * bearing in mind where it came from |
|
| 773 | - * |
|
| 774 | - * @param int|string $object_id |
|
| 775 | - * @param string $model_name |
|
| 776 | - * @param array $old_db_to_new_db_mapping |
|
| 777 | - * @param bool $export_from_site_a_to_b |
|
| 778 | - * @return int |
|
| 779 | - */ |
|
| 780 | - protected function _find_mapping_in( |
|
| 781 | - $object_id, |
|
| 782 | - string $model_name, |
|
| 783 | - array $old_db_to_new_db_mapping, |
|
| 784 | - bool $export_from_site_a_to_b |
|
| 785 | - ) { |
|
| 786 | - if (isset($old_db_to_new_db_mapping[ $model_name ][ $object_id ])) { |
|
| 787 | - return $old_db_to_new_db_mapping[ $model_name ][ $object_id ]; |
|
| 788 | - } |
|
| 789 | - if ($object_id == '0' || $object_id == '') { |
|
| 790 | - // leave as-is |
|
| 791 | - return $object_id; |
|
| 792 | - } |
|
| 793 | - if ($export_from_site_a_to_b) { |
|
| 794 | - // we couldn't find a mapping for this, and it's from a different site, |
|
| 795 | - // so blank it out |
|
| 796 | - return null; |
|
| 797 | - } |
|
| 798 | - // we couldn't find a mapping for this, but it's from this DB anyway |
|
| 799 | - // so let's just leave it as-is |
|
| 800 | - return $object_id; |
|
| 801 | - } |
|
| 802 | - |
|
| 803 | - |
|
| 804 | - /** |
|
| 805 | - * |
|
| 806 | - * @param int|string $id_in_csv |
|
| 807 | - * @param array $model_object_data |
|
| 808 | - * @param EEM_Base $model |
|
| 809 | - * @param array $old_db_to_new_db_mapping |
|
| 810 | - * @return array updated $old_db_to_new_db_mapping |
|
| 811 | - * @throws EE_Error |
|
| 812 | - */ |
|
| 813 | - protected function _insert_from_data_array( |
|
| 814 | - $id_in_csv, |
|
| 815 | - array $model_object_data, |
|
| 816 | - EEM_Base $model, |
|
| 817 | - array $old_db_to_new_db_mapping |
|
| 818 | - ): array { |
|
| 819 | - // remove the primary key, if there is one (we don't want it for inserts OR updates) |
|
| 820 | - // we'll put it back in if we need it |
|
| 821 | - if ($model->has_primary_key_field() && $model->get_primary_key_field()->is_auto_increment()) { |
|
| 822 | - $effective_id = $model_object_data[ $model->primary_key_name() ]; |
|
| 823 | - unset($model_object_data[ $model->primary_key_name() ]); |
|
| 824 | - } else { |
|
| 825 | - $effective_id = $model->get_index_primary_key_string($model_object_data); |
|
| 826 | - } |
|
| 827 | - // the model takes care of validating the CSV's input |
|
| 828 | - try { |
|
| 829 | - $new_id = $model->insert($model_object_data); |
|
| 830 | - if ($new_id) { |
|
| 831 | - $old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $id_in_csv ] = $new_id; |
|
| 832 | - $this->_total_inserts++; |
|
| 833 | - EE_Error::add_success( |
|
| 834 | - sprintf( |
|
| 835 | - esc_html__("Successfully added new %s (with id %s) with csv data %s", "event_espresso"), |
|
| 836 | - $model->get_this_model_name(), |
|
| 837 | - $new_id, |
|
| 838 | - implode(",", $model_object_data) |
|
| 839 | - ) |
|
| 840 | - ); |
|
| 841 | - } else { |
|
| 842 | - $this->_total_insert_errors++; |
|
| 843 | - // put the ID used back in there for the error message |
|
| 844 | - if ($model->has_primary_key_field()) { |
|
| 845 | - $model_object_data[ $model->primary_key_name() ] = $effective_id; |
|
| 846 | - } |
|
| 847 | - EE_Error::add_error( |
|
| 848 | - sprintf( |
|
| 849 | - esc_html__("Could not insert new %s with the csv data: %s", "event_espresso"), |
|
| 850 | - $model->get_this_model_name(), |
|
| 851 | - http_build_query($model_object_data) |
|
| 852 | - ), |
|
| 853 | - __FILE__, |
|
| 854 | - __FUNCTION__, |
|
| 855 | - __LINE__ |
|
| 856 | - ); |
|
| 857 | - } |
|
| 858 | - } catch (EE_Error $e) { |
|
| 859 | - $this->_total_insert_errors++; |
|
| 860 | - if ($model->has_primary_key_field()) { |
|
| 861 | - $model_object_data[ $model->primary_key_name() ] = $effective_id; |
|
| 862 | - } |
|
| 863 | - EE_Error::add_error( |
|
| 864 | - sprintf( |
|
| 865 | - esc_html__("Could not insert new %s with the csv data: %s because %s", "event_espresso"), |
|
| 866 | - $model->get_this_model_name(), |
|
| 867 | - implode(",", $model_object_data), |
|
| 868 | - $e->getMessage() |
|
| 869 | - ), |
|
| 870 | - __FILE__, |
|
| 871 | - __FUNCTION__, |
|
| 872 | - __LINE__ |
|
| 873 | - ); |
|
| 874 | - } |
|
| 875 | - return $old_db_to_new_db_mapping; |
|
| 876 | - } |
|
| 877 | - |
|
| 878 | - |
|
| 879 | - /** |
|
| 880 | - * Given the model object data, finds the row to update and updates it |
|
| 881 | - * |
|
| 882 | - * @param string|int $id_in_csv |
|
| 883 | - * @param array $model_object_data |
|
| 884 | - * @param EEM_Base $model |
|
| 885 | - * @param array $old_db_to_new_db_mapping |
|
| 886 | - * @return array updated $old_db_to_new_db_mapping |
|
| 887 | - */ |
|
| 888 | - protected function _update_from_data_array( |
|
| 889 | - $id_in_csv, |
|
| 890 | - array $model_object_data, |
|
| 891 | - EEM_Base $model, |
|
| 892 | - array $old_db_to_new_db_mapping |
|
| 893 | - ): array { |
|
| 894 | - $conditions = null; |
|
| 895 | - try { |
|
| 896 | - // let's keep two copies of the model object data: |
|
| 897 | - // one for performing an update, one for everything else |
|
| 898 | - $model_object_data_for_update = $model_object_data; |
|
| 899 | - if ($model->has_primary_key_field()) { |
|
| 900 | - $conditions = [$model->primary_key_name() => $model_object_data[ $model->primary_key_name() ]]; |
|
| 901 | - // remove the primary key because we shouldn't use it for updating |
|
| 902 | - unset($model_object_data_for_update[ $model->primary_key_name() ]); |
|
| 903 | - } elseif ($model->get_combined_primary_key_fields() > 1) { |
|
| 904 | - $conditions = []; |
|
| 905 | - foreach ($model->get_combined_primary_key_fields() as $key_field) { |
|
| 906 | - $conditions[ $key_field->get_name() ] = $model_object_data[ $key_field->get_name() ]; |
|
| 907 | - } |
|
| 908 | - } else { |
|
| 909 | - // this should just throw an exception, explaining that we dont have a primary key (or a combined key) |
|
| 910 | - $model->primary_key_name(); |
|
| 911 | - } |
|
| 912 | - |
|
| 913 | - $success = $model->update($model_object_data_for_update, [$conditions]); |
|
| 914 | - if ($success) { |
|
| 915 | - $this->_total_updates++; |
|
| 916 | - EE_Error::add_success( |
|
| 917 | - sprintf( |
|
| 918 | - esc_html__("Successfully updated %s with csv data %s", "event_espresso"), |
|
| 919 | - $model->get_this_model_name(), |
|
| 920 | - implode(",", $model_object_data_for_update) |
|
| 921 | - ) |
|
| 922 | - ); |
|
| 923 | - // we should still record the mapping even though it was an update |
|
| 924 | - // because if we were going to insert something but it was going to conflict |
|
| 925 | - // we would have last-minute decided to update. So we'd like to know what we updated |
|
| 926 | - // and so we record what record ended up being updated using the mapping |
|
| 927 | - if ($model->has_primary_key_field()) { |
|
| 928 | - $new_key_for_mapping = $model_object_data[ $model->primary_key_name() ]; |
|
| 929 | - } else { |
|
| 930 | - // no primary key just a combined key |
|
| 931 | - $new_key_for_mapping = $model->get_index_primary_key_string($model_object_data); |
|
| 932 | - } |
|
| 933 | - $old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $id_in_csv ] = $new_key_for_mapping; |
|
| 934 | - } else { |
|
| 935 | - $matched_items = $model->get_all([$conditions]); |
|
| 936 | - if (! $matched_items) { |
|
| 937 | - // no items were matched (so we shouldn't have updated)... but then we should have inserted? what the heck? |
|
| 938 | - $this->_total_update_errors++; |
|
| 939 | - EE_Error::add_error( |
|
| 940 | - sprintf( |
|
| 941 | - esc_html__( |
|
| 942 | - "Could not update %s with the csv data: '%s' for an unknown reason (using WHERE conditions %s)", |
|
| 943 | - "event_espresso" |
|
| 944 | - ), |
|
| 945 | - $model->get_this_model_name(), |
|
| 946 | - http_build_query($model_object_data), |
|
| 947 | - http_build_query($conditions) |
|
| 948 | - ), |
|
| 949 | - __FILE__, |
|
| 950 | - __FUNCTION__, |
|
| 951 | - __LINE__ |
|
| 952 | - ); |
|
| 953 | - } else { |
|
| 954 | - $this->_total_updates++; |
|
| 955 | - EE_Error::add_success( |
|
| 956 | - sprintf( |
|
| 957 | - esc_html__( |
|
| 958 | - "%s with csv data '%s' was found in the database and didn't need updating because all the data is identical.", |
|
| 959 | - "event_espresso" |
|
| 960 | - ), |
|
| 961 | - $model->get_this_model_name(), |
|
| 962 | - implode(",", $model_object_data) |
|
| 963 | - ) |
|
| 964 | - ); |
|
| 965 | - } |
|
| 966 | - } |
|
| 967 | - } catch (EE_Error $e) { |
|
| 968 | - $this->_total_update_errors++; |
|
| 969 | - $basic_message = sprintf( |
|
| 970 | - esc_html__("Could not update %s with the csv data: %s because %s", "event_espresso"), |
|
| 971 | - $model->get_this_model_name(), |
|
| 972 | - implode(",", $model_object_data), |
|
| 973 | - $e->getMessage() |
|
| 974 | - ); |
|
| 975 | - $debug_message = $basic_message . ' Stack trace: ' . $e->getTraceAsString(); |
|
| 976 | - EE_Error::add_error("$basic_message | $debug_message", __FILE__, __FUNCTION__, __LINE__); |
|
| 977 | - } |
|
| 978 | - return $old_db_to_new_db_mapping; |
|
| 979 | - } |
|
| 980 | - |
|
| 981 | - |
|
| 982 | - /** |
|
| 983 | - * Gets the number of inserts performed since importer was instantiated or reset |
|
| 984 | - * |
|
| 985 | - * @return int |
|
| 986 | - */ |
|
| 987 | - public function get_total_inserts(): int |
|
| 988 | - { |
|
| 989 | - return $this->_total_inserts; |
|
| 990 | - } |
|
| 991 | - |
|
| 992 | - |
|
| 993 | - /** |
|
| 994 | - * Gets the number of insert errors since importer was instantiated or reset |
|
| 995 | - * |
|
| 996 | - * @return int |
|
| 997 | - */ |
|
| 998 | - public function get_total_insert_errors(): int |
|
| 999 | - { |
|
| 1000 | - return $this->_total_insert_errors; |
|
| 1001 | - } |
|
| 1002 | - |
|
| 1003 | - |
|
| 1004 | - /** |
|
| 1005 | - * Gets the number of updates performed since importer was instantiated or reset |
|
| 1006 | - * |
|
| 1007 | - * @return int |
|
| 1008 | - */ |
|
| 1009 | - public function get_total_updates(): int |
|
| 1010 | - { |
|
| 1011 | - return $this->_total_updates; |
|
| 1012 | - } |
|
| 1013 | - |
|
| 1014 | - |
|
| 1015 | - /** |
|
| 1016 | - * Gets the number of update errors since importer was instantiated or reset |
|
| 1017 | - * |
|
| 1018 | - * @return int |
|
| 1019 | - */ |
|
| 1020 | - public function get_total_update_errors(): int |
|
| 1021 | - { |
|
| 1022 | - return $this->_total_update_errors; |
|
| 1023 | - } |
|
| 130 | + return ob_get_clean(); |
|
| 131 | + } |
|
| 132 | + |
|
| 133 | + |
|
| 134 | + /** |
|
| 135 | + * @Import Event Espresso data - some code "borrowed" from event espresso csv_import.php |
|
| 136 | + * @access public |
|
| 137 | + * @return boolean success |
|
| 138 | + * @throws EE_Error |
|
| 139 | + * @throws ReflectionException |
|
| 140 | + */ |
|
| 141 | + public function import(): bool |
|
| 142 | + { |
|
| 143 | + require_once(EE_CLASSES . 'EE_CSV.class.php'); |
|
| 144 | + $EE_CSV = EE_CSV::instance(); |
|
| 145 | + /** @var RequestInterface $request */ |
|
| 146 | + $request = LoaderFactory::getLoader()->getShared(RequestInterface::class); |
|
| 147 | + $files = $this->getFileParams($request); |
|
| 148 | + |
|
| 149 | + $filename = $files['file']['name'][0]; |
|
| 150 | + $file_ext = substr(strrchr($filename, '.'), 1); |
|
| 151 | + $temp_file = $files['file']['tmp_name'][0]; |
|
| 152 | + $filesize = $files['file']['size'][0] / 1024;// convert from bytes to KB |
|
| 153 | + |
|
| 154 | + if ($file_ext !== 'csv') { |
|
| 155 | + EE_Error::add_error( |
|
| 156 | + sprintf( |
|
| 157 | + esc_html__('%s had an invalid file extension, not uploaded', 'event_espresso'), |
|
| 158 | + $filename |
|
| 159 | + ), |
|
| 160 | + __FILE__, |
|
| 161 | + __FUNCTION__, |
|
| 162 | + __LINE__ |
|
| 163 | + ); |
|
| 164 | + return false; |
|
| 165 | + } |
|
| 166 | + |
|
| 167 | + $ignore_max_file_size = (bool) apply_filters('FHEE__EE_Import__import__ignore_max_file_size', true, $files); |
|
| 168 | + if (! $ignore_max_file_size) { |
|
| 169 | + // max upload size in KB |
|
| 170 | + $max_upload = $EE_CSV->get_max_upload_size(); |
|
| 171 | + if ($filesize > $max_upload) { |
|
| 172 | + EE_Error::add_error( |
|
| 173 | + sprintf( |
|
| 174 | + esc_html__( |
|
| 175 | + "%s was too large of a file and could not be uploaded. The max filesize is %s' KB.", |
|
| 176 | + 'event_espresso' |
|
| 177 | + ), |
|
| 178 | + $filename, |
|
| 179 | + $max_upload |
|
| 180 | + ), |
|
| 181 | + __FILE__, |
|
| 182 | + __FUNCTION__, |
|
| 183 | + __LINE__ |
|
| 184 | + ); |
|
| 185 | + return false; |
|
| 186 | + } |
|
| 187 | + } |
|
| 188 | + |
|
| 189 | + $wp_upload_dir = str_replace(['\\', '/'], '/', wp_upload_dir()); |
|
| 190 | + $path_to_file = $wp_upload_dir['basedir'] . '/espresso/' . $filename; |
|
| 191 | + |
|
| 192 | + if (! move_uploaded_file($temp_file, $path_to_file)) { |
|
| 193 | + EE_Error::add_error( |
|
| 194 | + sprintf( |
|
| 195 | + esc_html__('%s was not successfully uploaded', 'event_espresso'), |
|
| 196 | + $filename |
|
| 197 | + ), |
|
| 198 | + __FILE__, |
|
| 199 | + __FUNCTION__, |
|
| 200 | + __LINE__ |
|
| 201 | + ); |
|
| 202 | + return false; |
|
| 203 | + } |
|
| 204 | + |
|
| 205 | + // convert csv to array |
|
| 206 | + $csv_array = $EE_CSV->import_csv_to_model_data_array($path_to_file); |
|
| 207 | + |
|
| 208 | + // was data successfully stored in an array? |
|
| 209 | + if (! is_array($csv_array)) { |
|
| 210 | + // no array? must be an error |
|
| 211 | + EE_Error::add_error( |
|
| 212 | + esc_html__('No file seems to have been uploaded', 'event_espresso'), |
|
| 213 | + __FILE__, |
|
| 214 | + __FUNCTION__, |
|
| 215 | + __LINE__ |
|
| 216 | + ); |
|
| 217 | + return false; |
|
| 218 | + } |
|
| 219 | + |
|
| 220 | + // save processed codes to db |
|
| 221 | + if ($this->save_csv_data_array_to_db($csv_array)) { |
|
| 222 | + return true; |
|
| 223 | + } |
|
| 224 | + return false; |
|
| 225 | + } |
|
| 226 | + |
|
| 227 | + |
|
| 228 | + /** |
|
| 229 | + * @param RequestInterface $request |
|
| 230 | + * @return array|null |
|
| 231 | + * @since $VID:$ |
|
| 232 | + */ |
|
| 233 | + private function getFileParams(RequestInterface $request): ?array |
|
| 234 | + { |
|
| 235 | + $error = null; |
|
| 236 | + if (! ($request->requestParamIsSet('import') && $request->requestParamIsSet('csv_submitted'))) { |
|
| 237 | + $error = esc_html__( |
|
| 238 | + "Invalid request. Expected request params 'import' or 'csv_submitted' were missing.", |
|
| 239 | + 'event_espresso' |
|
| 240 | + ); |
|
| 241 | + } |
|
| 242 | + $files = $request->filesParams(); |
|
| 243 | + |
|
| 244 | + if (! $error) { |
|
| 245 | + switch ($files['file']['error'][0]) { |
|
| 246 | + case UPLOAD_ERR_OK: |
|
| 247 | + break; |
|
| 248 | + case UPLOAD_ERR_INI_SIZE: |
|
| 249 | + $error = esc_html__( |
|
| 250 | + "'The uploaded file exceeds the upload_max_filesize directive in php.ini.'", |
|
| 251 | + 'event_espresso' |
|
| 252 | + ); |
|
| 253 | + break; |
|
| 254 | + case UPLOAD_ERR_FORM_SIZE: |
|
| 255 | + $error = esc_html__( |
|
| 256 | + 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.', |
|
| 257 | + 'event_espresso' |
|
| 258 | + ); |
|
| 259 | + break; |
|
| 260 | + case UPLOAD_ERR_PARTIAL: |
|
| 261 | + $error = esc_html__('The uploaded file was only partially uploaded.', 'event_espresso'); |
|
| 262 | + break; |
|
| 263 | + case UPLOAD_ERR_NO_FILE: |
|
| 264 | + $error = esc_html__('No file was uploaded.', 'event_espresso'); |
|
| 265 | + break; |
|
| 266 | + case UPLOAD_ERR_NO_TMP_DIR: |
|
| 267 | + $error = esc_html__('Missing a temporary folder.', 'event_espresso'); |
|
| 268 | + break; |
|
| 269 | + case UPLOAD_ERR_CANT_WRITE: |
|
| 270 | + $error = esc_html__('Failed to write file to disk.', 'event_espresso'); |
|
| 271 | + break; |
|
| 272 | + case UPLOAD_ERR_EXTENSION: |
|
| 273 | + $error = esc_html__('File upload stopped by extension.', 'event_espresso'); |
|
| 274 | + break; |
|
| 275 | + default: |
|
| 276 | + $error = esc_html__( |
|
| 277 | + 'An unknown error occurred and the file could not be uploaded', |
|
| 278 | + 'event_espresso' |
|
| 279 | + ); |
|
| 280 | + } |
|
| 281 | + } |
|
| 282 | + |
|
| 283 | + if ($error) { |
|
| 284 | + EE_Error::add_error($error, __FILE__, __FUNCTION__, __LINE__); |
|
| 285 | + return null; |
|
| 286 | + } |
|
| 287 | + |
|
| 288 | + return $files; |
|
| 289 | + } |
|
| 290 | + |
|
| 291 | + |
|
| 292 | + /** |
|
| 293 | + * Given an array of data (usually from a CSV import) attempts to save that data to the db. |
|
| 294 | + * If $model_name ISN'T provided, assumes that this is a 3d array, with toplevel keys being model names, |
|
| 295 | + * next level being numeric indexes adn each value representing a model object, and the last layer down |
|
| 296 | + * being keys of model fields and their proposed values. |
|
| 297 | + * If $model_name IS provided, assumes a 2d array of the bottom two layers previously mentioned. |
|
| 298 | + * If the CSV data says (in the metadata row) that it's from the SAME database, |
|
| 299 | + * we treat the IDs in the CSV as the normal IDs, and try to update those records. However, if those |
|
| 300 | + * IDs DON'T exist in the database, they're treated as temporary IDs, |
|
| 301 | + * which can used elsewhere to refer to the same object. Once an item |
|
| 302 | + * with a temporary ID gets inserted, we record its mapping from temporary |
|
| 303 | + * ID to real ID, and use the real ID in place of the temporary ID |
|
| 304 | + * when that temporary ID was used as a foreign key. |
|
| 305 | + * If the CSV data says (in the metadata again) that it's from a DIFFERENT database, |
|
| 306 | + * we treat all the IDs in the CSV as temporary ID- eg, if the CSV specifies an event with |
|
| 307 | + * ID 1, and the database already has an event with ID 1, we assume that's just a coincidence, |
|
| 308 | + * and insert a new event, and map it's temporary ID of 1 over to its new real ID. |
|
| 309 | + * An important exception are non-auto-increment primary keys. If one entry in the |
|
| 310 | + * CSV file has the same ID as one in the DB, we assume they are meant to be |
|
| 311 | + * the same item, and instead update the item in the DB with that same ID. |
|
| 312 | + * Also note, we remember the mappings permanently. So the 2nd, 3rd, and 10000th |
|
| 313 | + * time you import a CSV from a different site, we remember their mappings, and |
|
| 314 | + * will try to update the item in the DB instead of inserting another item (eg |
|
| 315 | + * if we previously imported an event with temporary ID 1, and then it got a |
|
| 316 | + * real ID of 123, we remember that. So the next time we import an event with |
|
| 317 | + * temporary ID, from the same site, we know that it's real ID is 123, and will |
|
| 318 | + * update that event, instead of adding a new event). |
|
| 319 | + * |
|
| 320 | + * @param array $csv_data_array the array containing the csv data produced from |
|
| 321 | + * EE_CSV::import_csv_to_model_data_array() |
|
| 322 | + * @param string $model_name |
|
| 323 | + * @return bool TRUE on success, FALSE on fail |
|
| 324 | + * @throws EE_Error |
|
| 325 | + * @throws ReflectionException |
|
| 326 | + */ |
|
| 327 | + public function save_csv_data_array_to_db(array $csv_data_array, string $model_name = ''): bool |
|
| 328 | + { |
|
| 329 | + $success = false; |
|
| 330 | + // whether to treat this import as if it's data from a different database or not |
|
| 331 | + // ie, if it IS from a different database, ignore foreign keys with |
|
| 332 | + $export_from_site_a_to_b = true; |
|
| 333 | + // first level of array is not table information but a table name was passed to the function |
|
| 334 | + // array is only two levels deep, so let's fix that by adding a level, else the next steps will fail |
|
| 335 | + if ($model_name) { |
|
| 336 | + $csv_data_array = [$csv_data_array]; |
|
| 337 | + } |
|
| 338 | + // begin looking through the $csv_data_array, expecting the toplevel key to be the model's name... |
|
| 339 | + $old_site_url = 'none-specified'; |
|
| 340 | + // handle metadata |
|
| 341 | + if (isset($csv_data_array[ EE_CSV::metadata_header ])) { |
|
| 342 | + $csv_metadata = array_shift($csv_data_array[ EE_CSV::metadata_header ]); |
|
| 343 | + // ok so its metadata, dont try to save it to the db obviously... |
|
| 344 | + if (isset($csv_metadata['site_url']) && $csv_metadata['site_url'] == site_url()) { |
|
| 345 | + EE_Error::add_attention( |
|
| 346 | + esc_html__( |
|
| 347 | + 'CSV Data appears to be from the same database, so attempting to update data', |
|
| 348 | + 'event_espresso' |
|
| 349 | + ) |
|
| 350 | + ); |
|
| 351 | + $export_from_site_a_to_b = false; |
|
| 352 | + } else { |
|
| 353 | + $old_site_url = $csv_metadata['site_url'] ?? $old_site_url; |
|
| 354 | + EE_Error::add_attention( |
|
| 355 | + sprintf( |
|
| 356 | + esc_html__( |
|
| 357 | + "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", |
|
| 358 | + "event_espresso" |
|
| 359 | + ), |
|
| 360 | + $old_site_url, |
|
| 361 | + site_url() |
|
| 362 | + ) |
|
| 363 | + ); |
|
| 364 | + } |
|
| 365 | + unset($csv_data_array[ EE_CSV::metadata_header ]); |
|
| 366 | + } |
|
| 367 | + |
|
| 368 | + $id_mapping_option_name = 'ee_id_mapping_from' . sanitize_title($old_site_url); |
|
| 369 | + /** |
|
| 370 | + * @var $old_db_to_new_db_mapping 2d array: toplevel keys being model names, |
|
| 371 | + * bottom-level keys being the original key, |
|
| 372 | + * and the value will be the newly-inserted ID. |
|
| 373 | + * If we have already imported data from the same website via CSV, |
|
| 374 | + * it should be kept in this wp option |
|
| 375 | + */ |
|
| 376 | + $old_db_to_new_db_mapping = (array) get_option($id_mapping_option_name, []); |
|
| 377 | + |
|
| 378 | + if ($old_db_to_new_db_mapping) { |
|
| 379 | + EE_Error::add_attention( |
|
| 380 | + sprintf( |
|
| 381 | + esc_html__( |
|
| 382 | + "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", |
|
| 383 | + "event_espresso" |
|
| 384 | + ), |
|
| 385 | + $old_site_url, |
|
| 386 | + site_url() |
|
| 387 | + ) |
|
| 388 | + ); |
|
| 389 | + } |
|
| 390 | + $old_db_to_new_db_mapping = $this->save_data_rows_to_db( |
|
| 391 | + $csv_data_array, |
|
| 392 | + $export_from_site_a_to_b, |
|
| 393 | + $old_db_to_new_db_mapping |
|
| 394 | + ); |
|
| 395 | + |
|
| 396 | + // save the mapping from old db to new db in case they try re-importing the same data from the same website again |
|
| 397 | + update_option($id_mapping_option_name, $old_db_to_new_db_mapping); |
|
| 398 | + |
|
| 399 | + if ($this->_total_updates > 0) { |
|
| 400 | + EE_Error::add_success( |
|
| 401 | + sprintf( |
|
| 402 | + esc_html__("%s existing records in the database were updated.", "event_espresso"), |
|
| 403 | + $this->_total_updates |
|
| 404 | + ) |
|
| 405 | + ); |
|
| 406 | + $success = true; |
|
| 407 | + } |
|
| 408 | + if ($this->_total_inserts > 0) { |
|
| 409 | + EE_Error::add_success( |
|
| 410 | + sprintf( |
|
| 411 | + esc_html__("%s new records were added to the database.", "event_espresso"), |
|
| 412 | + $this->_total_inserts |
|
| 413 | + ) |
|
| 414 | + ); |
|
| 415 | + $success = true; |
|
| 416 | + } |
|
| 417 | + |
|
| 418 | + if ($this->_total_update_errors > 0) { |
|
| 419 | + EE_Error::add_error( |
|
| 420 | + sprintf( |
|
| 421 | + esc_html__( |
|
| 422 | + "'One or more errors occurred, and a total of %s existing records in the database were <strong>not</strong> updated.'", |
|
| 423 | + "event_espresso" |
|
| 424 | + ), |
|
| 425 | + $this->_total_update_errors |
|
| 426 | + ), |
|
| 427 | + __FILE__, |
|
| 428 | + __FUNCTION__, |
|
| 429 | + __LINE__ |
|
| 430 | + ); |
|
| 431 | + $success = false; |
|
| 432 | + } |
|
| 433 | + if ($this->_total_insert_errors > 0) { |
|
| 434 | + EE_Error::add_error( |
|
| 435 | + sprintf( |
|
| 436 | + esc_html__( |
|
| 437 | + "One or more errors occurred, and a total of %s new records were <strong>not</strong> added to the database.'", |
|
| 438 | + "event_espresso" |
|
| 439 | + ), |
|
| 440 | + $this->_total_insert_errors |
|
| 441 | + ), |
|
| 442 | + __FILE__, |
|
| 443 | + __FUNCTION__, |
|
| 444 | + __LINE__ |
|
| 445 | + ); |
|
| 446 | + $success = false; |
|
| 447 | + } |
|
| 448 | + |
|
| 449 | + // lastly, we need to update the datetime and ticket sold amounts |
|
| 450 | + // as those may have been affected by this |
|
| 451 | + EEM_Ticket::instance()->update_tickets_sold(EEM_Ticket::instance()->get_all()); |
|
| 452 | + |
|
| 453 | + // if there was at least one success and absolutely no errors |
|
| 454 | + return $success; |
|
| 455 | + } |
|
| 456 | + |
|
| 457 | + |
|
| 458 | + /** |
|
| 459 | + * Processes the array of data, given the knowledge that it's from the same database or a different one, |
|
| 460 | + * and the mapping from temporary IDs to real IDs. |
|
| 461 | + * If the data is from a different database, we treat the primary keys and their corresponding |
|
| 462 | + * foreign keys as "temp Ids", basically identifiers that get mapped to real primary keys |
|
| 463 | + * in the real target database. As items are inserted, their temporary primary keys |
|
| 464 | + * are mapped to the real IDs in the target database. Also, before doing any update or |
|
| 465 | + * insert, we replace all the temp ID which are foreign keys with their mapped real IDs. |
|
| 466 | + * An exception: string primary keys are treated as real IDs, or else we'd need to |
|
| 467 | + * dynamically generate new string primary keys which would be very awkward for the country table etc. |
|
| 468 | + * Also, models with no primary key are strange too. We combine use their primary key INDEX (a |
|
| 469 | + * combination of fields) to create a unique string identifying the row and store |
|
| 470 | + * those in the mapping. |
|
| 471 | + * |
|
| 472 | + * If the data is from the same database, we usually treat primary keys as real IDs. |
|
| 473 | + * An exception is if there is nothing in the database for that ID. If that's the case, |
|
| 474 | + * we need to insert a new row for that ID, and then map from the non-existent ID |
|
| 475 | + * to the newly-inserted real ID. |
|
| 476 | + * |
|
| 477 | + * @param array $csv_data_array |
|
| 478 | + * @param bool $export_from_site_a_to_b |
|
| 479 | + * @param array $old_db_to_new_db_mapping |
|
| 480 | + * @return array|bool updated $old_db_to_new_db_mapping |
|
| 481 | + * @throws EE_Error |
|
| 482 | + * @throws ReflectionException |
|
| 483 | + */ |
|
| 484 | + public function save_data_rows_to_db( |
|
| 485 | + array $csv_data_array, |
|
| 486 | + bool $export_from_site_a_to_b, |
|
| 487 | + array $old_db_to_new_db_mapping |
|
| 488 | + ) { |
|
| 489 | + foreach ($csv_data_array as $model_name => $model_data_from_import) { |
|
| 490 | + // check that assumption was correct. If |
|
| 491 | + if (! EE_Registry::instance()->is_model_name($model_name)) { |
|
| 492 | + // no table info in the array and no table name passed to the function?? FAIL |
|
| 493 | + EE_Error::add_error( |
|
| 494 | + esc_html__( |
|
| 495 | + 'No table information was specified and/or found, therefore the import could not be completed', |
|
| 496 | + 'event_espresso' |
|
| 497 | + ), |
|
| 498 | + __FILE__, |
|
| 499 | + __FUNCTION__, |
|
| 500 | + __LINE__ |
|
| 501 | + ); |
|
| 502 | + return false; |
|
| 503 | + } |
|
| 504 | + |
|
| 505 | + /* @var $model EEM_Base */ |
|
| 506 | + $model = EE_Registry::instance()->load_model($model_name); |
|
| 507 | + |
|
| 508 | + // so without further ado, scanning all the data provided for primary keys and their initial values |
|
| 509 | + foreach ($model_data_from_import as $model_object_data) { |
|
| 510 | + // before we do ANYTHING, make sure the csv row wasn't just completely blank |
|
| 511 | + $row_is_completely_empty = true; |
|
| 512 | + foreach ($model_object_data as $field) { |
|
| 513 | + if ($field) { |
|
| 514 | + $row_is_completely_empty = false; |
|
| 515 | + } |
|
| 516 | + } |
|
| 517 | + if ($row_is_completely_empty) { |
|
| 518 | + continue; |
|
| 519 | + } |
|
| 520 | + // find the PK in the row of data (or a combined key if |
|
| 521 | + // there is no primary key) |
|
| 522 | + if ($model->has_primary_key_field()) { |
|
| 523 | + $id_in_csv = $model_object_data[ $model->primary_key_name() ]; |
|
| 524 | + } else { |
|
| 525 | + $id_in_csv = $model->get_index_primary_key_string($model_object_data); |
|
| 526 | + } |
|
| 527 | + |
|
| 528 | + $model_object_data = $this->_replace_temp_ids_with_mappings( |
|
| 529 | + $model_object_data, |
|
| 530 | + $model, |
|
| 531 | + $old_db_to_new_db_mapping, |
|
| 532 | + $export_from_site_a_to_b |
|
| 533 | + ); |
|
| 534 | + // now we need to decide if we're going to |
|
| 535 | + $what_to_do = $export_from_site_a_to_b |
|
| 536 | + // add a new model object given the $model_object_data |
|
| 537 | + ? $this->_decide_whether_to_insert_or_update_given_data_from_other_db( |
|
| 538 | + $id_in_csv, |
|
| 539 | + $model_name, |
|
| 540 | + $old_db_to_new_db_mapping |
|
| 541 | + ) |
|
| 542 | + // or just update cuz this is just a re-import |
|
| 543 | + : $this->_decide_whether_to_insert_or_update_given_data_from_same_db( |
|
| 544 | + $model_object_data, |
|
| 545 | + $model |
|
| 546 | + ); |
|
| 547 | + |
|
| 548 | + if ($what_to_do === EE_Import::do_nothing) { |
|
| 549 | + continue; |
|
| 550 | + } |
|
| 551 | + |
|
| 552 | + // double-check we actually want to insert, if that's what we're planning |
|
| 553 | + // based on whether this item would be unique in the DB or not |
|
| 554 | + if ($what_to_do == EE_Import::do_insert) { |
|
| 555 | + // we're supposed to be inserting. But wait, will this thing |
|
| 556 | + // be acceptable if inserted? |
|
| 557 | + $conflicting = $model->get_one_conflicting($model_object_data, false); |
|
| 558 | + if ($conflicting) { |
|
| 559 | + // ok, this item would conflict if inserted. Just update the item that it conflicts with. |
|
| 560 | + $what_to_do = EE_Import::do_update; |
|
| 561 | + // and if this model has a primary key, remember its mapping |
|
| 562 | + if ($model->has_primary_key_field()) { |
|
| 563 | + $old_db_to_new_db_mapping[ $model_name ][ $id_in_csv ] = $conflicting->ID(); |
|
| 564 | + $model_object_data[ $model->primary_key_name() ] = $conflicting->ID(); |
|
| 565 | + } else { |
|
| 566 | + // we want to update this conflicting item, instead of inserting a conflicting item |
|
| 567 | + // so we need to make sure they match entirely |
|
| 568 | + // (it's possible that they only conflicted on one field, |
|
| 569 | + // but we need them to match on other fields for the WHERE conditions in the update). |
|
| 570 | + // At the time of this comment, there were no models like this |
|
| 571 | + foreach ($model->get_combined_primary_key_fields() as $key_field) { |
|
| 572 | + $model_object_data[ $key_field->get_name() ] = $conflicting->get( |
|
| 573 | + $key_field->get_name() |
|
| 574 | + ); |
|
| 575 | + } |
|
| 576 | + } |
|
| 577 | + } |
|
| 578 | + } |
|
| 579 | + if ($what_to_do == EE_Import::do_insert) { |
|
| 580 | + $old_db_to_new_db_mapping = $this->_insert_from_data_array( |
|
| 581 | + $id_in_csv, |
|
| 582 | + $model_object_data, |
|
| 583 | + $model, |
|
| 584 | + $old_db_to_new_db_mapping |
|
| 585 | + ); |
|
| 586 | + } elseif ($what_to_do == EE_Import::do_update) { |
|
| 587 | + $old_db_to_new_db_mapping = $this->_update_from_data_array( |
|
| 588 | + $id_in_csv, |
|
| 589 | + $model_object_data, |
|
| 590 | + $model, |
|
| 591 | + $old_db_to_new_db_mapping |
|
| 592 | + ); |
|
| 593 | + } else { |
|
| 594 | + throw new EE_Error( |
|
| 595 | + sprintf( |
|
| 596 | + esc_html__( |
|
| 597 | + 'Programming error. We should be inserting or updating, but instead we are being told to "%s", whifh is invalid', |
|
| 598 | + 'event_espresso' |
|
| 599 | + ), |
|
| 600 | + $what_to_do |
|
| 601 | + ) |
|
| 602 | + ); |
|
| 603 | + } |
|
| 604 | + } |
|
| 605 | + } |
|
| 606 | + return $old_db_to_new_db_mapping; |
|
| 607 | + } |
|
| 608 | + |
|
| 609 | + |
|
| 610 | + /** |
|
| 611 | + * Decides whether or not to insert, given that this data is from another database. |
|
| 612 | + * So, if the primary key of this $model_object_data already exists in the database, |
|
| 613 | + * it's just a coincidence and we should still insert. The only time we should |
|
| 614 | + * update is when we know what it maps to, or there's something that would |
|
| 615 | + * conflict (and we should instead just update that conflicting thing) |
|
| 616 | + * |
|
| 617 | + * @param string $id_in_csv |
|
| 618 | + * @param string $model_name |
|
| 619 | + * @param array $old_db_to_new_db_mapping by reference so it can be modified |
|
| 620 | + * @return string one of the constants on this class that starts with do_* |
|
| 621 | + */ |
|
| 622 | + protected function _decide_whether_to_insert_or_update_given_data_from_other_db( |
|
| 623 | + string $id_in_csv, |
|
| 624 | + string $model_name, |
|
| 625 | + array $old_db_to_new_db_mapping |
|
| 626 | + ): string { |
|
| 627 | + // if it's a site-to-site export-and-import, see if this model object's id |
|
| 628 | + // in the old data that we know of |
|
| 629 | + if (isset($old_db_to_new_db_mapping[ $model_name ][ $id_in_csv ])) { |
|
| 630 | + return EE_Import::do_update; |
|
| 631 | + } |
|
| 632 | + return EE_Import::do_insert; |
|
| 633 | + } |
|
| 634 | + |
|
| 635 | + |
|
| 636 | + /** |
|
| 637 | + * If this thing basically already exists in the database, we want to update it; |
|
| 638 | + * otherwise insert it (ie, someone tweaked the CSV file, or the item was |
|
| 639 | + * deleted in the database so it should be re-inserted) |
|
| 640 | + * |
|
| 641 | + * @param array $model_object_data |
|
| 642 | + * @param EEM_Base $model |
|
| 643 | + * @return string |
|
| 644 | + * @throws EE_Error |
|
| 645 | + */ |
|
| 646 | + protected function _decide_whether_to_insert_or_update_given_data_from_same_db( |
|
| 647 | + array $model_object_data, |
|
| 648 | + EEM_Base $model |
|
| 649 | + ): string { |
|
| 650 | + // in this case, check if this thing ACTUALLY exists in the database |
|
| 651 | + if ($model->get_one_conflicting($model_object_data)) { |
|
| 652 | + return EE_Import::do_update; |
|
| 653 | + } |
|
| 654 | + return EE_Import::do_insert; |
|
| 655 | + } |
|
| 656 | + |
|
| 657 | + |
|
| 658 | + /** |
|
| 659 | + * Using the $old_db_to_new_db_mapping array, replaces all the temporary IDs |
|
| 660 | + * with their mapped real IDs. Eg, if importing from site A to B, the mapping |
|
| 661 | + * file may indicate that the ID "my_event_id" maps to an actual event ID of 123. |
|
| 662 | + * So this function searches for any event temp Ids called "my_event_id" and |
|
| 663 | + * replaces them with 123. |
|
| 664 | + * Also, if there is no temp ID for the INT foreign keys from another database, |
|
| 665 | + * replaces them with 0 or the field's default. |
|
| 666 | + * |
|
| 667 | + * @param array $model_object_data |
|
| 668 | + * @param EEM_Base $model |
|
| 669 | + * @param array $old_db_to_new_db_mapping |
|
| 670 | + * @param bool $export_from_site_a_to_b |
|
| 671 | + * @return array updated model object data with temp IDs removed |
|
| 672 | + * @throws EE_Error |
|
| 673 | + */ |
|
| 674 | + protected function _replace_temp_ids_with_mappings( |
|
| 675 | + array $model_object_data, |
|
| 676 | + EEM_Base $model, |
|
| 677 | + array $old_db_to_new_db_mapping, |
|
| 678 | + bool $export_from_site_a_to_b |
|
| 679 | + ): array { |
|
| 680 | + // if this model object's primary key is in the mapping, replace it |
|
| 681 | + if ( |
|
| 682 | + $model->has_primary_key_field() |
|
| 683 | + && $model->get_primary_key_field()->is_auto_increment() |
|
| 684 | + && isset($old_db_to_new_db_mapping[ $model->get_this_model_name() ]) |
|
| 685 | + && isset( |
|
| 686 | + $old_db_to_new_db_mapping[ $model->get_this_model_name( |
|
| 687 | + ) ][ $model_object_data[ $model->primary_key_name() ] ] |
|
| 688 | + ) |
|
| 689 | + ) { |
|
| 690 | + $model_object_data[ $model->primary_key_name() ] = $old_db_to_new_db_mapping[ $model->get_this_model_name( |
|
| 691 | + ) ][ $model_object_data[ $model->primary_key_name() ] ]; |
|
| 692 | + } |
|
| 693 | + |
|
| 694 | + try { |
|
| 695 | + $model_name_field = $model->get_field_containing_related_model_name(); |
|
| 696 | + } catch (EE_Error $e) { |
|
| 697 | + $model_name_field = null; |
|
| 698 | + } |
|
| 699 | + |
|
| 700 | + foreach ($model->field_settings(true) as $field_obj) { |
|
| 701 | + if (! $field_obj instanceof EE_Foreign_Key_Int_Field) { |
|
| 702 | + // not a foreign key, or it's a string foreign key |
|
| 703 | + // which we leave alone, because those are things like country names, |
|
| 704 | + // which we'd really rather not make 2 USAs etc (we'd actually prefer to just update one) |
|
| 705 | + // or it's just a regular value that ought to be replaced |
|
| 706 | + continue; |
|
| 707 | + } |
|
| 708 | + $models_pointed_to = $field_obj->get_model_names_pointed_to(); |
|
| 709 | + foreach ($models_pointed_to as $model_pointed_to_by_fk) { |
|
| 710 | + if ($model_name_field) { |
|
| 711 | + $value_of_model_name_field = $model_object_data[ $model_name_field->get_name() ]; |
|
| 712 | + if ($value_of_model_name_field == $model_pointed_to_by_fk) { |
|
| 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 | + // once we've found a mapping for this field no need to continue |
|
| 720 | + // break; |
|
| 721 | + } |
|
| 722 | + } else { |
|
| 723 | + $model_object_data[ $field_obj->get_name() ] = $this->_find_mapping_in( |
|
| 724 | + $model_object_data[ $field_obj->get_name() ], |
|
| 725 | + $model_pointed_to_by_fk, |
|
| 726 | + $old_db_to_new_db_mapping, |
|
| 727 | + $export_from_site_a_to_b |
|
| 728 | + ); |
|
| 729 | + // once we've found a mapping for this field no need to continue |
|
| 730 | + // break; |
|
| 731 | + } |
|
| 732 | + } |
|
| 733 | + } |
|
| 734 | + |
|
| 735 | + if ($model instanceof EEM_Term_Taxonomy) { |
|
| 736 | + $model_object_data = $this->_handle_split_term_ids($model_object_data); |
|
| 737 | + } |
|
| 738 | + return $model_object_data; |
|
| 739 | + } |
|
| 740 | + |
|
| 741 | + |
|
| 742 | + /** |
|
| 743 | + * If the data was exported PRE-4.2, but then imported POST-4.2, then the term_id |
|
| 744 | + * this term-taxonomy refers to may be out-of-date so we need to update it. |
|
| 745 | + * see https://make.wordpress.org/core/2015/02/16/taxonomy-term-splitting-in-4-2-a-developer-guide/ |
|
| 746 | + * |
|
| 747 | + * @param array $model_object_data |
|
| 748 | + * @return array new model object data |
|
| 749 | + */ |
|
| 750 | + protected function _handle_split_term_ids(array $model_object_data): array |
|
| 751 | + { |
|
| 752 | + if ( |
|
| 753 | + isset($model_object_data['term_id']) |
|
| 754 | + && isset($model_object_data['taxonomy']) |
|
| 755 | + && apply_filters( |
|
| 756 | + 'FHEE__EE_Import__handle_split_term_ids__function_exists', |
|
| 757 | + function_exists('wp_get_split_term'), |
|
| 758 | + $model_object_data |
|
| 759 | + ) |
|
| 760 | + ) { |
|
| 761 | + $new_term_id = wp_get_split_term($model_object_data['term_id'], $model_object_data['taxonomy']); |
|
| 762 | + if ($new_term_id) { |
|
| 763 | + $model_object_data['term_id'] = $new_term_id; |
|
| 764 | + } |
|
| 765 | + } |
|
| 766 | + return $model_object_data; |
|
| 767 | + } |
|
| 768 | + |
|
| 769 | + |
|
| 770 | + /** |
|
| 771 | + * Given the object's ID and its model's name, find it int he mapping data, |
|
| 772 | + * bearing in mind where it came from |
|
| 773 | + * |
|
| 774 | + * @param int|string $object_id |
|
| 775 | + * @param string $model_name |
|
| 776 | + * @param array $old_db_to_new_db_mapping |
|
| 777 | + * @param bool $export_from_site_a_to_b |
|
| 778 | + * @return int |
|
| 779 | + */ |
|
| 780 | + protected function _find_mapping_in( |
|
| 781 | + $object_id, |
|
| 782 | + string $model_name, |
|
| 783 | + array $old_db_to_new_db_mapping, |
|
| 784 | + bool $export_from_site_a_to_b |
|
| 785 | + ) { |
|
| 786 | + if (isset($old_db_to_new_db_mapping[ $model_name ][ $object_id ])) { |
|
| 787 | + return $old_db_to_new_db_mapping[ $model_name ][ $object_id ]; |
|
| 788 | + } |
|
| 789 | + if ($object_id == '0' || $object_id == '') { |
|
| 790 | + // leave as-is |
|
| 791 | + return $object_id; |
|
| 792 | + } |
|
| 793 | + if ($export_from_site_a_to_b) { |
|
| 794 | + // we couldn't find a mapping for this, and it's from a different site, |
|
| 795 | + // so blank it out |
|
| 796 | + return null; |
|
| 797 | + } |
|
| 798 | + // we couldn't find a mapping for this, but it's from this DB anyway |
|
| 799 | + // so let's just leave it as-is |
|
| 800 | + return $object_id; |
|
| 801 | + } |
|
| 802 | + |
|
| 803 | + |
|
| 804 | + /** |
|
| 805 | + * |
|
| 806 | + * @param int|string $id_in_csv |
|
| 807 | + * @param array $model_object_data |
|
| 808 | + * @param EEM_Base $model |
|
| 809 | + * @param array $old_db_to_new_db_mapping |
|
| 810 | + * @return array updated $old_db_to_new_db_mapping |
|
| 811 | + * @throws EE_Error |
|
| 812 | + */ |
|
| 813 | + protected function _insert_from_data_array( |
|
| 814 | + $id_in_csv, |
|
| 815 | + array $model_object_data, |
|
| 816 | + EEM_Base $model, |
|
| 817 | + array $old_db_to_new_db_mapping |
|
| 818 | + ): array { |
|
| 819 | + // remove the primary key, if there is one (we don't want it for inserts OR updates) |
|
| 820 | + // we'll put it back in if we need it |
|
| 821 | + if ($model->has_primary_key_field() && $model->get_primary_key_field()->is_auto_increment()) { |
|
| 822 | + $effective_id = $model_object_data[ $model->primary_key_name() ]; |
|
| 823 | + unset($model_object_data[ $model->primary_key_name() ]); |
|
| 824 | + } else { |
|
| 825 | + $effective_id = $model->get_index_primary_key_string($model_object_data); |
|
| 826 | + } |
|
| 827 | + // the model takes care of validating the CSV's input |
|
| 828 | + try { |
|
| 829 | + $new_id = $model->insert($model_object_data); |
|
| 830 | + if ($new_id) { |
|
| 831 | + $old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $id_in_csv ] = $new_id; |
|
| 832 | + $this->_total_inserts++; |
|
| 833 | + EE_Error::add_success( |
|
| 834 | + sprintf( |
|
| 835 | + esc_html__("Successfully added new %s (with id %s) with csv data %s", "event_espresso"), |
|
| 836 | + $model->get_this_model_name(), |
|
| 837 | + $new_id, |
|
| 838 | + implode(",", $model_object_data) |
|
| 839 | + ) |
|
| 840 | + ); |
|
| 841 | + } else { |
|
| 842 | + $this->_total_insert_errors++; |
|
| 843 | + // put the ID used back in there for the error message |
|
| 844 | + if ($model->has_primary_key_field()) { |
|
| 845 | + $model_object_data[ $model->primary_key_name() ] = $effective_id; |
|
| 846 | + } |
|
| 847 | + EE_Error::add_error( |
|
| 848 | + sprintf( |
|
| 849 | + esc_html__("Could not insert new %s with the csv data: %s", "event_espresso"), |
|
| 850 | + $model->get_this_model_name(), |
|
| 851 | + http_build_query($model_object_data) |
|
| 852 | + ), |
|
| 853 | + __FILE__, |
|
| 854 | + __FUNCTION__, |
|
| 855 | + __LINE__ |
|
| 856 | + ); |
|
| 857 | + } |
|
| 858 | + } catch (EE_Error $e) { |
|
| 859 | + $this->_total_insert_errors++; |
|
| 860 | + if ($model->has_primary_key_field()) { |
|
| 861 | + $model_object_data[ $model->primary_key_name() ] = $effective_id; |
|
| 862 | + } |
|
| 863 | + EE_Error::add_error( |
|
| 864 | + sprintf( |
|
| 865 | + esc_html__("Could not insert new %s with the csv data: %s because %s", "event_espresso"), |
|
| 866 | + $model->get_this_model_name(), |
|
| 867 | + implode(",", $model_object_data), |
|
| 868 | + $e->getMessage() |
|
| 869 | + ), |
|
| 870 | + __FILE__, |
|
| 871 | + __FUNCTION__, |
|
| 872 | + __LINE__ |
|
| 873 | + ); |
|
| 874 | + } |
|
| 875 | + return $old_db_to_new_db_mapping; |
|
| 876 | + } |
|
| 877 | + |
|
| 878 | + |
|
| 879 | + /** |
|
| 880 | + * Given the model object data, finds the row to update and updates it |
|
| 881 | + * |
|
| 882 | + * @param string|int $id_in_csv |
|
| 883 | + * @param array $model_object_data |
|
| 884 | + * @param EEM_Base $model |
|
| 885 | + * @param array $old_db_to_new_db_mapping |
|
| 886 | + * @return array updated $old_db_to_new_db_mapping |
|
| 887 | + */ |
|
| 888 | + protected function _update_from_data_array( |
|
| 889 | + $id_in_csv, |
|
| 890 | + array $model_object_data, |
|
| 891 | + EEM_Base $model, |
|
| 892 | + array $old_db_to_new_db_mapping |
|
| 893 | + ): array { |
|
| 894 | + $conditions = null; |
|
| 895 | + try { |
|
| 896 | + // let's keep two copies of the model object data: |
|
| 897 | + // one for performing an update, one for everything else |
|
| 898 | + $model_object_data_for_update = $model_object_data; |
|
| 899 | + if ($model->has_primary_key_field()) { |
|
| 900 | + $conditions = [$model->primary_key_name() => $model_object_data[ $model->primary_key_name() ]]; |
|
| 901 | + // remove the primary key because we shouldn't use it for updating |
|
| 902 | + unset($model_object_data_for_update[ $model->primary_key_name() ]); |
|
| 903 | + } elseif ($model->get_combined_primary_key_fields() > 1) { |
|
| 904 | + $conditions = []; |
|
| 905 | + foreach ($model->get_combined_primary_key_fields() as $key_field) { |
|
| 906 | + $conditions[ $key_field->get_name() ] = $model_object_data[ $key_field->get_name() ]; |
|
| 907 | + } |
|
| 908 | + } else { |
|
| 909 | + // this should just throw an exception, explaining that we dont have a primary key (or a combined key) |
|
| 910 | + $model->primary_key_name(); |
|
| 911 | + } |
|
| 912 | + |
|
| 913 | + $success = $model->update($model_object_data_for_update, [$conditions]); |
|
| 914 | + if ($success) { |
|
| 915 | + $this->_total_updates++; |
|
| 916 | + EE_Error::add_success( |
|
| 917 | + sprintf( |
|
| 918 | + esc_html__("Successfully updated %s with csv data %s", "event_espresso"), |
|
| 919 | + $model->get_this_model_name(), |
|
| 920 | + implode(",", $model_object_data_for_update) |
|
| 921 | + ) |
|
| 922 | + ); |
|
| 923 | + // we should still record the mapping even though it was an update |
|
| 924 | + // because if we were going to insert something but it was going to conflict |
|
| 925 | + // we would have last-minute decided to update. So we'd like to know what we updated |
|
| 926 | + // and so we record what record ended up being updated using the mapping |
|
| 927 | + if ($model->has_primary_key_field()) { |
|
| 928 | + $new_key_for_mapping = $model_object_data[ $model->primary_key_name() ]; |
|
| 929 | + } else { |
|
| 930 | + // no primary key just a combined key |
|
| 931 | + $new_key_for_mapping = $model->get_index_primary_key_string($model_object_data); |
|
| 932 | + } |
|
| 933 | + $old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $id_in_csv ] = $new_key_for_mapping; |
|
| 934 | + } else { |
|
| 935 | + $matched_items = $model->get_all([$conditions]); |
|
| 936 | + if (! $matched_items) { |
|
| 937 | + // no items were matched (so we shouldn't have updated)... but then we should have inserted? what the heck? |
|
| 938 | + $this->_total_update_errors++; |
|
| 939 | + EE_Error::add_error( |
|
| 940 | + sprintf( |
|
| 941 | + esc_html__( |
|
| 942 | + "Could not update %s with the csv data: '%s' for an unknown reason (using WHERE conditions %s)", |
|
| 943 | + "event_espresso" |
|
| 944 | + ), |
|
| 945 | + $model->get_this_model_name(), |
|
| 946 | + http_build_query($model_object_data), |
|
| 947 | + http_build_query($conditions) |
|
| 948 | + ), |
|
| 949 | + __FILE__, |
|
| 950 | + __FUNCTION__, |
|
| 951 | + __LINE__ |
|
| 952 | + ); |
|
| 953 | + } else { |
|
| 954 | + $this->_total_updates++; |
|
| 955 | + EE_Error::add_success( |
|
| 956 | + sprintf( |
|
| 957 | + esc_html__( |
|
| 958 | + "%s with csv data '%s' was found in the database and didn't need updating because all the data is identical.", |
|
| 959 | + "event_espresso" |
|
| 960 | + ), |
|
| 961 | + $model->get_this_model_name(), |
|
| 962 | + implode(",", $model_object_data) |
|
| 963 | + ) |
|
| 964 | + ); |
|
| 965 | + } |
|
| 966 | + } |
|
| 967 | + } catch (EE_Error $e) { |
|
| 968 | + $this->_total_update_errors++; |
|
| 969 | + $basic_message = sprintf( |
|
| 970 | + esc_html__("Could not update %s with the csv data: %s because %s", "event_espresso"), |
|
| 971 | + $model->get_this_model_name(), |
|
| 972 | + implode(",", $model_object_data), |
|
| 973 | + $e->getMessage() |
|
| 974 | + ); |
|
| 975 | + $debug_message = $basic_message . ' Stack trace: ' . $e->getTraceAsString(); |
|
| 976 | + EE_Error::add_error("$basic_message | $debug_message", __FILE__, __FUNCTION__, __LINE__); |
|
| 977 | + } |
|
| 978 | + return $old_db_to_new_db_mapping; |
|
| 979 | + } |
|
| 980 | + |
|
| 981 | + |
|
| 982 | + /** |
|
| 983 | + * Gets the number of inserts performed since importer was instantiated or reset |
|
| 984 | + * |
|
| 985 | + * @return int |
|
| 986 | + */ |
|
| 987 | + public function get_total_inserts(): int |
|
| 988 | + { |
|
| 989 | + return $this->_total_inserts; |
|
| 990 | + } |
|
| 991 | + |
|
| 992 | + |
|
| 993 | + /** |
|
| 994 | + * Gets the number of insert errors since importer was instantiated or reset |
|
| 995 | + * |
|
| 996 | + * @return int |
|
| 997 | + */ |
|
| 998 | + public function get_total_insert_errors(): int |
|
| 999 | + { |
|
| 1000 | + return $this->_total_insert_errors; |
|
| 1001 | + } |
|
| 1002 | + |
|
| 1003 | + |
|
| 1004 | + /** |
|
| 1005 | + * Gets the number of updates performed since importer was instantiated or reset |
|
| 1006 | + * |
|
| 1007 | + * @return int |
|
| 1008 | + */ |
|
| 1009 | + public function get_total_updates(): int |
|
| 1010 | + { |
|
| 1011 | + return $this->_total_updates; |
|
| 1012 | + } |
|
| 1013 | + |
|
| 1014 | + |
|
| 1015 | + /** |
|
| 1016 | + * Gets the number of update errors since importer was instantiated or reset |
|
| 1017 | + * |
|
| 1018 | + * @return int |
|
| 1019 | + */ |
|
| 1020 | + public function get_total_update_errors(): int |
|
| 1021 | + { |
|
| 1022 | + return $this->_total_update_errors; |
|
| 1023 | + } |
|
| 1024 | 1024 | } |
@@ -9,346 +9,346 @@ |
||
| 9 | 9 | */ |
| 10 | 10 | class EE_Price extends EE_Soft_Delete_Base_Class |
| 11 | 11 | { |
| 12 | - /** |
|
| 13 | - * @param array $props_n_values incoming values |
|
| 14 | - * @param string $timezone incoming timezone (if not set the timezone set for the website will be used.) |
|
| 15 | - * @param array $date_formats incoming date_formats in an array where the first value is the |
|
| 16 | - * date_format and the second value is the time format |
|
| 17 | - * @return EE_Price |
|
| 18 | - * @throws EE_Error|ReflectionException |
|
| 19 | - */ |
|
| 20 | - public static function new_instance($props_n_values = [], $timezone = null, $date_formats = []) |
|
| 21 | - { |
|
| 22 | - $price = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats); |
|
| 23 | - if (! $price instanceof EE_Price) { |
|
| 24 | - $price = new EE_Price($props_n_values, false, $timezone, $date_formats); |
|
| 25 | - } |
|
| 26 | - return $price; |
|
| 27 | - } |
|
| 28 | - |
|
| 29 | - |
|
| 30 | - /** |
|
| 31 | - * @param array $props_n_values incoming values from the database |
|
| 32 | - * @param string $timezone incoming timezone as set by the model. If not set the timezone for |
|
| 33 | - * the website will be used. |
|
| 34 | - * @return EE_Price |
|
| 35 | - * @throws EE_Error|ReflectionException |
|
| 36 | - */ |
|
| 37 | - public static function new_instance_from_db($props_n_values = [], $timezone = null) |
|
| 38 | - { |
|
| 39 | - return new self($props_n_values, true, $timezone); |
|
| 40 | - } |
|
| 41 | - |
|
| 42 | - |
|
| 43 | - /** |
|
| 44 | - * Adds some defaults if they're not specified |
|
| 45 | - * |
|
| 46 | - * @param array $props_n_values |
|
| 47 | - * @param bool $bydb |
|
| 48 | - * @param string $timezone |
|
| 49 | - * @param array $date_formats incoming date_formats in an array where the first value is the |
|
| 50 | - * date_format and the second value is the time format |
|
| 51 | - * @throws EE_Error |
|
| 52 | - * @throws ReflectionException |
|
| 53 | - */ |
|
| 54 | - protected function __construct($props_n_values = [], $bydb = false, $timezone = '', $date_formats = []) |
|
| 55 | - { |
|
| 56 | - parent::__construct($props_n_values, $bydb, $timezone, $date_formats); |
|
| 57 | - } |
|
| 58 | - |
|
| 59 | - |
|
| 60 | - /** |
|
| 61 | - * Set Price type ID |
|
| 62 | - * |
|
| 63 | - * @param int $PRT_ID |
|
| 64 | - * @throws EE_Error |
|
| 65 | - * @throws ReflectionException |
|
| 66 | - */ |
|
| 67 | - public function set_type($PRT_ID = 0) |
|
| 68 | - { |
|
| 69 | - $this->set('PRT_ID', $PRT_ID); |
|
| 70 | - } |
|
| 71 | - |
|
| 72 | - |
|
| 73 | - /** |
|
| 74 | - * Set Price Amount |
|
| 75 | - * |
|
| 76 | - * @param float $amount |
|
| 77 | - * @throws EE_Error |
|
| 78 | - * @throws ReflectionException |
|
| 79 | - */ |
|
| 80 | - public function set_amount($amount = 0.00) |
|
| 81 | - { |
|
| 82 | - $this->set('PRC_amount', $amount); |
|
| 83 | - } |
|
| 84 | - |
|
| 85 | - |
|
| 86 | - /** |
|
| 87 | - * Set Price Name |
|
| 88 | - * |
|
| 89 | - * @param string $PRC_name |
|
| 90 | - * @throws EE_Error |
|
| 91 | - * @throws ReflectionException |
|
| 92 | - */ |
|
| 93 | - public function set_name($PRC_name = '') |
|
| 94 | - { |
|
| 95 | - $this->set('PRC_name', $PRC_name); |
|
| 96 | - } |
|
| 97 | - |
|
| 98 | - |
|
| 99 | - /** |
|
| 100 | - * Set Price Description |
|
| 101 | - * |
|
| 102 | - * @param string $PRC_desc |
|
| 103 | - * @throws EE_Error |
|
| 104 | - * @throws ReflectionException |
|
| 105 | - */ |
|
| 106 | - public function set_description($PRC_desc = '') |
|
| 107 | - { |
|
| 108 | - $this->Set('PRC_desc', $PRC_desc); |
|
| 109 | - } |
|
| 110 | - |
|
| 111 | - |
|
| 112 | - /** |
|
| 113 | - * set is_default |
|
| 114 | - * |
|
| 115 | - * @param bool $PRC_is_default |
|
| 116 | - * @throws EE_Error |
|
| 117 | - * @throws ReflectionException |
|
| 118 | - */ |
|
| 119 | - public function set_is_default($PRC_is_default = false) |
|
| 120 | - { |
|
| 121 | - $this->set('PRC_is_default', $PRC_is_default); |
|
| 122 | - } |
|
| 123 | - |
|
| 124 | - |
|
| 125 | - /** |
|
| 126 | - * set deleted |
|
| 127 | - * |
|
| 128 | - * @param bool $PRC_deleted |
|
| 129 | - * @throws EE_Error |
|
| 130 | - * @throws ReflectionException |
|
| 131 | - */ |
|
| 132 | - public function set_deleted($PRC_deleted = null) |
|
| 133 | - { |
|
| 134 | - $this->set('PRC_deleted', $PRC_deleted); |
|
| 135 | - } |
|
| 136 | - |
|
| 137 | - |
|
| 138 | - /** |
|
| 139 | - * get Price type |
|
| 140 | - * |
|
| 141 | - * @return int |
|
| 142 | - * @throws EE_Error |
|
| 143 | - * @throws ReflectionException |
|
| 144 | - */ |
|
| 145 | - public function type() |
|
| 146 | - { |
|
| 147 | - return $this->get('PRT_ID'); |
|
| 148 | - } |
|
| 149 | - |
|
| 150 | - |
|
| 151 | - /** |
|
| 152 | - * get Price Amount |
|
| 153 | - * |
|
| 154 | - * @return float |
|
| 155 | - * @throws EE_Error |
|
| 156 | - * @throws ReflectionException |
|
| 157 | - */ |
|
| 158 | - public function amount() |
|
| 159 | - { |
|
| 160 | - return $this->get('PRC_amount'); |
|
| 161 | - } |
|
| 162 | - |
|
| 163 | - |
|
| 164 | - /** |
|
| 165 | - * get Price Name |
|
| 166 | - * |
|
| 167 | - * @return string |
|
| 168 | - * @throws EE_Error |
|
| 169 | - * @throws ReflectionException |
|
| 170 | - */ |
|
| 171 | - public function name() |
|
| 172 | - { |
|
| 173 | - return $this->get('PRC_name'); |
|
| 174 | - } |
|
| 175 | - |
|
| 176 | - |
|
| 177 | - /** |
|
| 178 | - * get Price description |
|
| 179 | - * |
|
| 180 | - * @return string |
|
| 181 | - * @throws EE_Error |
|
| 182 | - * @throws ReflectionException |
|
| 183 | - */ |
|
| 184 | - public function desc() |
|
| 185 | - { |
|
| 186 | - return $this->get('PRC_desc'); |
|
| 187 | - } |
|
| 188 | - |
|
| 189 | - |
|
| 190 | - /** |
|
| 191 | - * get overrides |
|
| 192 | - * |
|
| 193 | - * @return int |
|
| 194 | - * @throws EE_Error |
|
| 195 | - * @throws ReflectionException |
|
| 196 | - */ |
|
| 197 | - public function overrides() |
|
| 198 | - { |
|
| 199 | - return $this->get('PRC_overrides'); |
|
| 200 | - } |
|
| 201 | - |
|
| 202 | - |
|
| 203 | - /** |
|
| 204 | - * get order |
|
| 205 | - * |
|
| 206 | - * @return int |
|
| 207 | - * @throws EE_Error |
|
| 208 | - * @throws ReflectionException |
|
| 209 | - */ |
|
| 210 | - public function order() |
|
| 211 | - { |
|
| 212 | - return $this->get('PRC_order'); |
|
| 213 | - } |
|
| 214 | - |
|
| 215 | - |
|
| 216 | - /** |
|
| 217 | - * get the author of the price |
|
| 218 | - * |
|
| 219 | - * @return int |
|
| 220 | - * @throws EE_Error |
|
| 221 | - * @throws ReflectionException |
|
| 222 | - * @since 4.5.0 |
|
| 223 | - * |
|
| 224 | - */ |
|
| 225 | - public function wp_user() |
|
| 226 | - { |
|
| 227 | - return $this->get('PRC_wp_user'); |
|
| 228 | - } |
|
| 229 | - |
|
| 230 | - |
|
| 231 | - /** |
|
| 232 | - * get is_default |
|
| 233 | - * |
|
| 234 | - * @return bool |
|
| 235 | - * @throws EE_Error |
|
| 236 | - * @throws ReflectionException |
|
| 237 | - */ |
|
| 238 | - public function is_default() |
|
| 239 | - { |
|
| 240 | - return $this->get('PRC_is_default'); |
|
| 241 | - } |
|
| 242 | - |
|
| 243 | - |
|
| 244 | - /** |
|
| 245 | - * get deleted |
|
| 246 | - * |
|
| 247 | - * @return bool |
|
| 248 | - * @throws EE_Error |
|
| 249 | - * @throws ReflectionException |
|
| 250 | - */ |
|
| 251 | - public function deleted() |
|
| 252 | - { |
|
| 253 | - return $this->get('PRC_deleted'); |
|
| 254 | - } |
|
| 255 | - |
|
| 256 | - |
|
| 257 | - /** |
|
| 258 | - * @return bool |
|
| 259 | - * @throws EE_Error |
|
| 260 | - * @throws ReflectionException |
|
| 261 | - */ |
|
| 262 | - public function parent() |
|
| 263 | - { |
|
| 264 | - return $this->get('PRC_parent'); |
|
| 265 | - } |
|
| 266 | - |
|
| 267 | - |
|
| 268 | - // some helper methods for getting info on the price_type for this price |
|
| 269 | - |
|
| 270 | - |
|
| 271 | - /** |
|
| 272 | - * return whether the price is a base price or not |
|
| 273 | - * |
|
| 274 | - * @return boolean |
|
| 275 | - * @throws EE_Error |
|
| 276 | - * @throws ReflectionException |
|
| 277 | - */ |
|
| 278 | - public function is_base_price() |
|
| 279 | - { |
|
| 280 | - $price_type = $this->type_obj(); |
|
| 281 | - return $price_type->base_type() === 1; |
|
| 282 | - } |
|
| 283 | - |
|
| 284 | - |
|
| 285 | - /** |
|
| 286 | - * |
|
| 287 | - * @return EE_Base_Class|EE_Price_Type |
|
| 288 | - * @throws EE_Error |
|
| 289 | - * @throws ReflectionException |
|
| 290 | - */ |
|
| 291 | - public function type_obj() |
|
| 292 | - { |
|
| 293 | - return $this->get_first_related('Price_Type'); |
|
| 294 | - } |
|
| 295 | - |
|
| 296 | - |
|
| 297 | - /** |
|
| 298 | - * Simply indicates whether this price increases or decreases the total |
|
| 299 | - * |
|
| 300 | - * @return boolean true = discount, otherwise adds to the total |
|
| 301 | - * @throws EE_Error |
|
| 302 | - * @throws ReflectionException |
|
| 303 | - */ |
|
| 304 | - public function is_discount() |
|
| 305 | - { |
|
| 306 | - $price_type = $this->type_obj(); |
|
| 307 | - return $price_type->is_discount(); |
|
| 308 | - } |
|
| 309 | - |
|
| 310 | - |
|
| 311 | - /** |
|
| 312 | - * whether the price is a percentage or not |
|
| 313 | - * |
|
| 314 | - * @return boolean |
|
| 315 | - * @throws EE_Error |
|
| 316 | - * @throws ReflectionException |
|
| 317 | - */ |
|
| 318 | - public function is_percent() |
|
| 319 | - { |
|
| 320 | - $price_type = $this->type_obj(); |
|
| 321 | - return $price_type->get('PRT_is_percent'); |
|
| 322 | - } |
|
| 323 | - |
|
| 324 | - |
|
| 325 | - /** |
|
| 326 | - * return pretty price dependant on whether its a dollar or percent. |
|
| 327 | - * |
|
| 328 | - * @return string |
|
| 329 | - * @throws EE_Error |
|
| 330 | - * @throws ReflectionException |
|
| 331 | - * @since 4.4.0 |
|
| 332 | - * |
|
| 333 | - */ |
|
| 334 | - public function pretty_price() |
|
| 335 | - { |
|
| 336 | - return $this->is_percent() |
|
| 337 | - ? apply_filters( |
|
| 338 | - 'FHEE__format_percentage_value', |
|
| 339 | - $this->get_pretty('PRC_amount', 'localized_float') |
|
| 340 | - ) |
|
| 341 | - : $this->get_pretty('PRC_amount', 'localized_currency'); |
|
| 342 | - } |
|
| 343 | - |
|
| 344 | - |
|
| 345 | - /** |
|
| 346 | - * @return mixed |
|
| 347 | - * @throws EE_Error |
|
| 348 | - * @throws ReflectionException |
|
| 349 | - */ |
|
| 350 | - public function get_price_without_currency_symbol() |
|
| 351 | - { |
|
| 352 | - return $this->get_pretty('PRC_amount', 'localized_float'); |
|
| 353 | - } |
|
| 12 | + /** |
|
| 13 | + * @param array $props_n_values incoming values |
|
| 14 | + * @param string $timezone incoming timezone (if not set the timezone set for the website will be used.) |
|
| 15 | + * @param array $date_formats incoming date_formats in an array where the first value is the |
|
| 16 | + * date_format and the second value is the time format |
|
| 17 | + * @return EE_Price |
|
| 18 | + * @throws EE_Error|ReflectionException |
|
| 19 | + */ |
|
| 20 | + public static function new_instance($props_n_values = [], $timezone = null, $date_formats = []) |
|
| 21 | + { |
|
| 22 | + $price = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats); |
|
| 23 | + if (! $price instanceof EE_Price) { |
|
| 24 | + $price = new EE_Price($props_n_values, false, $timezone, $date_formats); |
|
| 25 | + } |
|
| 26 | + return $price; |
|
| 27 | + } |
|
| 28 | + |
|
| 29 | + |
|
| 30 | + /** |
|
| 31 | + * @param array $props_n_values incoming values from the database |
|
| 32 | + * @param string $timezone incoming timezone as set by the model. If not set the timezone for |
|
| 33 | + * the website will be used. |
|
| 34 | + * @return EE_Price |
|
| 35 | + * @throws EE_Error|ReflectionException |
|
| 36 | + */ |
|
| 37 | + public static function new_instance_from_db($props_n_values = [], $timezone = null) |
|
| 38 | + { |
|
| 39 | + return new self($props_n_values, true, $timezone); |
|
| 40 | + } |
|
| 41 | + |
|
| 42 | + |
|
| 43 | + /** |
|
| 44 | + * Adds some defaults if they're not specified |
|
| 45 | + * |
|
| 46 | + * @param array $props_n_values |
|
| 47 | + * @param bool $bydb |
|
| 48 | + * @param string $timezone |
|
| 49 | + * @param array $date_formats incoming date_formats in an array where the first value is the |
|
| 50 | + * date_format and the second value is the time format |
|
| 51 | + * @throws EE_Error |
|
| 52 | + * @throws ReflectionException |
|
| 53 | + */ |
|
| 54 | + protected function __construct($props_n_values = [], $bydb = false, $timezone = '', $date_formats = []) |
|
| 55 | + { |
|
| 56 | + parent::__construct($props_n_values, $bydb, $timezone, $date_formats); |
|
| 57 | + } |
|
| 58 | + |
|
| 59 | + |
|
| 60 | + /** |
|
| 61 | + * Set Price type ID |
|
| 62 | + * |
|
| 63 | + * @param int $PRT_ID |
|
| 64 | + * @throws EE_Error |
|
| 65 | + * @throws ReflectionException |
|
| 66 | + */ |
|
| 67 | + public function set_type($PRT_ID = 0) |
|
| 68 | + { |
|
| 69 | + $this->set('PRT_ID', $PRT_ID); |
|
| 70 | + } |
|
| 71 | + |
|
| 72 | + |
|
| 73 | + /** |
|
| 74 | + * Set Price Amount |
|
| 75 | + * |
|
| 76 | + * @param float $amount |
|
| 77 | + * @throws EE_Error |
|
| 78 | + * @throws ReflectionException |
|
| 79 | + */ |
|
| 80 | + public function set_amount($amount = 0.00) |
|
| 81 | + { |
|
| 82 | + $this->set('PRC_amount', $amount); |
|
| 83 | + } |
|
| 84 | + |
|
| 85 | + |
|
| 86 | + /** |
|
| 87 | + * Set Price Name |
|
| 88 | + * |
|
| 89 | + * @param string $PRC_name |
|
| 90 | + * @throws EE_Error |
|
| 91 | + * @throws ReflectionException |
|
| 92 | + */ |
|
| 93 | + public function set_name($PRC_name = '') |
|
| 94 | + { |
|
| 95 | + $this->set('PRC_name', $PRC_name); |
|
| 96 | + } |
|
| 97 | + |
|
| 98 | + |
|
| 99 | + /** |
|
| 100 | + * Set Price Description |
|
| 101 | + * |
|
| 102 | + * @param string $PRC_desc |
|
| 103 | + * @throws EE_Error |
|
| 104 | + * @throws ReflectionException |
|
| 105 | + */ |
|
| 106 | + public function set_description($PRC_desc = '') |
|
| 107 | + { |
|
| 108 | + $this->Set('PRC_desc', $PRC_desc); |
|
| 109 | + } |
|
| 110 | + |
|
| 111 | + |
|
| 112 | + /** |
|
| 113 | + * set is_default |
|
| 114 | + * |
|
| 115 | + * @param bool $PRC_is_default |
|
| 116 | + * @throws EE_Error |
|
| 117 | + * @throws ReflectionException |
|
| 118 | + */ |
|
| 119 | + public function set_is_default($PRC_is_default = false) |
|
| 120 | + { |
|
| 121 | + $this->set('PRC_is_default', $PRC_is_default); |
|
| 122 | + } |
|
| 123 | + |
|
| 124 | + |
|
| 125 | + /** |
|
| 126 | + * set deleted |
|
| 127 | + * |
|
| 128 | + * @param bool $PRC_deleted |
|
| 129 | + * @throws EE_Error |
|
| 130 | + * @throws ReflectionException |
|
| 131 | + */ |
|
| 132 | + public function set_deleted($PRC_deleted = null) |
|
| 133 | + { |
|
| 134 | + $this->set('PRC_deleted', $PRC_deleted); |
|
| 135 | + } |
|
| 136 | + |
|
| 137 | + |
|
| 138 | + /** |
|
| 139 | + * get Price type |
|
| 140 | + * |
|
| 141 | + * @return int |
|
| 142 | + * @throws EE_Error |
|
| 143 | + * @throws ReflectionException |
|
| 144 | + */ |
|
| 145 | + public function type() |
|
| 146 | + { |
|
| 147 | + return $this->get('PRT_ID'); |
|
| 148 | + } |
|
| 149 | + |
|
| 150 | + |
|
| 151 | + /** |
|
| 152 | + * get Price Amount |
|
| 153 | + * |
|
| 154 | + * @return float |
|
| 155 | + * @throws EE_Error |
|
| 156 | + * @throws ReflectionException |
|
| 157 | + */ |
|
| 158 | + public function amount() |
|
| 159 | + { |
|
| 160 | + return $this->get('PRC_amount'); |
|
| 161 | + } |
|
| 162 | + |
|
| 163 | + |
|
| 164 | + /** |
|
| 165 | + * get Price Name |
|
| 166 | + * |
|
| 167 | + * @return string |
|
| 168 | + * @throws EE_Error |
|
| 169 | + * @throws ReflectionException |
|
| 170 | + */ |
|
| 171 | + public function name() |
|
| 172 | + { |
|
| 173 | + return $this->get('PRC_name'); |
|
| 174 | + } |
|
| 175 | + |
|
| 176 | + |
|
| 177 | + /** |
|
| 178 | + * get Price description |
|
| 179 | + * |
|
| 180 | + * @return string |
|
| 181 | + * @throws EE_Error |
|
| 182 | + * @throws ReflectionException |
|
| 183 | + */ |
|
| 184 | + public function desc() |
|
| 185 | + { |
|
| 186 | + return $this->get('PRC_desc'); |
|
| 187 | + } |
|
| 188 | + |
|
| 189 | + |
|
| 190 | + /** |
|
| 191 | + * get overrides |
|
| 192 | + * |
|
| 193 | + * @return int |
|
| 194 | + * @throws EE_Error |
|
| 195 | + * @throws ReflectionException |
|
| 196 | + */ |
|
| 197 | + public function overrides() |
|
| 198 | + { |
|
| 199 | + return $this->get('PRC_overrides'); |
|
| 200 | + } |
|
| 201 | + |
|
| 202 | + |
|
| 203 | + /** |
|
| 204 | + * get order |
|
| 205 | + * |
|
| 206 | + * @return int |
|
| 207 | + * @throws EE_Error |
|
| 208 | + * @throws ReflectionException |
|
| 209 | + */ |
|
| 210 | + public function order() |
|
| 211 | + { |
|
| 212 | + return $this->get('PRC_order'); |
|
| 213 | + } |
|
| 214 | + |
|
| 215 | + |
|
| 216 | + /** |
|
| 217 | + * get the author of the price |
|
| 218 | + * |
|
| 219 | + * @return int |
|
| 220 | + * @throws EE_Error |
|
| 221 | + * @throws ReflectionException |
|
| 222 | + * @since 4.5.0 |
|
| 223 | + * |
|
| 224 | + */ |
|
| 225 | + public function wp_user() |
|
| 226 | + { |
|
| 227 | + return $this->get('PRC_wp_user'); |
|
| 228 | + } |
|
| 229 | + |
|
| 230 | + |
|
| 231 | + /** |
|
| 232 | + * get is_default |
|
| 233 | + * |
|
| 234 | + * @return bool |
|
| 235 | + * @throws EE_Error |
|
| 236 | + * @throws ReflectionException |
|
| 237 | + */ |
|
| 238 | + public function is_default() |
|
| 239 | + { |
|
| 240 | + return $this->get('PRC_is_default'); |
|
| 241 | + } |
|
| 242 | + |
|
| 243 | + |
|
| 244 | + /** |
|
| 245 | + * get deleted |
|
| 246 | + * |
|
| 247 | + * @return bool |
|
| 248 | + * @throws EE_Error |
|
| 249 | + * @throws ReflectionException |
|
| 250 | + */ |
|
| 251 | + public function deleted() |
|
| 252 | + { |
|
| 253 | + return $this->get('PRC_deleted'); |
|
| 254 | + } |
|
| 255 | + |
|
| 256 | + |
|
| 257 | + /** |
|
| 258 | + * @return bool |
|
| 259 | + * @throws EE_Error |
|
| 260 | + * @throws ReflectionException |
|
| 261 | + */ |
|
| 262 | + public function parent() |
|
| 263 | + { |
|
| 264 | + return $this->get('PRC_parent'); |
|
| 265 | + } |
|
| 266 | + |
|
| 267 | + |
|
| 268 | + // some helper methods for getting info on the price_type for this price |
|
| 269 | + |
|
| 270 | + |
|
| 271 | + /** |
|
| 272 | + * return whether the price is a base price or not |
|
| 273 | + * |
|
| 274 | + * @return boolean |
|
| 275 | + * @throws EE_Error |
|
| 276 | + * @throws ReflectionException |
|
| 277 | + */ |
|
| 278 | + public function is_base_price() |
|
| 279 | + { |
|
| 280 | + $price_type = $this->type_obj(); |
|
| 281 | + return $price_type->base_type() === 1; |
|
| 282 | + } |
|
| 283 | + |
|
| 284 | + |
|
| 285 | + /** |
|
| 286 | + * |
|
| 287 | + * @return EE_Base_Class|EE_Price_Type |
|
| 288 | + * @throws EE_Error |
|
| 289 | + * @throws ReflectionException |
|
| 290 | + */ |
|
| 291 | + public function type_obj() |
|
| 292 | + { |
|
| 293 | + return $this->get_first_related('Price_Type'); |
|
| 294 | + } |
|
| 295 | + |
|
| 296 | + |
|
| 297 | + /** |
|
| 298 | + * Simply indicates whether this price increases or decreases the total |
|
| 299 | + * |
|
| 300 | + * @return boolean true = discount, otherwise adds to the total |
|
| 301 | + * @throws EE_Error |
|
| 302 | + * @throws ReflectionException |
|
| 303 | + */ |
|
| 304 | + public function is_discount() |
|
| 305 | + { |
|
| 306 | + $price_type = $this->type_obj(); |
|
| 307 | + return $price_type->is_discount(); |
|
| 308 | + } |
|
| 309 | + |
|
| 310 | + |
|
| 311 | + /** |
|
| 312 | + * whether the price is a percentage or not |
|
| 313 | + * |
|
| 314 | + * @return boolean |
|
| 315 | + * @throws EE_Error |
|
| 316 | + * @throws ReflectionException |
|
| 317 | + */ |
|
| 318 | + public function is_percent() |
|
| 319 | + { |
|
| 320 | + $price_type = $this->type_obj(); |
|
| 321 | + return $price_type->get('PRT_is_percent'); |
|
| 322 | + } |
|
| 323 | + |
|
| 324 | + |
|
| 325 | + /** |
|
| 326 | + * return pretty price dependant on whether its a dollar or percent. |
|
| 327 | + * |
|
| 328 | + * @return string |
|
| 329 | + * @throws EE_Error |
|
| 330 | + * @throws ReflectionException |
|
| 331 | + * @since 4.4.0 |
|
| 332 | + * |
|
| 333 | + */ |
|
| 334 | + public function pretty_price() |
|
| 335 | + { |
|
| 336 | + return $this->is_percent() |
|
| 337 | + ? apply_filters( |
|
| 338 | + 'FHEE__format_percentage_value', |
|
| 339 | + $this->get_pretty('PRC_amount', 'localized_float') |
|
| 340 | + ) |
|
| 341 | + : $this->get_pretty('PRC_amount', 'localized_currency'); |
|
| 342 | + } |
|
| 343 | + |
|
| 344 | + |
|
| 345 | + /** |
|
| 346 | + * @return mixed |
|
| 347 | + * @throws EE_Error |
|
| 348 | + * @throws ReflectionException |
|
| 349 | + */ |
|
| 350 | + public function get_price_without_currency_symbol() |
|
| 351 | + { |
|
| 352 | + return $this->get_pretty('PRC_amount', 'localized_float'); |
|
| 353 | + } |
|
| 354 | 354 | } |
@@ -9,36 +9,36 @@ discard block |
||
| 9 | 9 | use EventEspresso\core\services\request\sanitizers\AllowedTags; |
| 10 | 10 | |
| 11 | 11 | if (! function_exists('espresso_get_template_part')) { |
| 12 | - /** |
|
| 13 | - * espresso_get_template_part |
|
| 14 | - * basically a copy of the WordPress get_template_part() function but uses EEH_Template::locate_template() instead, and doesn't add base versions of files |
|
| 15 | - * so not a very useful function at all except that it adds familiarity PLUS filtering based off of the entire template part name |
|
| 16 | - * |
|
| 17 | - * @param string $slug The slug name for the generic template. |
|
| 18 | - * @param string $name The name of the specialised template. |
|
| 19 | - */ |
|
| 20 | - function espresso_get_template_part($slug = null, $name = null) |
|
| 21 | - { |
|
| 22 | - EEH_Template::get_template_part($slug, $name); |
|
| 23 | - } |
|
| 12 | + /** |
|
| 13 | + * espresso_get_template_part |
|
| 14 | + * basically a copy of the WordPress get_template_part() function but uses EEH_Template::locate_template() instead, and doesn't add base versions of files |
|
| 15 | + * so not a very useful function at all except that it adds familiarity PLUS filtering based off of the entire template part name |
|
| 16 | + * |
|
| 17 | + * @param string $slug The slug name for the generic template. |
|
| 18 | + * @param string $name The name of the specialised template. |
|
| 19 | + */ |
|
| 20 | + function espresso_get_template_part($slug = null, $name = null) |
|
| 21 | + { |
|
| 22 | + EEH_Template::get_template_part($slug, $name); |
|
| 23 | + } |
|
| 24 | 24 | } |
| 25 | 25 | |
| 26 | 26 | |
| 27 | 27 | if (! function_exists('espresso_get_object_css_class')) { |
| 28 | - /** |
|
| 29 | - * espresso_get_object_css_class - attempts to generate a css class based on the type of EE object passed |
|
| 30 | - * |
|
| 31 | - * @param EE_Base_Class $object the EE object the css class is being generated for |
|
| 32 | - * @param string $prefix added to the beginning of the generated class |
|
| 33 | - * @param string $suffix added to the end of the generated class |
|
| 34 | - * @return string |
|
| 35 | - * @throws EE_Error |
|
| 36 | - * @throws ReflectionException |
|
| 37 | - */ |
|
| 38 | - function espresso_get_object_css_class($object = null, $prefix = '', $suffix = '') |
|
| 39 | - { |
|
| 40 | - return EEH_Template::get_object_css_class($object, $prefix, $suffix); |
|
| 41 | - } |
|
| 28 | + /** |
|
| 29 | + * espresso_get_object_css_class - attempts to generate a css class based on the type of EE object passed |
|
| 30 | + * |
|
| 31 | + * @param EE_Base_Class $object the EE object the css class is being generated for |
|
| 32 | + * @param string $prefix added to the beginning of the generated class |
|
| 33 | + * @param string $suffix added to the end of the generated class |
|
| 34 | + * @return string |
|
| 35 | + * @throws EE_Error |
|
| 36 | + * @throws ReflectionException |
|
| 37 | + */ |
|
| 38 | + function espresso_get_object_css_class($object = null, $prefix = '', $suffix = '') |
|
| 39 | + { |
|
| 40 | + return EEH_Template::get_object_css_class($object, $prefix, $suffix); |
|
| 41 | + } |
|
| 42 | 42 | } |
| 43 | 43 | |
| 44 | 44 | /** |
@@ -51,750 +51,750 @@ discard block |
||
| 51 | 51 | */ |
| 52 | 52 | class EEH_Template |
| 53 | 53 | { |
| 54 | - /** |
|
| 55 | - * @var EE_Currency_Config[] |
|
| 56 | - */ |
|
| 57 | - private static $currency_config; |
|
| 58 | - |
|
| 59 | - /** |
|
| 60 | - * @var CurrencyFormatter |
|
| 61 | - */ |
|
| 62 | - private static $currency_formatter; |
|
| 63 | - |
|
| 64 | - /** |
|
| 65 | - * @var array |
|
| 66 | - */ |
|
| 67 | - private static $_espresso_themes = []; |
|
| 68 | - |
|
| 69 | - |
|
| 70 | - /** |
|
| 71 | - * get legacy currency config object for locale |
|
| 72 | - * |
|
| 73 | - * @param Locale $currency_locale |
|
| 74 | - * @return EE_Currency_Config |
|
| 75 | - * @throws EE_Error |
|
| 76 | - * @throws ReflectionException |
|
| 77 | - * @since $VID:$ |
|
| 78 | - */ |
|
| 79 | - private static function getCurrencyConfigForCountryISO(Locale $currency_locale) |
|
| 80 | - { |
|
| 81 | - $currencyISO = $currency_locale->currencyIsoCode(); |
|
| 82 | - if ( |
|
| 83 | - ! isset(EEH_Template::$currency_config[ $currencyISO ]) |
|
| 84 | - || ! EEH_Template::$currency_config[ $currencyISO ] instanceof EE_Currency_Config |
|
| 85 | - ) { |
|
| 86 | - $CNT_ISO = EEM_Country::instance()->getCountryISOForCurrencyISO($currencyISO); |
|
| 87 | - $currency_config = new EE_Currency_Config($CNT_ISO); |
|
| 88 | - EEH_Template::$currency_config[ $currencyISO ] = $currency_config; |
|
| 89 | - } |
|
| 90 | - return EEH_Template::$currency_config[ $currencyISO ]; |
|
| 91 | - } |
|
| 92 | - |
|
| 93 | - |
|
| 94 | - /** |
|
| 95 | - * @return CurrencyFormatter |
|
| 96 | - * @since $VID:$ |
|
| 97 | - */ |
|
| 98 | - private static function getCurrencyFormatter() |
|
| 99 | - { |
|
| 100 | - if (! EEH_Template::$currency_formatter instanceof CurrencyFormatter) { |
|
| 101 | - EEH_Template::$currency_formatter = LoaderFactory::getLoader()->getShared(CurrencyFormatter::class); |
|
| 102 | - } |
|
| 103 | - return EEH_Template::$currency_formatter; |
|
| 104 | - } |
|
| 105 | - |
|
| 106 | - |
|
| 107 | - /** |
|
| 108 | - * is_espresso_theme - returns TRUE or FALSE on whether the currently active WP theme is an espresso theme |
|
| 109 | - * |
|
| 110 | - * @return boolean |
|
| 111 | - */ |
|
| 112 | - public static function is_espresso_theme() |
|
| 113 | - { |
|
| 114 | - return wp_get_theme()->get('TextDomain') === 'event_espresso'; |
|
| 115 | - } |
|
| 116 | - |
|
| 117 | - |
|
| 118 | - /** |
|
| 119 | - * load_espresso_theme_functions - if current theme is an espresso theme, or uses ee theme template parts, then |
|
| 120 | - * load its functions.php file ( if not already loaded ) |
|
| 121 | - * |
|
| 122 | - * @return void |
|
| 123 | - */ |
|
| 124 | - public static function load_espresso_theme_functions() |
|
| 125 | - { |
|
| 126 | - if ( |
|
| 127 | - ! defined('EE_THEME_FUNCTIONS_LOADED') |
|
| 128 | - && is_readable(EE_PUBLIC . EE_Config::get_current_theme() . '/functions.php') |
|
| 129 | - ) { |
|
| 130 | - require_once(EE_PUBLIC . EE_Config::get_current_theme() . '/functions.php'); |
|
| 131 | - } |
|
| 132 | - } |
|
| 133 | - |
|
| 134 | - |
|
| 135 | - /** |
|
| 136 | - * get_espresso_themes - returns an array of Espresso Child themes located in the /templates/ directory |
|
| 137 | - * |
|
| 138 | - * @return array |
|
| 139 | - */ |
|
| 140 | - public static function get_espresso_themes() |
|
| 141 | - { |
|
| 142 | - if (empty(EEH_Template::$_espresso_themes)) { |
|
| 143 | - $espresso_themes = glob(EE_PUBLIC . '*', GLOB_ONLYDIR); |
|
| 144 | - if (empty($espresso_themes)) { |
|
| 145 | - return []; |
|
| 146 | - } |
|
| 147 | - if (($key = array_search('global_assets', $espresso_themes, true)) !== false) { |
|
| 148 | - unset($espresso_themes[ $key ]); |
|
| 149 | - } |
|
| 150 | - EEH_Template::$_espresso_themes = []; |
|
| 151 | - foreach ($espresso_themes as $espresso_theme) { |
|
| 152 | - EEH_Template::$_espresso_themes[ basename($espresso_theme) ] = $espresso_theme; |
|
| 153 | - } |
|
| 154 | - } |
|
| 155 | - return EEH_Template::$_espresso_themes; |
|
| 156 | - } |
|
| 157 | - |
|
| 158 | - |
|
| 159 | - /** |
|
| 160 | - * EEH_Template::get_template_part |
|
| 161 | - * basically a copy of the WordPress get_template_part() function but uses EEH_Template::locate_template() instead, |
|
| 162 | - * and doesn't add base versions of files so not a very useful function at all except that it adds familiarity PLUS |
|
| 163 | - * filtering based off of the entire template part name |
|
| 164 | - * |
|
| 165 | - * @param string $slug The slug name for the generic template. |
|
| 166 | - * @param string $name The name of the specialised template. |
|
| 167 | - * @param array $template_args |
|
| 168 | - * @param bool $return_string |
|
| 169 | - * @return void echos the html output for the template |
|
| 170 | - */ |
|
| 171 | - public static function get_template_part( |
|
| 172 | - $slug = null, |
|
| 173 | - $name = null, |
|
| 174 | - $template_args = [], |
|
| 175 | - $return_string = false |
|
| 176 | - ) { |
|
| 177 | - do_action("get_template_part_{$slug}-{$name}", $slug, $name); |
|
| 178 | - $templates = []; |
|
| 179 | - $name = (string) $name; |
|
| 180 | - if ($name !== '') { |
|
| 181 | - $templates[] = "{$slug}-{$name}.php"; |
|
| 182 | - } |
|
| 183 | - // allow template parts to be turned off via something like: |
|
| 184 | - // add_filter( 'FHEE__content_espresso_events_tickets_template__display_datetimes', '__return_false' ); |
|
| 185 | - if (apply_filters("FHEE__EEH_Template__get_template_part__display__{$slug}_{$name}", true)) { |
|
| 186 | - return EEH_Template::locate_template($templates, $template_args, true, $return_string); |
|
| 187 | - } |
|
| 188 | - return ''; |
|
| 189 | - } |
|
| 190 | - |
|
| 191 | - |
|
| 192 | - /** |
|
| 193 | - * locate_template |
|
| 194 | - * locate a template file by looking in the following places, in the following order: |
|
| 195 | - * <server path up to>/wp-content/themes/<current active WordPress theme>/ |
|
| 196 | - * <assumed full absolute server path> |
|
| 197 | - * <server path up to>/wp-content/uploads/espresso/templates/<current EE theme>/ |
|
| 198 | - * <server path up to>/wp-content/uploads/espresso/templates/ |
|
| 199 | - * <server path up to>/wp-content/plugins/<EE4 folder>/public/<current EE theme>/ |
|
| 200 | - * <server path up to>/wp-content/plugins/<EE4 folder>/core/templates/<current EE theme>/ |
|
| 201 | - * <server path up to>/wp-content/plugins/<EE4 folder>/ |
|
| 202 | - * as soon as the template is found in one of these locations, it will be returned or loaded |
|
| 203 | - * Example: |
|
| 204 | - * You are using the WordPress Twenty Sixteen theme, |
|
| 205 | - * and you want to customize the "some-event.template.php" template, |
|
| 206 | - * which is located in the "/relative/path/to/" folder relative to the main EE plugin folder. |
|
| 207 | - * Assuming WP is installed on your server in the "/home/public_html/" folder, |
|
| 208 | - * EEH_Template::locate_template() will look at the following paths in order until the template is found: |
|
| 209 | - * /home/public_html/wp-content/themes/twentysixteen/some-event.template.php |
|
| 210 | - * /relative/path/to/some-event.template.php |
|
| 211 | - * /home/public_html/wp-content/uploads/espresso/templates/Espresso_Arabica_2014/relative/path/to/some-event.template.php |
|
| 212 | - * /home/public_html/wp-content/uploads/espresso/templates/relative/path/to/some-event.template.php |
|
| 213 | - * /home/public_html/wp-content/plugins/event-espresso-core-reg/public/Espresso_Arabica_2014/relative/path/to/some-event.template.php |
|
| 214 | - * /home/public_html/wp-content/plugins/event-espresso-core-reg/core/templates/Espresso_Arabica_2014/relative/path/to/some-event.template.php |
|
| 215 | - * /home/public_html/wp-content/plugins/event-espresso-core-reg/relative/path/to/some-event.template.php |
|
| 216 | - * Had you passed an absolute path to your template that was in some other location, |
|
| 217 | - * ie: "/absolute/path/to/some-event.template.php" |
|
| 218 | - * then the search would have been : |
|
| 219 | - * /home/public_html/wp-content/themes/twentysixteen/some-event.template.php |
|
| 220 | - * /absolute/path/to/some-event.template.php |
|
| 221 | - * and stopped there upon finding it in the second location |
|
| 222 | - * |
|
| 223 | - * @param array|string $templates array of template file names including extension (or just a single string) |
|
| 224 | - * @param array $template_args an array of arguments to be extracted for use in the template |
|
| 225 | - * @param boolean $load whether to pass the located template path on to the |
|
| 226 | - * EEH_Template::display_template() method or simply return it |
|
| 227 | - * @param boolean $return_string whether to send output immediately to screen, or capture and return as a |
|
| 228 | - * string |
|
| 229 | - * @param boolean $check_if_custom If TRUE, this flags this method to return boolean for whether this will |
|
| 230 | - * generate a custom template or not. Used in places where you don't actually |
|
| 231 | - * load the template, you just want to know if there's a custom version of it. |
|
| 232 | - * @return mixed |
|
| 233 | - * @throws DomainException |
|
| 234 | - * @throws InvalidArgumentException |
|
| 235 | - * @throws InvalidDataTypeException |
|
| 236 | - * @throws InvalidInterfaceException |
|
| 237 | - */ |
|
| 238 | - public static function locate_template( |
|
| 239 | - $templates = [], |
|
| 240 | - $template_args = [], |
|
| 241 | - $load = true, |
|
| 242 | - $return_string = true, |
|
| 243 | - $check_if_custom = false |
|
| 244 | - ) { |
|
| 245 | - // first use WP locate_template to check for template in the current theme folder |
|
| 246 | - $template_path = locate_template($templates); |
|
| 247 | - |
|
| 248 | - if ($check_if_custom && ! empty($template_path)) { |
|
| 249 | - return true; |
|
| 250 | - } |
|
| 251 | - |
|
| 252 | - // not in the theme |
|
| 253 | - if (empty($template_path)) { |
|
| 254 | - // not even a template to look for ? |
|
| 255 | - if (empty($templates)) { |
|
| 256 | - $loader = LoaderFactory::getLoader(); |
|
| 257 | - /** @var RequestInterface $request */ |
|
| 258 | - $request = $loader->getShared(RequestInterface::class); |
|
| 259 | - // get post_type |
|
| 260 | - $post_type = $request->getRequestParam('post_type'); |
|
| 261 | - /** @var EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions $custom_post_types */ |
|
| 262 | - $custom_post_types = $loader->getShared( |
|
| 263 | - 'EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions' |
|
| 264 | - ); |
|
| 265 | - // get array of EE Custom Post Types |
|
| 266 | - $EE_CPTs = $custom_post_types->getDefinitions(); |
|
| 267 | - // build template name based on request |
|
| 268 | - if (isset($EE_CPTs[ $post_type ])) { |
|
| 269 | - $archive_or_single = is_archive() ? 'archive' : ''; |
|
| 270 | - $archive_or_single = is_single() ? 'single' : $archive_or_single; |
|
| 271 | - $templates = $archive_or_single . '-' . $post_type . '.php'; |
|
| 272 | - } |
|
| 273 | - } |
|
| 274 | - // currently active EE template theme |
|
| 275 | - $current_theme = EE_Config::get_current_theme(); |
|
| 276 | - |
|
| 277 | - // array of paths to folders that may contain templates |
|
| 278 | - $template_folder_paths = [ |
|
| 279 | - // first check the /wp-content/uploads/espresso/templates/(current EE theme)/ folder for an EE theme template file |
|
| 280 | - EVENT_ESPRESSO_TEMPLATE_DIR . $current_theme, |
|
| 281 | - // then in the root of the /wp-content/uploads/espresso/templates/ folder |
|
| 282 | - EVENT_ESPRESSO_TEMPLATE_DIR, |
|
| 283 | - ]; |
|
| 284 | - |
|
| 285 | - // add core plugin folders for checking only if we're not $check_if_custom |
|
| 286 | - if (! $check_if_custom) { |
|
| 287 | - $core_paths = [ |
|
| 288 | - // in the /wp-content/plugins/(EE4 folder)/public/(current EE theme)/ folder within the plugin |
|
| 289 | - EE_PUBLIC . $current_theme, |
|
| 290 | - // in the /wp-content/plugins/(EE4 folder)/core/templates/(current EE theme)/ folder within the plugin |
|
| 291 | - EE_TEMPLATES . $current_theme, |
|
| 292 | - // or maybe relative from the plugin root: /wp-content/plugins/(EE4 folder)/ |
|
| 293 | - EE_PLUGIN_DIR_PATH, |
|
| 294 | - ]; |
|
| 295 | - $template_folder_paths = array_merge($template_folder_paths, $core_paths); |
|
| 296 | - } |
|
| 297 | - |
|
| 298 | - // now filter that array |
|
| 299 | - $template_folder_paths = apply_filters( |
|
| 300 | - 'FHEE__EEH_Template__locate_template__template_folder_paths', |
|
| 301 | - $template_folder_paths |
|
| 302 | - ); |
|
| 303 | - $templates = is_array($templates) ? $templates : [$templates]; |
|
| 304 | - $template_folder_paths = is_array($template_folder_paths) |
|
| 305 | - ? $template_folder_paths |
|
| 306 | - : [$template_folder_paths]; |
|
| 307 | - // array to hold all possible template paths |
|
| 308 | - $full_template_paths = []; |
|
| 309 | - $file_name = ''; |
|
| 310 | - // loop through $templates |
|
| 311 | - foreach ($templates as $template) { |
|
| 312 | - // normalize directory separators |
|
| 313 | - $template = EEH_File::standardise_directory_separators($template); |
|
| 314 | - $file_name = basename($template); |
|
| 315 | - $template_path_minus_file_name = substr($template, 0, (strlen($file_name) * -1)); |
|
| 316 | - // while looping through all template folder paths |
|
| 317 | - foreach ($template_folder_paths as $template_folder_path) { |
|
| 318 | - // normalize directory separators |
|
| 319 | - $template_folder_path = EEH_File::standardise_directory_separators($template_folder_path); |
|
| 320 | - // determine if any common base path exists between the two paths |
|
| 321 | - $common_base_path = EEH_Template::_find_common_base_path( |
|
| 322 | - [$template_folder_path, $template_path_minus_file_name] |
|
| 323 | - ); |
|
| 324 | - if ($common_base_path !== '') { |
|
| 325 | - // both paths have a common base, so just tack the filename onto our search path |
|
| 326 | - $resolved_path = EEH_File::end_with_directory_separator($template_folder_path) . $file_name; |
|
| 327 | - } else { |
|
| 328 | - // no common base path, so let's just concatenate |
|
| 329 | - $resolved_path = EEH_File::end_with_directory_separator($template_folder_path) . $template; |
|
| 330 | - } |
|
| 331 | - // build up our template locations array by adding our resolved paths |
|
| 332 | - $full_template_paths[] = $resolved_path; |
|
| 333 | - } |
|
| 334 | - // if $template is an absolute path, then we'll tack it onto the start of our array so that it gets searched first |
|
| 335 | - array_unshift($full_template_paths, $template); |
|
| 336 | - // path to the directory of the current theme: /wp-content/themes/(current WP theme)/ |
|
| 337 | - array_unshift($full_template_paths, get_stylesheet_directory() . '/' . $file_name); |
|
| 338 | - } |
|
| 339 | - // filter final array of full template paths |
|
| 340 | - $full_template_paths = apply_filters( |
|
| 341 | - 'FHEE__EEH_Template__locate_template__full_template_paths', |
|
| 342 | - $full_template_paths, |
|
| 343 | - $file_name |
|
| 344 | - ); |
|
| 345 | - // now loop through our final array of template location paths and check each location |
|
| 346 | - foreach ((array) $full_template_paths as $full_template_path) { |
|
| 347 | - if (is_readable($full_template_path)) { |
|
| 348 | - $template_path = str_replace(['\\', '/'], DIRECTORY_SEPARATOR, $full_template_path); |
|
| 349 | - break; |
|
| 350 | - } |
|
| 351 | - } |
|
| 352 | - } |
|
| 353 | - |
|
| 354 | - // hook that can be used to display the full template path that will be used |
|
| 355 | - do_action('AHEE__EEH_Template__locate_template__full_template_path', $template_path); |
|
| 356 | - |
|
| 357 | - // if we got it and you want to see it... |
|
| 358 | - if ($template_path && $load && ! $check_if_custom) { |
|
| 359 | - if ($return_string) { |
|
| 360 | - return EEH_Template::display_template($template_path, $template_args, true); |
|
| 361 | - } |
|
| 362 | - EEH_Template::display_template($template_path, $template_args); |
|
| 363 | - } |
|
| 364 | - return $check_if_custom && ! empty($template_path) ? true : $template_path; |
|
| 365 | - } |
|
| 366 | - |
|
| 367 | - |
|
| 368 | - /** |
|
| 369 | - * _find_common_base_path |
|
| 370 | - * given two paths, this determines if there is a common base path between the two |
|
| 371 | - * |
|
| 372 | - * @param array $paths |
|
| 373 | - * @return string |
|
| 374 | - */ |
|
| 375 | - protected static function _find_common_base_path($paths) |
|
| 376 | - { |
|
| 377 | - $last_offset = 0; |
|
| 378 | - $common_base_path = ''; |
|
| 379 | - while (($index = strpos($paths[0], '/', $last_offset)) !== false) { |
|
| 380 | - $dir_length = $index - $last_offset + 1; |
|
| 381 | - $directory = substr($paths[0], $last_offset, $dir_length); |
|
| 382 | - foreach ($paths as $path) { |
|
| 383 | - if (substr($path, $last_offset, $dir_length) !== $directory) { |
|
| 384 | - return $common_base_path; |
|
| 385 | - } |
|
| 386 | - } |
|
| 387 | - $common_base_path .= $directory; |
|
| 388 | - $last_offset = $index + 1; |
|
| 389 | - } |
|
| 390 | - return substr($common_base_path, 0, -1); |
|
| 391 | - } |
|
| 392 | - |
|
| 393 | - |
|
| 394 | - /** |
|
| 395 | - * load and display a template |
|
| 396 | - * |
|
| 397 | - * @param bool|string $template_path server path to the file to be loaded, including file name and extension |
|
| 398 | - * @param array $template_args an array of arguments to be extracted for use in the template |
|
| 399 | - * @param boolean $return_string whether to send output immediately to screen, or capture and return as a |
|
| 400 | - * string |
|
| 401 | - * @param bool $throw_exceptions if set to true, will throw an exception if the template is either |
|
| 402 | - * not found or is not readable |
|
| 403 | - * @return string |
|
| 404 | - * @throws DomainException |
|
| 405 | - */ |
|
| 406 | - public static function display_template( |
|
| 407 | - $template_path = false, |
|
| 408 | - $template_args = [], |
|
| 409 | - $return_string = false, |
|
| 410 | - $throw_exceptions = false |
|
| 411 | - ) { |
|
| 412 | - /** |
|
| 413 | - * These two filters are intended for last minute changes to templates being loaded and/or template arg |
|
| 414 | - * modifications. NOTE... modifying these things can cause breakage as most templates running through |
|
| 415 | - * the display_template method are templates we DON'T want modified (usually because of js |
|
| 416 | - * dependencies etc). So unless you know what you are doing, do NOT filter templates or template args |
|
| 417 | - * using this. |
|
| 418 | - * |
|
| 419 | - * @since 4.6.0 |
|
| 420 | - */ |
|
| 421 | - $template_path = (string) apply_filters('FHEE__EEH_Template__display_template__template_path', $template_path); |
|
| 422 | - $template_args = (array) apply_filters('FHEE__EEH_Template__display_template__template_args', $template_args); |
|
| 423 | - |
|
| 424 | - // you gimme nuttin - YOU GET NUTTIN !! |
|
| 425 | - if (! $template_path || ! is_readable($template_path)) { |
|
| 426 | - // ignore whether template is accessible ? |
|
| 427 | - if ($throw_exceptions) { |
|
| 428 | - throw new DomainException( |
|
| 429 | - esc_html__('Invalid, unreadable, or missing file.', 'event_espresso') |
|
| 430 | - ); |
|
| 431 | - } |
|
| 432 | - return ''; |
|
| 433 | - } |
|
| 434 | - // if $template_args are not in an array, then make it so |
|
| 435 | - if (! is_array($template_args) && ! is_object($template_args)) { |
|
| 436 | - $template_args = [$template_args]; |
|
| 437 | - } |
|
| 438 | - extract($template_args, EXTR_SKIP); |
|
| 439 | - // ignore whether template is accessible ? |
|
| 440 | - if ($throw_exceptions && ! is_readable($template_path)) { |
|
| 441 | - throw new DomainException( |
|
| 442 | - esc_html__( |
|
| 443 | - 'Invalid, unreadable, or missing file.', |
|
| 444 | - 'event_espresso' |
|
| 445 | - ) |
|
| 446 | - ); |
|
| 447 | - } |
|
| 448 | - |
|
| 449 | - |
|
| 450 | - if ($return_string) { |
|
| 451 | - // because we want to return a string, we are going to capture the output |
|
| 452 | - ob_start(); |
|
| 453 | - include($template_path); |
|
| 454 | - return ob_get_clean(); |
|
| 455 | - } |
|
| 456 | - include($template_path); |
|
| 457 | - return ''; |
|
| 458 | - } |
|
| 459 | - |
|
| 460 | - |
|
| 461 | - /** |
|
| 462 | - * get_object_css_class - attempts to generate a css class based on the type of EE object passed |
|
| 463 | - * |
|
| 464 | - * @param EE_Base_Class $object the EE object the css class is being generated for |
|
| 465 | - * @param string $prefix added to the beginning of the generated class |
|
| 466 | - * @param string $suffix added to the end of the generated class |
|
| 467 | - * @return string |
|
| 468 | - * @throws EE_Error |
|
| 469 | - * @throws ReflectionException |
|
| 470 | - */ |
|
| 471 | - public static function get_object_css_class($object = null, $prefix = '', $suffix = '') |
|
| 472 | - { |
|
| 473 | - // in the beginning... |
|
| 474 | - $prefix = ! empty($prefix) ? rtrim($prefix, '-') . '-' : ''; |
|
| 475 | - // da muddle |
|
| 476 | - $class = ''; |
|
| 477 | - // the end |
|
| 478 | - $suffix = ! empty($suffix) ? '-' . ltrim($suffix, '-') : ''; |
|
| 479 | - // is the passed object an EE object ? |
|
| 480 | - if ($object instanceof EE_Base_Class) { |
|
| 481 | - // grab the exact type of object |
|
| 482 | - $obj_class = get_class($object); |
|
| 483 | - // depending on the type of object... |
|
| 484 | - $class = strtolower(str_replace('_', '-', $obj_class)); |
|
| 485 | - $class .= method_exists($obj_class, 'name') ? '-' . sanitize_title($object->name()) : ''; |
|
| 486 | - } |
|
| 487 | - return $prefix . $class . $suffix; |
|
| 488 | - } |
|
| 489 | - |
|
| 490 | - |
|
| 491 | - /** |
|
| 492 | - * EEH_Template::format_currency |
|
| 493 | - * This helper takes a raw float value and formats it according to the default config country currency settings, or |
|
| 494 | - * the country currency settings from the supplied country ISO code |
|
| 495 | - * |
|
| 496 | - * @param float $amount raw money value |
|
| 497 | - * @param boolean $return_raw whether to return the formatted float value only with no currency sign |
|
| 498 | - * or code |
|
| 499 | - * @param boolean $display_code whether to display the country code (USD). Default = TRUE |
|
| 500 | - * @param string $CNT_ISO 2 letter ISO code for a country |
|
| 501 | - * @param string $cur_code_span_class |
|
| 502 | - * @param boolean $allow_fractional_subunits whether to allow displaying partial penny amounts |
|
| 503 | - * @return string the html output for the formatted money value |
|
| 504 | - * @throws EE_Error |
|
| 505 | - * @throws ReflectionException |
|
| 506 | - * @deprecated $VID:$ plz use CurrencyFormatter::formatForLocale() |
|
| 507 | - */ |
|
| 508 | - public static function format_currency( |
|
| 509 | - $amount = null, |
|
| 510 | - $return_raw = false, |
|
| 511 | - $display_code = true, |
|
| 512 | - $CNT_ISO = '', |
|
| 513 | - $cur_code_span_class = 'currency-code', // not used anywhere |
|
| 514 | - $allow_fractional_subunits = false |
|
| 515 | - ) { |
|
| 516 | - // ensure amount was received |
|
| 517 | - if ($amount === null) { |
|
| 518 | - $msg = esc_html__('In order to format currency, an amount needs to be passed.', 'event_espresso'); |
|
| 519 | - EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__); |
|
| 520 | - return ''; |
|
| 521 | - } |
|
| 522 | - // ensure amount is float |
|
| 523 | - $amount = (float) apply_filters('FHEE__EEH_Template__format_currency__raw_amount', (float) $amount); |
|
| 524 | - $CNT_ISO = apply_filters('FHEE__EEH_Template__format_currency__CNT_ISO', $CNT_ISO, $amount); |
|
| 525 | - |
|
| 526 | - // filter raw amount (allows 0.00 to be changed to "free" for example) |
|
| 527 | - $amount_formatted = apply_filters('FHEE__EEH_Template__format_currency__amount', $amount, $return_raw); |
|
| 528 | - // still a number or was amount converted to a string like "free" ? |
|
| 529 | - if (! is_float($amount_formatted)) { |
|
| 530 | - return esc_html($amount_formatted); |
|
| 531 | - } |
|
| 532 | - |
|
| 533 | - try { |
|
| 534 | - $currency_formatter = EEH_Template::getCurrencyFormatter(); |
|
| 535 | - $currency_ISO = ''; |
|
| 536 | - if ($CNT_ISO !== '') { |
|
| 537 | - $currency_config = EEH_Money::get_currency_config($CNT_ISO); |
|
| 538 | - $currency_ISO = $currency_config->code; |
|
| 539 | - } |
|
| 540 | - $currency_locale = $currency_formatter->getLocaleForCurrencyISO($currency_ISO, true); |
|
| 541 | - |
|
| 542 | - // filter to allow global setting of display_code |
|
| 543 | - $display_code = apply_filters('FHEE__EEH_Template__format_currency__display_code', $display_code); |
|
| 544 | - |
|
| 545 | - $format = CurrencyFormatter::FORMAT_LOCALIZED_CURRENCY; |
|
| 546 | - if ($return_raw) { |
|
| 547 | - $format = CurrencyFormatter::FORMAT_LOCALIZED_FLOAT; |
|
| 548 | - } elseif ($display_code) { |
|
| 549 | - $format = CurrencyFormatter::FORMAT_LOCALIZED_CURRENCY_HTML_CODE; |
|
| 550 | - } |
|
| 551 | - // don't like this but it maintains backwards compatibility with how things were done before |
|
| 552 | - $precision = $allow_fractional_subunits |
|
| 553 | - ? $currency_locale->decimalPrecision() + 2 |
|
| 554 | - : $currency_locale->decimalPrecision(); |
|
| 555 | - |
|
| 556 | - $amount_formatted = $currency_formatter->formatForLocale( |
|
| 557 | - $amount_formatted, |
|
| 558 | - $format, |
|
| 559 | - $currency_locale->name(), |
|
| 560 | - $precision |
|
| 561 | - ); |
|
| 562 | - |
|
| 563 | - // filter results |
|
| 564 | - $amount_formatted = apply_filters( |
|
| 565 | - 'FHEE__EEH_Template__format_currency__amount_formatted', |
|
| 566 | - $amount_formatted, |
|
| 567 | - EEH_Template::getCurrencyConfigForCountryISO($currency_locale), |
|
| 568 | - $return_raw, |
|
| 569 | - $display_code |
|
| 570 | - ); |
|
| 571 | - } catch (Exception $e) { |
|
| 572 | - // eat exception |
|
| 573 | - } |
|
| 574 | - // return formatted currency amount |
|
| 575 | - return esc_html($amount_formatted); |
|
| 576 | - } |
|
| 577 | - |
|
| 578 | - |
|
| 579 | - /** |
|
| 580 | - * This function is used for outputting the localized label for a given status id in the schema requested (and |
|
| 581 | - * possibly plural). The intended use of this function is only for cases where wanting a label outside of a |
|
| 582 | - * related status model or model object (i.e. in documentation etc.) |
|
| 583 | - * |
|
| 584 | - * @param string $status_id Status ID matching a registered status in the esp_status table. If there is no |
|
| 585 | - * match, then 'Unknown' will be returned. |
|
| 586 | - * @param boolean $plural Whether to return plural or not |
|
| 587 | - * @param string $schema 'UPPER', 'lower', or 'Sentence' |
|
| 588 | - * @return string The localized label for the status id. |
|
| 589 | - * @throws EE_Error |
|
| 590 | - */ |
|
| 591 | - public static function pretty_status($status_id, $plural = false, $schema = 'upper') |
|
| 592 | - { |
|
| 593 | - $status = EEM_Status::instance()->localized_status( |
|
| 594 | - [$status_id => esc_html__('unknown', 'event_espresso')], |
|
| 595 | - $plural, |
|
| 596 | - $schema |
|
| 597 | - ); |
|
| 598 | - return $status[ $status_id ]; |
|
| 599 | - } |
|
| 600 | - |
|
| 601 | - |
|
| 602 | - /** |
|
| 603 | - * This helper just returns a button or link for the given parameters |
|
| 604 | - * |
|
| 605 | - * @param string $url the url for the link, note that `esc_url` will be called on it |
|
| 606 | - * @param string $label What is the label you want displayed for the button |
|
| 607 | - * @param string $class what class is used for the button (defaults to 'button-primary') |
|
| 608 | - * @param string $icon |
|
| 609 | - * @param string $title |
|
| 610 | - * @return string the html output for the button |
|
| 611 | - */ |
|
| 612 | - public static function get_button_or_link($url, $label, $class = 'button button--primary', $icon = '', $title = '') |
|
| 613 | - { |
|
| 614 | - $icon_html = ''; |
|
| 615 | - if (! empty($icon)) { |
|
| 616 | - $dashicons = preg_split("(ee-icon |dashicons )", $icon); |
|
| 617 | - $dashicons = array_filter($dashicons); |
|
| 618 | - $count = count($dashicons); |
|
| 619 | - $icon_html .= $count > 1 ? '<span class="ee-composite-dashicon">' : ''; |
|
| 620 | - foreach ($dashicons as $dashicon) { |
|
| 621 | - $type = strpos($dashicon, 'ee-icon') !== false ? 'ee-icon ' : 'dashicons '; |
|
| 622 | - $icon_html .= '<span class="' . $type . $dashicon . '"></span>'; |
|
| 623 | - } |
|
| 624 | - $icon_html .= $count > 1 ? '</span>' : ''; |
|
| 625 | - } |
|
| 626 | - // sanitize & escape |
|
| 627 | - $id = sanitize_title_with_dashes($label); |
|
| 628 | - $url = esc_url_raw($url); |
|
| 629 | - $class = esc_attr($class); |
|
| 630 | - $title = esc_attr($title); |
|
| 631 | - $label = esc_html($label); |
|
| 632 | - return "<a id='{$id}' href='{$url}' class='{$class}' title='{$title}'>{$icon_html}{$label}</a>"; |
|
| 633 | - } |
|
| 634 | - |
|
| 635 | - |
|
| 636 | - /** |
|
| 637 | - * This returns a generated link that will load the related help tab on admin pages. |
|
| 638 | - * |
|
| 639 | - * @param string $help_tab_id the id for the connected help tab |
|
| 640 | - * @param bool|string $page The page identifier for the page the help tab is on |
|
| 641 | - * @param bool|string $action The action (route) for the admin page the help tab is on. |
|
| 642 | - * @param bool|string $icon_style (optional) include css class for the style you want to use for the help icon. |
|
| 643 | - * @param bool|string $help_text (optional) send help text you want to use for the link if default not to be used |
|
| 644 | - * @return string generated link |
|
| 645 | - */ |
|
| 646 | - public static function get_help_tab_link( |
|
| 647 | - $help_tab_id, |
|
| 648 | - $page = false, |
|
| 649 | - $action = false, |
|
| 650 | - $icon_style = false, |
|
| 651 | - $help_text = false |
|
| 652 | - ) { |
|
| 653 | - $allowedtags = AllowedTags::getAllowedTags(); |
|
| 654 | - /** @var RequestInterface $request */ |
|
| 655 | - $request = LoaderFactory::getLoader()->getShared(RequestInterface::class); |
|
| 656 | - $page = $page ?: $request->getRequestParam('page', '', 'key'); |
|
| 657 | - $action = $action ?: $request->getRequestParam('action', 'default', 'key'); |
|
| 658 | - |
|
| 659 | - |
|
| 660 | - $help_tab_lnk = $page . '-' . $action . '-' . $help_tab_id; |
|
| 661 | - $icon = $icon_style ? $icon_style : ' dashicons-editor-help'; |
|
| 662 | - $help_text = $help_text ? $help_text : ''; |
|
| 663 | - $link = '<a id="' . $help_tab_lnk . '"'; |
|
| 664 | - $link .= ' class="ee-clickable espresso-help-tab-lnk dashicons' . $icon . '"'; |
|
| 665 | - $link .= ' title="' . esc_attr__( |
|
| 666 | - 'Click to open the \'Help\' tab for more information about this feature.', |
|
| 667 | - 'event_espresso' |
|
| 668 | - ) . '"'; |
|
| 669 | - $link .= ' > ' . $help_text . ' </a>'; |
|
| 670 | - return $link; |
|
| 671 | - } |
|
| 672 | - |
|
| 673 | - |
|
| 674 | - /** |
|
| 675 | - * This helper generates the html structure for the jquery joyride plugin with the given params. |
|
| 676 | - * |
|
| 677 | - * @link http://zurb.com/playground/jquery-joyride-feature-tour-plugin |
|
| 678 | - * @see EE_Admin_Page->_stop_callback() for the construct expected for the $stops param. |
|
| 679 | - * @param EE_Help_Tour |
|
| 680 | - * @return string html |
|
| 681 | - * @throws EE_Error |
|
| 682 | - */ |
|
| 683 | - public static function help_tour_stops_generator(EE_Help_Tour $tour) |
|
| 684 | - { |
|
| 685 | - $id = $tour->get_slug(); |
|
| 686 | - $stops = $tour->get_stops(); |
|
| 687 | - |
|
| 688 | - $content = '<ol style="display:none" id="' . $id . '">'; |
|
| 689 | - |
|
| 690 | - foreach ($stops as $stop) { |
|
| 691 | - $data_id = ! empty($stop['id']) ? ' data-id="' . $stop['id'] . '"' : ''; |
|
| 692 | - $data_class = empty($data_id) && ! empty($stop['class']) ? ' data-class="' . $stop['class'] . '"' : ''; |
|
| 693 | - |
|
| 694 | - // if container is set to modal then let's make sure we set the options accordingly |
|
| 695 | - if (empty($data_id) && empty($data_class)) { |
|
| 696 | - $stop['options']['modal'] = true; |
|
| 697 | - $stop['options']['expose'] = true; |
|
| 698 | - } |
|
| 699 | - |
|
| 700 | - $custom_class = ! empty($stop['custom_class']) ? ' class="' . $stop['custom_class'] . '"' : ''; |
|
| 701 | - $button_text = ! empty($stop['button_text']) ? ' data-button="' . $stop['button_text'] . '"' : ''; |
|
| 702 | - $inner_content = isset($stop['content']) ? $stop['content'] : ''; |
|
| 703 | - |
|
| 704 | - // options |
|
| 705 | - if (isset($stop['options']) && is_array($stop['options'])) { |
|
| 706 | - $options = ' data-options="'; |
|
| 707 | - foreach ($stop['options'] as $option => $value) { |
|
| 708 | - $options .= $option . ':' . $value . ';'; |
|
| 709 | - } |
|
| 710 | - $options .= '"'; |
|
| 711 | - } else { |
|
| 712 | - $options = ''; |
|
| 713 | - } |
|
| 714 | - |
|
| 715 | - // let's put all together |
|
| 716 | - $content .= '<li' |
|
| 717 | - . $data_id |
|
| 718 | - . $data_class |
|
| 719 | - . $custom_class |
|
| 720 | - . $button_text |
|
| 721 | - . $options |
|
| 722 | - . '>' |
|
| 723 | - . $inner_content |
|
| 724 | - . '</li>'; |
|
| 725 | - } |
|
| 726 | - |
|
| 727 | - $content .= '</ol>'; |
|
| 728 | - return $content; |
|
| 729 | - } |
|
| 730 | - |
|
| 731 | - |
|
| 732 | - /** |
|
| 733 | - * This is a helper method to generate a status legend for a given status array. |
|
| 734 | - * Note this will only work if the incoming statuses have a key in the EEM_Status->localized_status() methods |
|
| 735 | - * status_array. |
|
| 736 | - * |
|
| 737 | - * @param array $status_array array of statuses that will make up the legend. In format: |
|
| 738 | - * array( |
|
| 739 | - * 'status_item' => 'status_name' |
|
| 740 | - * ) |
|
| 741 | - * @param string $active_status This is used to indicate what the active status is IF that is to be highlighted in |
|
| 742 | - * the legend. |
|
| 743 | - * @return string html structure for status. |
|
| 744 | - * @throws EE_Error |
|
| 745 | - */ |
|
| 746 | - public static function status_legend($status_array, $active_status = '') |
|
| 747 | - { |
|
| 748 | - if (! is_array($status_array)) { |
|
| 749 | - throw new EE_Error( |
|
| 750 | - esc_html__( |
|
| 751 | - 'The EEH_Template::status_legend helper required the incoming status_array argument to be an array!', |
|
| 752 | - 'event_espresso' |
|
| 753 | - ) |
|
| 754 | - ); |
|
| 755 | - } |
|
| 756 | - |
|
| 757 | - $content = ' |
|
| 54 | + /** |
|
| 55 | + * @var EE_Currency_Config[] |
|
| 56 | + */ |
|
| 57 | + private static $currency_config; |
|
| 58 | + |
|
| 59 | + /** |
|
| 60 | + * @var CurrencyFormatter |
|
| 61 | + */ |
|
| 62 | + private static $currency_formatter; |
|
| 63 | + |
|
| 64 | + /** |
|
| 65 | + * @var array |
|
| 66 | + */ |
|
| 67 | + private static $_espresso_themes = []; |
|
| 68 | + |
|
| 69 | + |
|
| 70 | + /** |
|
| 71 | + * get legacy currency config object for locale |
|
| 72 | + * |
|
| 73 | + * @param Locale $currency_locale |
|
| 74 | + * @return EE_Currency_Config |
|
| 75 | + * @throws EE_Error |
|
| 76 | + * @throws ReflectionException |
|
| 77 | + * @since $VID:$ |
|
| 78 | + */ |
|
| 79 | + private static function getCurrencyConfigForCountryISO(Locale $currency_locale) |
|
| 80 | + { |
|
| 81 | + $currencyISO = $currency_locale->currencyIsoCode(); |
|
| 82 | + if ( |
|
| 83 | + ! isset(EEH_Template::$currency_config[ $currencyISO ]) |
|
| 84 | + || ! EEH_Template::$currency_config[ $currencyISO ] instanceof EE_Currency_Config |
|
| 85 | + ) { |
|
| 86 | + $CNT_ISO = EEM_Country::instance()->getCountryISOForCurrencyISO($currencyISO); |
|
| 87 | + $currency_config = new EE_Currency_Config($CNT_ISO); |
|
| 88 | + EEH_Template::$currency_config[ $currencyISO ] = $currency_config; |
|
| 89 | + } |
|
| 90 | + return EEH_Template::$currency_config[ $currencyISO ]; |
|
| 91 | + } |
|
| 92 | + |
|
| 93 | + |
|
| 94 | + /** |
|
| 95 | + * @return CurrencyFormatter |
|
| 96 | + * @since $VID:$ |
|
| 97 | + */ |
|
| 98 | + private static function getCurrencyFormatter() |
|
| 99 | + { |
|
| 100 | + if (! EEH_Template::$currency_formatter instanceof CurrencyFormatter) { |
|
| 101 | + EEH_Template::$currency_formatter = LoaderFactory::getLoader()->getShared(CurrencyFormatter::class); |
|
| 102 | + } |
|
| 103 | + return EEH_Template::$currency_formatter; |
|
| 104 | + } |
|
| 105 | + |
|
| 106 | + |
|
| 107 | + /** |
|
| 108 | + * is_espresso_theme - returns TRUE or FALSE on whether the currently active WP theme is an espresso theme |
|
| 109 | + * |
|
| 110 | + * @return boolean |
|
| 111 | + */ |
|
| 112 | + public static function is_espresso_theme() |
|
| 113 | + { |
|
| 114 | + return wp_get_theme()->get('TextDomain') === 'event_espresso'; |
|
| 115 | + } |
|
| 116 | + |
|
| 117 | + |
|
| 118 | + /** |
|
| 119 | + * load_espresso_theme_functions - if current theme is an espresso theme, or uses ee theme template parts, then |
|
| 120 | + * load its functions.php file ( if not already loaded ) |
|
| 121 | + * |
|
| 122 | + * @return void |
|
| 123 | + */ |
|
| 124 | + public static function load_espresso_theme_functions() |
|
| 125 | + { |
|
| 126 | + if ( |
|
| 127 | + ! defined('EE_THEME_FUNCTIONS_LOADED') |
|
| 128 | + && is_readable(EE_PUBLIC . EE_Config::get_current_theme() . '/functions.php') |
|
| 129 | + ) { |
|
| 130 | + require_once(EE_PUBLIC . EE_Config::get_current_theme() . '/functions.php'); |
|
| 131 | + } |
|
| 132 | + } |
|
| 133 | + |
|
| 134 | + |
|
| 135 | + /** |
|
| 136 | + * get_espresso_themes - returns an array of Espresso Child themes located in the /templates/ directory |
|
| 137 | + * |
|
| 138 | + * @return array |
|
| 139 | + */ |
|
| 140 | + public static function get_espresso_themes() |
|
| 141 | + { |
|
| 142 | + if (empty(EEH_Template::$_espresso_themes)) { |
|
| 143 | + $espresso_themes = glob(EE_PUBLIC . '*', GLOB_ONLYDIR); |
|
| 144 | + if (empty($espresso_themes)) { |
|
| 145 | + return []; |
|
| 146 | + } |
|
| 147 | + if (($key = array_search('global_assets', $espresso_themes, true)) !== false) { |
|
| 148 | + unset($espresso_themes[ $key ]); |
|
| 149 | + } |
|
| 150 | + EEH_Template::$_espresso_themes = []; |
|
| 151 | + foreach ($espresso_themes as $espresso_theme) { |
|
| 152 | + EEH_Template::$_espresso_themes[ basename($espresso_theme) ] = $espresso_theme; |
|
| 153 | + } |
|
| 154 | + } |
|
| 155 | + return EEH_Template::$_espresso_themes; |
|
| 156 | + } |
|
| 157 | + |
|
| 158 | + |
|
| 159 | + /** |
|
| 160 | + * EEH_Template::get_template_part |
|
| 161 | + * basically a copy of the WordPress get_template_part() function but uses EEH_Template::locate_template() instead, |
|
| 162 | + * and doesn't add base versions of files so not a very useful function at all except that it adds familiarity PLUS |
|
| 163 | + * filtering based off of the entire template part name |
|
| 164 | + * |
|
| 165 | + * @param string $slug The slug name for the generic template. |
|
| 166 | + * @param string $name The name of the specialised template. |
|
| 167 | + * @param array $template_args |
|
| 168 | + * @param bool $return_string |
|
| 169 | + * @return void echos the html output for the template |
|
| 170 | + */ |
|
| 171 | + public static function get_template_part( |
|
| 172 | + $slug = null, |
|
| 173 | + $name = null, |
|
| 174 | + $template_args = [], |
|
| 175 | + $return_string = false |
|
| 176 | + ) { |
|
| 177 | + do_action("get_template_part_{$slug}-{$name}", $slug, $name); |
|
| 178 | + $templates = []; |
|
| 179 | + $name = (string) $name; |
|
| 180 | + if ($name !== '') { |
|
| 181 | + $templates[] = "{$slug}-{$name}.php"; |
|
| 182 | + } |
|
| 183 | + // allow template parts to be turned off via something like: |
|
| 184 | + // add_filter( 'FHEE__content_espresso_events_tickets_template__display_datetimes', '__return_false' ); |
|
| 185 | + if (apply_filters("FHEE__EEH_Template__get_template_part__display__{$slug}_{$name}", true)) { |
|
| 186 | + return EEH_Template::locate_template($templates, $template_args, true, $return_string); |
|
| 187 | + } |
|
| 188 | + return ''; |
|
| 189 | + } |
|
| 190 | + |
|
| 191 | + |
|
| 192 | + /** |
|
| 193 | + * locate_template |
|
| 194 | + * locate a template file by looking in the following places, in the following order: |
|
| 195 | + * <server path up to>/wp-content/themes/<current active WordPress theme>/ |
|
| 196 | + * <assumed full absolute server path> |
|
| 197 | + * <server path up to>/wp-content/uploads/espresso/templates/<current EE theme>/ |
|
| 198 | + * <server path up to>/wp-content/uploads/espresso/templates/ |
|
| 199 | + * <server path up to>/wp-content/plugins/<EE4 folder>/public/<current EE theme>/ |
|
| 200 | + * <server path up to>/wp-content/plugins/<EE4 folder>/core/templates/<current EE theme>/ |
|
| 201 | + * <server path up to>/wp-content/plugins/<EE4 folder>/ |
|
| 202 | + * as soon as the template is found in one of these locations, it will be returned or loaded |
|
| 203 | + * Example: |
|
| 204 | + * You are using the WordPress Twenty Sixteen theme, |
|
| 205 | + * and you want to customize the "some-event.template.php" template, |
|
| 206 | + * which is located in the "/relative/path/to/" folder relative to the main EE plugin folder. |
|
| 207 | + * Assuming WP is installed on your server in the "/home/public_html/" folder, |
|
| 208 | + * EEH_Template::locate_template() will look at the following paths in order until the template is found: |
|
| 209 | + * /home/public_html/wp-content/themes/twentysixteen/some-event.template.php |
|
| 210 | + * /relative/path/to/some-event.template.php |
|
| 211 | + * /home/public_html/wp-content/uploads/espresso/templates/Espresso_Arabica_2014/relative/path/to/some-event.template.php |
|
| 212 | + * /home/public_html/wp-content/uploads/espresso/templates/relative/path/to/some-event.template.php |
|
| 213 | + * /home/public_html/wp-content/plugins/event-espresso-core-reg/public/Espresso_Arabica_2014/relative/path/to/some-event.template.php |
|
| 214 | + * /home/public_html/wp-content/plugins/event-espresso-core-reg/core/templates/Espresso_Arabica_2014/relative/path/to/some-event.template.php |
|
| 215 | + * /home/public_html/wp-content/plugins/event-espresso-core-reg/relative/path/to/some-event.template.php |
|
| 216 | + * Had you passed an absolute path to your template that was in some other location, |
|
| 217 | + * ie: "/absolute/path/to/some-event.template.php" |
|
| 218 | + * then the search would have been : |
|
| 219 | + * /home/public_html/wp-content/themes/twentysixteen/some-event.template.php |
|
| 220 | + * /absolute/path/to/some-event.template.php |
|
| 221 | + * and stopped there upon finding it in the second location |
|
| 222 | + * |
|
| 223 | + * @param array|string $templates array of template file names including extension (or just a single string) |
|
| 224 | + * @param array $template_args an array of arguments to be extracted for use in the template |
|
| 225 | + * @param boolean $load whether to pass the located template path on to the |
|
| 226 | + * EEH_Template::display_template() method or simply return it |
|
| 227 | + * @param boolean $return_string whether to send output immediately to screen, or capture and return as a |
|
| 228 | + * string |
|
| 229 | + * @param boolean $check_if_custom If TRUE, this flags this method to return boolean for whether this will |
|
| 230 | + * generate a custom template or not. Used in places where you don't actually |
|
| 231 | + * load the template, you just want to know if there's a custom version of it. |
|
| 232 | + * @return mixed |
|
| 233 | + * @throws DomainException |
|
| 234 | + * @throws InvalidArgumentException |
|
| 235 | + * @throws InvalidDataTypeException |
|
| 236 | + * @throws InvalidInterfaceException |
|
| 237 | + */ |
|
| 238 | + public static function locate_template( |
|
| 239 | + $templates = [], |
|
| 240 | + $template_args = [], |
|
| 241 | + $load = true, |
|
| 242 | + $return_string = true, |
|
| 243 | + $check_if_custom = false |
|
| 244 | + ) { |
|
| 245 | + // first use WP locate_template to check for template in the current theme folder |
|
| 246 | + $template_path = locate_template($templates); |
|
| 247 | + |
|
| 248 | + if ($check_if_custom && ! empty($template_path)) { |
|
| 249 | + return true; |
|
| 250 | + } |
|
| 251 | + |
|
| 252 | + // not in the theme |
|
| 253 | + if (empty($template_path)) { |
|
| 254 | + // not even a template to look for ? |
|
| 255 | + if (empty($templates)) { |
|
| 256 | + $loader = LoaderFactory::getLoader(); |
|
| 257 | + /** @var RequestInterface $request */ |
|
| 258 | + $request = $loader->getShared(RequestInterface::class); |
|
| 259 | + // get post_type |
|
| 260 | + $post_type = $request->getRequestParam('post_type'); |
|
| 261 | + /** @var EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions $custom_post_types */ |
|
| 262 | + $custom_post_types = $loader->getShared( |
|
| 263 | + 'EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions' |
|
| 264 | + ); |
|
| 265 | + // get array of EE Custom Post Types |
|
| 266 | + $EE_CPTs = $custom_post_types->getDefinitions(); |
|
| 267 | + // build template name based on request |
|
| 268 | + if (isset($EE_CPTs[ $post_type ])) { |
|
| 269 | + $archive_or_single = is_archive() ? 'archive' : ''; |
|
| 270 | + $archive_or_single = is_single() ? 'single' : $archive_or_single; |
|
| 271 | + $templates = $archive_or_single . '-' . $post_type . '.php'; |
|
| 272 | + } |
|
| 273 | + } |
|
| 274 | + // currently active EE template theme |
|
| 275 | + $current_theme = EE_Config::get_current_theme(); |
|
| 276 | + |
|
| 277 | + // array of paths to folders that may contain templates |
|
| 278 | + $template_folder_paths = [ |
|
| 279 | + // first check the /wp-content/uploads/espresso/templates/(current EE theme)/ folder for an EE theme template file |
|
| 280 | + EVENT_ESPRESSO_TEMPLATE_DIR . $current_theme, |
|
| 281 | + // then in the root of the /wp-content/uploads/espresso/templates/ folder |
|
| 282 | + EVENT_ESPRESSO_TEMPLATE_DIR, |
|
| 283 | + ]; |
|
| 284 | + |
|
| 285 | + // add core plugin folders for checking only if we're not $check_if_custom |
|
| 286 | + if (! $check_if_custom) { |
|
| 287 | + $core_paths = [ |
|
| 288 | + // in the /wp-content/plugins/(EE4 folder)/public/(current EE theme)/ folder within the plugin |
|
| 289 | + EE_PUBLIC . $current_theme, |
|
| 290 | + // in the /wp-content/plugins/(EE4 folder)/core/templates/(current EE theme)/ folder within the plugin |
|
| 291 | + EE_TEMPLATES . $current_theme, |
|
| 292 | + // or maybe relative from the plugin root: /wp-content/plugins/(EE4 folder)/ |
|
| 293 | + EE_PLUGIN_DIR_PATH, |
|
| 294 | + ]; |
|
| 295 | + $template_folder_paths = array_merge($template_folder_paths, $core_paths); |
|
| 296 | + } |
|
| 297 | + |
|
| 298 | + // now filter that array |
|
| 299 | + $template_folder_paths = apply_filters( |
|
| 300 | + 'FHEE__EEH_Template__locate_template__template_folder_paths', |
|
| 301 | + $template_folder_paths |
|
| 302 | + ); |
|
| 303 | + $templates = is_array($templates) ? $templates : [$templates]; |
|
| 304 | + $template_folder_paths = is_array($template_folder_paths) |
|
| 305 | + ? $template_folder_paths |
|
| 306 | + : [$template_folder_paths]; |
|
| 307 | + // array to hold all possible template paths |
|
| 308 | + $full_template_paths = []; |
|
| 309 | + $file_name = ''; |
|
| 310 | + // loop through $templates |
|
| 311 | + foreach ($templates as $template) { |
|
| 312 | + // normalize directory separators |
|
| 313 | + $template = EEH_File::standardise_directory_separators($template); |
|
| 314 | + $file_name = basename($template); |
|
| 315 | + $template_path_minus_file_name = substr($template, 0, (strlen($file_name) * -1)); |
|
| 316 | + // while looping through all template folder paths |
|
| 317 | + foreach ($template_folder_paths as $template_folder_path) { |
|
| 318 | + // normalize directory separators |
|
| 319 | + $template_folder_path = EEH_File::standardise_directory_separators($template_folder_path); |
|
| 320 | + // determine if any common base path exists between the two paths |
|
| 321 | + $common_base_path = EEH_Template::_find_common_base_path( |
|
| 322 | + [$template_folder_path, $template_path_minus_file_name] |
|
| 323 | + ); |
|
| 324 | + if ($common_base_path !== '') { |
|
| 325 | + // both paths have a common base, so just tack the filename onto our search path |
|
| 326 | + $resolved_path = EEH_File::end_with_directory_separator($template_folder_path) . $file_name; |
|
| 327 | + } else { |
|
| 328 | + // no common base path, so let's just concatenate |
|
| 329 | + $resolved_path = EEH_File::end_with_directory_separator($template_folder_path) . $template; |
|
| 330 | + } |
|
| 331 | + // build up our template locations array by adding our resolved paths |
|
| 332 | + $full_template_paths[] = $resolved_path; |
|
| 333 | + } |
|
| 334 | + // if $template is an absolute path, then we'll tack it onto the start of our array so that it gets searched first |
|
| 335 | + array_unshift($full_template_paths, $template); |
|
| 336 | + // path to the directory of the current theme: /wp-content/themes/(current WP theme)/ |
|
| 337 | + array_unshift($full_template_paths, get_stylesheet_directory() . '/' . $file_name); |
|
| 338 | + } |
|
| 339 | + // filter final array of full template paths |
|
| 340 | + $full_template_paths = apply_filters( |
|
| 341 | + 'FHEE__EEH_Template__locate_template__full_template_paths', |
|
| 342 | + $full_template_paths, |
|
| 343 | + $file_name |
|
| 344 | + ); |
|
| 345 | + // now loop through our final array of template location paths and check each location |
|
| 346 | + foreach ((array) $full_template_paths as $full_template_path) { |
|
| 347 | + if (is_readable($full_template_path)) { |
|
| 348 | + $template_path = str_replace(['\\', '/'], DIRECTORY_SEPARATOR, $full_template_path); |
|
| 349 | + break; |
|
| 350 | + } |
|
| 351 | + } |
|
| 352 | + } |
|
| 353 | + |
|
| 354 | + // hook that can be used to display the full template path that will be used |
|
| 355 | + do_action('AHEE__EEH_Template__locate_template__full_template_path', $template_path); |
|
| 356 | + |
|
| 357 | + // if we got it and you want to see it... |
|
| 358 | + if ($template_path && $load && ! $check_if_custom) { |
|
| 359 | + if ($return_string) { |
|
| 360 | + return EEH_Template::display_template($template_path, $template_args, true); |
|
| 361 | + } |
|
| 362 | + EEH_Template::display_template($template_path, $template_args); |
|
| 363 | + } |
|
| 364 | + return $check_if_custom && ! empty($template_path) ? true : $template_path; |
|
| 365 | + } |
|
| 366 | + |
|
| 367 | + |
|
| 368 | + /** |
|
| 369 | + * _find_common_base_path |
|
| 370 | + * given two paths, this determines if there is a common base path between the two |
|
| 371 | + * |
|
| 372 | + * @param array $paths |
|
| 373 | + * @return string |
|
| 374 | + */ |
|
| 375 | + protected static function _find_common_base_path($paths) |
|
| 376 | + { |
|
| 377 | + $last_offset = 0; |
|
| 378 | + $common_base_path = ''; |
|
| 379 | + while (($index = strpos($paths[0], '/', $last_offset)) !== false) { |
|
| 380 | + $dir_length = $index - $last_offset + 1; |
|
| 381 | + $directory = substr($paths[0], $last_offset, $dir_length); |
|
| 382 | + foreach ($paths as $path) { |
|
| 383 | + if (substr($path, $last_offset, $dir_length) !== $directory) { |
|
| 384 | + return $common_base_path; |
|
| 385 | + } |
|
| 386 | + } |
|
| 387 | + $common_base_path .= $directory; |
|
| 388 | + $last_offset = $index + 1; |
|
| 389 | + } |
|
| 390 | + return substr($common_base_path, 0, -1); |
|
| 391 | + } |
|
| 392 | + |
|
| 393 | + |
|
| 394 | + /** |
|
| 395 | + * load and display a template |
|
| 396 | + * |
|
| 397 | + * @param bool|string $template_path server path to the file to be loaded, including file name and extension |
|
| 398 | + * @param array $template_args an array of arguments to be extracted for use in the template |
|
| 399 | + * @param boolean $return_string whether to send output immediately to screen, or capture and return as a |
|
| 400 | + * string |
|
| 401 | + * @param bool $throw_exceptions if set to true, will throw an exception if the template is either |
|
| 402 | + * not found or is not readable |
|
| 403 | + * @return string |
|
| 404 | + * @throws DomainException |
|
| 405 | + */ |
|
| 406 | + public static function display_template( |
|
| 407 | + $template_path = false, |
|
| 408 | + $template_args = [], |
|
| 409 | + $return_string = false, |
|
| 410 | + $throw_exceptions = false |
|
| 411 | + ) { |
|
| 412 | + /** |
|
| 413 | + * These two filters are intended for last minute changes to templates being loaded and/or template arg |
|
| 414 | + * modifications. NOTE... modifying these things can cause breakage as most templates running through |
|
| 415 | + * the display_template method are templates we DON'T want modified (usually because of js |
|
| 416 | + * dependencies etc). So unless you know what you are doing, do NOT filter templates or template args |
|
| 417 | + * using this. |
|
| 418 | + * |
|
| 419 | + * @since 4.6.0 |
|
| 420 | + */ |
|
| 421 | + $template_path = (string) apply_filters('FHEE__EEH_Template__display_template__template_path', $template_path); |
|
| 422 | + $template_args = (array) apply_filters('FHEE__EEH_Template__display_template__template_args', $template_args); |
|
| 423 | + |
|
| 424 | + // you gimme nuttin - YOU GET NUTTIN !! |
|
| 425 | + if (! $template_path || ! is_readable($template_path)) { |
|
| 426 | + // ignore whether template is accessible ? |
|
| 427 | + if ($throw_exceptions) { |
|
| 428 | + throw new DomainException( |
|
| 429 | + esc_html__('Invalid, unreadable, or missing file.', 'event_espresso') |
|
| 430 | + ); |
|
| 431 | + } |
|
| 432 | + return ''; |
|
| 433 | + } |
|
| 434 | + // if $template_args are not in an array, then make it so |
|
| 435 | + if (! is_array($template_args) && ! is_object($template_args)) { |
|
| 436 | + $template_args = [$template_args]; |
|
| 437 | + } |
|
| 438 | + extract($template_args, EXTR_SKIP); |
|
| 439 | + // ignore whether template is accessible ? |
|
| 440 | + if ($throw_exceptions && ! is_readable($template_path)) { |
|
| 441 | + throw new DomainException( |
|
| 442 | + esc_html__( |
|
| 443 | + 'Invalid, unreadable, or missing file.', |
|
| 444 | + 'event_espresso' |
|
| 445 | + ) |
|
| 446 | + ); |
|
| 447 | + } |
|
| 448 | + |
|
| 449 | + |
|
| 450 | + if ($return_string) { |
|
| 451 | + // because we want to return a string, we are going to capture the output |
|
| 452 | + ob_start(); |
|
| 453 | + include($template_path); |
|
| 454 | + return ob_get_clean(); |
|
| 455 | + } |
|
| 456 | + include($template_path); |
|
| 457 | + return ''; |
|
| 458 | + } |
|
| 459 | + |
|
| 460 | + |
|
| 461 | + /** |
|
| 462 | + * get_object_css_class - attempts to generate a css class based on the type of EE object passed |
|
| 463 | + * |
|
| 464 | + * @param EE_Base_Class $object the EE object the css class is being generated for |
|
| 465 | + * @param string $prefix added to the beginning of the generated class |
|
| 466 | + * @param string $suffix added to the end of the generated class |
|
| 467 | + * @return string |
|
| 468 | + * @throws EE_Error |
|
| 469 | + * @throws ReflectionException |
|
| 470 | + */ |
|
| 471 | + public static function get_object_css_class($object = null, $prefix = '', $suffix = '') |
|
| 472 | + { |
|
| 473 | + // in the beginning... |
|
| 474 | + $prefix = ! empty($prefix) ? rtrim($prefix, '-') . '-' : ''; |
|
| 475 | + // da muddle |
|
| 476 | + $class = ''; |
|
| 477 | + // the end |
|
| 478 | + $suffix = ! empty($suffix) ? '-' . ltrim($suffix, '-') : ''; |
|
| 479 | + // is the passed object an EE object ? |
|
| 480 | + if ($object instanceof EE_Base_Class) { |
|
| 481 | + // grab the exact type of object |
|
| 482 | + $obj_class = get_class($object); |
|
| 483 | + // depending on the type of object... |
|
| 484 | + $class = strtolower(str_replace('_', '-', $obj_class)); |
|
| 485 | + $class .= method_exists($obj_class, 'name') ? '-' . sanitize_title($object->name()) : ''; |
|
| 486 | + } |
|
| 487 | + return $prefix . $class . $suffix; |
|
| 488 | + } |
|
| 489 | + |
|
| 490 | + |
|
| 491 | + /** |
|
| 492 | + * EEH_Template::format_currency |
|
| 493 | + * This helper takes a raw float value and formats it according to the default config country currency settings, or |
|
| 494 | + * the country currency settings from the supplied country ISO code |
|
| 495 | + * |
|
| 496 | + * @param float $amount raw money value |
|
| 497 | + * @param boolean $return_raw whether to return the formatted float value only with no currency sign |
|
| 498 | + * or code |
|
| 499 | + * @param boolean $display_code whether to display the country code (USD). Default = TRUE |
|
| 500 | + * @param string $CNT_ISO 2 letter ISO code for a country |
|
| 501 | + * @param string $cur_code_span_class |
|
| 502 | + * @param boolean $allow_fractional_subunits whether to allow displaying partial penny amounts |
|
| 503 | + * @return string the html output for the formatted money value |
|
| 504 | + * @throws EE_Error |
|
| 505 | + * @throws ReflectionException |
|
| 506 | + * @deprecated $VID:$ plz use CurrencyFormatter::formatForLocale() |
|
| 507 | + */ |
|
| 508 | + public static function format_currency( |
|
| 509 | + $amount = null, |
|
| 510 | + $return_raw = false, |
|
| 511 | + $display_code = true, |
|
| 512 | + $CNT_ISO = '', |
|
| 513 | + $cur_code_span_class = 'currency-code', // not used anywhere |
|
| 514 | + $allow_fractional_subunits = false |
|
| 515 | + ) { |
|
| 516 | + // ensure amount was received |
|
| 517 | + if ($amount === null) { |
|
| 518 | + $msg = esc_html__('In order to format currency, an amount needs to be passed.', 'event_espresso'); |
|
| 519 | + EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__); |
|
| 520 | + return ''; |
|
| 521 | + } |
|
| 522 | + // ensure amount is float |
|
| 523 | + $amount = (float) apply_filters('FHEE__EEH_Template__format_currency__raw_amount', (float) $amount); |
|
| 524 | + $CNT_ISO = apply_filters('FHEE__EEH_Template__format_currency__CNT_ISO', $CNT_ISO, $amount); |
|
| 525 | + |
|
| 526 | + // filter raw amount (allows 0.00 to be changed to "free" for example) |
|
| 527 | + $amount_formatted = apply_filters('FHEE__EEH_Template__format_currency__amount', $amount, $return_raw); |
|
| 528 | + // still a number or was amount converted to a string like "free" ? |
|
| 529 | + if (! is_float($amount_formatted)) { |
|
| 530 | + return esc_html($amount_formatted); |
|
| 531 | + } |
|
| 532 | + |
|
| 533 | + try { |
|
| 534 | + $currency_formatter = EEH_Template::getCurrencyFormatter(); |
|
| 535 | + $currency_ISO = ''; |
|
| 536 | + if ($CNT_ISO !== '') { |
|
| 537 | + $currency_config = EEH_Money::get_currency_config($CNT_ISO); |
|
| 538 | + $currency_ISO = $currency_config->code; |
|
| 539 | + } |
|
| 540 | + $currency_locale = $currency_formatter->getLocaleForCurrencyISO($currency_ISO, true); |
|
| 541 | + |
|
| 542 | + // filter to allow global setting of display_code |
|
| 543 | + $display_code = apply_filters('FHEE__EEH_Template__format_currency__display_code', $display_code); |
|
| 544 | + |
|
| 545 | + $format = CurrencyFormatter::FORMAT_LOCALIZED_CURRENCY; |
|
| 546 | + if ($return_raw) { |
|
| 547 | + $format = CurrencyFormatter::FORMAT_LOCALIZED_FLOAT; |
|
| 548 | + } elseif ($display_code) { |
|
| 549 | + $format = CurrencyFormatter::FORMAT_LOCALIZED_CURRENCY_HTML_CODE; |
|
| 550 | + } |
|
| 551 | + // don't like this but it maintains backwards compatibility with how things were done before |
|
| 552 | + $precision = $allow_fractional_subunits |
|
| 553 | + ? $currency_locale->decimalPrecision() + 2 |
|
| 554 | + : $currency_locale->decimalPrecision(); |
|
| 555 | + |
|
| 556 | + $amount_formatted = $currency_formatter->formatForLocale( |
|
| 557 | + $amount_formatted, |
|
| 558 | + $format, |
|
| 559 | + $currency_locale->name(), |
|
| 560 | + $precision |
|
| 561 | + ); |
|
| 562 | + |
|
| 563 | + // filter results |
|
| 564 | + $amount_formatted = apply_filters( |
|
| 565 | + 'FHEE__EEH_Template__format_currency__amount_formatted', |
|
| 566 | + $amount_formatted, |
|
| 567 | + EEH_Template::getCurrencyConfigForCountryISO($currency_locale), |
|
| 568 | + $return_raw, |
|
| 569 | + $display_code |
|
| 570 | + ); |
|
| 571 | + } catch (Exception $e) { |
|
| 572 | + // eat exception |
|
| 573 | + } |
|
| 574 | + // return formatted currency amount |
|
| 575 | + return esc_html($amount_formatted); |
|
| 576 | + } |
|
| 577 | + |
|
| 578 | + |
|
| 579 | + /** |
|
| 580 | + * This function is used for outputting the localized label for a given status id in the schema requested (and |
|
| 581 | + * possibly plural). The intended use of this function is only for cases where wanting a label outside of a |
|
| 582 | + * related status model or model object (i.e. in documentation etc.) |
|
| 583 | + * |
|
| 584 | + * @param string $status_id Status ID matching a registered status in the esp_status table. If there is no |
|
| 585 | + * match, then 'Unknown' will be returned. |
|
| 586 | + * @param boolean $plural Whether to return plural or not |
|
| 587 | + * @param string $schema 'UPPER', 'lower', or 'Sentence' |
|
| 588 | + * @return string The localized label for the status id. |
|
| 589 | + * @throws EE_Error |
|
| 590 | + */ |
|
| 591 | + public static function pretty_status($status_id, $plural = false, $schema = 'upper') |
|
| 592 | + { |
|
| 593 | + $status = EEM_Status::instance()->localized_status( |
|
| 594 | + [$status_id => esc_html__('unknown', 'event_espresso')], |
|
| 595 | + $plural, |
|
| 596 | + $schema |
|
| 597 | + ); |
|
| 598 | + return $status[ $status_id ]; |
|
| 599 | + } |
|
| 600 | + |
|
| 601 | + |
|
| 602 | + /** |
|
| 603 | + * This helper just returns a button or link for the given parameters |
|
| 604 | + * |
|
| 605 | + * @param string $url the url for the link, note that `esc_url` will be called on it |
|
| 606 | + * @param string $label What is the label you want displayed for the button |
|
| 607 | + * @param string $class what class is used for the button (defaults to 'button-primary') |
|
| 608 | + * @param string $icon |
|
| 609 | + * @param string $title |
|
| 610 | + * @return string the html output for the button |
|
| 611 | + */ |
|
| 612 | + public static function get_button_or_link($url, $label, $class = 'button button--primary', $icon = '', $title = '') |
|
| 613 | + { |
|
| 614 | + $icon_html = ''; |
|
| 615 | + if (! empty($icon)) { |
|
| 616 | + $dashicons = preg_split("(ee-icon |dashicons )", $icon); |
|
| 617 | + $dashicons = array_filter($dashicons); |
|
| 618 | + $count = count($dashicons); |
|
| 619 | + $icon_html .= $count > 1 ? '<span class="ee-composite-dashicon">' : ''; |
|
| 620 | + foreach ($dashicons as $dashicon) { |
|
| 621 | + $type = strpos($dashicon, 'ee-icon') !== false ? 'ee-icon ' : 'dashicons '; |
|
| 622 | + $icon_html .= '<span class="' . $type . $dashicon . '"></span>'; |
|
| 623 | + } |
|
| 624 | + $icon_html .= $count > 1 ? '</span>' : ''; |
|
| 625 | + } |
|
| 626 | + // sanitize & escape |
|
| 627 | + $id = sanitize_title_with_dashes($label); |
|
| 628 | + $url = esc_url_raw($url); |
|
| 629 | + $class = esc_attr($class); |
|
| 630 | + $title = esc_attr($title); |
|
| 631 | + $label = esc_html($label); |
|
| 632 | + return "<a id='{$id}' href='{$url}' class='{$class}' title='{$title}'>{$icon_html}{$label}</a>"; |
|
| 633 | + } |
|
| 634 | + |
|
| 635 | + |
|
| 636 | + /** |
|
| 637 | + * This returns a generated link that will load the related help tab on admin pages. |
|
| 638 | + * |
|
| 639 | + * @param string $help_tab_id the id for the connected help tab |
|
| 640 | + * @param bool|string $page The page identifier for the page the help tab is on |
|
| 641 | + * @param bool|string $action The action (route) for the admin page the help tab is on. |
|
| 642 | + * @param bool|string $icon_style (optional) include css class for the style you want to use for the help icon. |
|
| 643 | + * @param bool|string $help_text (optional) send help text you want to use for the link if default not to be used |
|
| 644 | + * @return string generated link |
|
| 645 | + */ |
|
| 646 | + public static function get_help_tab_link( |
|
| 647 | + $help_tab_id, |
|
| 648 | + $page = false, |
|
| 649 | + $action = false, |
|
| 650 | + $icon_style = false, |
|
| 651 | + $help_text = false |
|
| 652 | + ) { |
|
| 653 | + $allowedtags = AllowedTags::getAllowedTags(); |
|
| 654 | + /** @var RequestInterface $request */ |
|
| 655 | + $request = LoaderFactory::getLoader()->getShared(RequestInterface::class); |
|
| 656 | + $page = $page ?: $request->getRequestParam('page', '', 'key'); |
|
| 657 | + $action = $action ?: $request->getRequestParam('action', 'default', 'key'); |
|
| 658 | + |
|
| 659 | + |
|
| 660 | + $help_tab_lnk = $page . '-' . $action . '-' . $help_tab_id; |
|
| 661 | + $icon = $icon_style ? $icon_style : ' dashicons-editor-help'; |
|
| 662 | + $help_text = $help_text ? $help_text : ''; |
|
| 663 | + $link = '<a id="' . $help_tab_lnk . '"'; |
|
| 664 | + $link .= ' class="ee-clickable espresso-help-tab-lnk dashicons' . $icon . '"'; |
|
| 665 | + $link .= ' title="' . esc_attr__( |
|
| 666 | + 'Click to open the \'Help\' tab for more information about this feature.', |
|
| 667 | + 'event_espresso' |
|
| 668 | + ) . '"'; |
|
| 669 | + $link .= ' > ' . $help_text . ' </a>'; |
|
| 670 | + return $link; |
|
| 671 | + } |
|
| 672 | + |
|
| 673 | + |
|
| 674 | + /** |
|
| 675 | + * This helper generates the html structure for the jquery joyride plugin with the given params. |
|
| 676 | + * |
|
| 677 | + * @link http://zurb.com/playground/jquery-joyride-feature-tour-plugin |
|
| 678 | + * @see EE_Admin_Page->_stop_callback() for the construct expected for the $stops param. |
|
| 679 | + * @param EE_Help_Tour |
|
| 680 | + * @return string html |
|
| 681 | + * @throws EE_Error |
|
| 682 | + */ |
|
| 683 | + public static function help_tour_stops_generator(EE_Help_Tour $tour) |
|
| 684 | + { |
|
| 685 | + $id = $tour->get_slug(); |
|
| 686 | + $stops = $tour->get_stops(); |
|
| 687 | + |
|
| 688 | + $content = '<ol style="display:none" id="' . $id . '">'; |
|
| 689 | + |
|
| 690 | + foreach ($stops as $stop) { |
|
| 691 | + $data_id = ! empty($stop['id']) ? ' data-id="' . $stop['id'] . '"' : ''; |
|
| 692 | + $data_class = empty($data_id) && ! empty($stop['class']) ? ' data-class="' . $stop['class'] . '"' : ''; |
|
| 693 | + |
|
| 694 | + // if container is set to modal then let's make sure we set the options accordingly |
|
| 695 | + if (empty($data_id) && empty($data_class)) { |
|
| 696 | + $stop['options']['modal'] = true; |
|
| 697 | + $stop['options']['expose'] = true; |
|
| 698 | + } |
|
| 699 | + |
|
| 700 | + $custom_class = ! empty($stop['custom_class']) ? ' class="' . $stop['custom_class'] . '"' : ''; |
|
| 701 | + $button_text = ! empty($stop['button_text']) ? ' data-button="' . $stop['button_text'] . '"' : ''; |
|
| 702 | + $inner_content = isset($stop['content']) ? $stop['content'] : ''; |
|
| 703 | + |
|
| 704 | + // options |
|
| 705 | + if (isset($stop['options']) && is_array($stop['options'])) { |
|
| 706 | + $options = ' data-options="'; |
|
| 707 | + foreach ($stop['options'] as $option => $value) { |
|
| 708 | + $options .= $option . ':' . $value . ';'; |
|
| 709 | + } |
|
| 710 | + $options .= '"'; |
|
| 711 | + } else { |
|
| 712 | + $options = ''; |
|
| 713 | + } |
|
| 714 | + |
|
| 715 | + // let's put all together |
|
| 716 | + $content .= '<li' |
|
| 717 | + . $data_id |
|
| 718 | + . $data_class |
|
| 719 | + . $custom_class |
|
| 720 | + . $button_text |
|
| 721 | + . $options |
|
| 722 | + . '>' |
|
| 723 | + . $inner_content |
|
| 724 | + . '</li>'; |
|
| 725 | + } |
|
| 726 | + |
|
| 727 | + $content .= '</ol>'; |
|
| 728 | + return $content; |
|
| 729 | + } |
|
| 730 | + |
|
| 731 | + |
|
| 732 | + /** |
|
| 733 | + * This is a helper method to generate a status legend for a given status array. |
|
| 734 | + * Note this will only work if the incoming statuses have a key in the EEM_Status->localized_status() methods |
|
| 735 | + * status_array. |
|
| 736 | + * |
|
| 737 | + * @param array $status_array array of statuses that will make up the legend. In format: |
|
| 738 | + * array( |
|
| 739 | + * 'status_item' => 'status_name' |
|
| 740 | + * ) |
|
| 741 | + * @param string $active_status This is used to indicate what the active status is IF that is to be highlighted in |
|
| 742 | + * the legend. |
|
| 743 | + * @return string html structure for status. |
|
| 744 | + * @throws EE_Error |
|
| 745 | + */ |
|
| 746 | + public static function status_legend($status_array, $active_status = '') |
|
| 747 | + { |
|
| 748 | + if (! is_array($status_array)) { |
|
| 749 | + throw new EE_Error( |
|
| 750 | + esc_html__( |
|
| 751 | + 'The EEH_Template::status_legend helper required the incoming status_array argument to be an array!', |
|
| 752 | + 'event_espresso' |
|
| 753 | + ) |
|
| 754 | + ); |
|
| 755 | + } |
|
| 756 | + |
|
| 757 | + $content = ' |
|
| 758 | 758 | <div class="ee-list-table-legend-container"> |
| 759 | 759 | <h4 class="status-legend-title"> |
| 760 | 760 | ' . esc_html__('Status Legend', 'event_espresso') . ' |
| 761 | 761 | </h4> |
| 762 | 762 | <dl class="ee-list-table-legend">'; |
| 763 | 763 | |
| 764 | - foreach ($status_array as $item => $status) { |
|
| 765 | - $active_class = $active_status == $status ? 'class="ee-is-active-status"' : ''; |
|
| 766 | - $content .= ' |
|
| 764 | + foreach ($status_array as $item => $status) { |
|
| 765 | + $active_class = $active_status == $status ? 'class="ee-is-active-status"' : ''; |
|
| 766 | + $content .= ' |
|
| 767 | 767 | <dt id="' . esc_attr('ee-legend-item-tooltip-' . $item) . '" ' . $active_class . '> |
| 768 | 768 | <span class="' . esc_attr('ee-status-legend ee-status-legend-' . $status) . '"></span> |
| 769 | 769 | <span class="ee-legend-description"> |
| 770 | 770 | ' . EEH_Template::pretty_status($status, false, 'sentence') . ' |
| 771 | 771 | </span> |
| 772 | 772 | </dt>'; |
| 773 | - } |
|
| 773 | + } |
|
| 774 | 774 | |
| 775 | - $content .= ' |
|
| 775 | + $content .= ' |
|
| 776 | 776 | </dl> |
| 777 | 777 | </div> |
| 778 | 778 | '; |
| 779 | - return $content; |
|
| 780 | - } |
|
| 781 | - |
|
| 782 | - |
|
| 783 | - /** |
|
| 784 | - * Gets HTML for laying out a deeply-nested array (and objects) in a format |
|
| 785 | - * that's nice for presenting in the wp admin |
|
| 786 | - * |
|
| 787 | - * @param mixed $data |
|
| 788 | - * @return string |
|
| 789 | - */ |
|
| 790 | - public static function layout_array_as_table($data) |
|
| 791 | - { |
|
| 792 | - if (is_object($data) || $data instanceof __PHP_Incomplete_Class) { |
|
| 793 | - $data = (array) $data; |
|
| 794 | - } |
|
| 795 | - ob_start(); |
|
| 796 | - if (is_array($data)) { |
|
| 797 | - if (EEH_Array::is_associative_array($data)) { ?> |
|
| 779 | + return $content; |
|
| 780 | + } |
|
| 781 | + |
|
| 782 | + |
|
| 783 | + /** |
|
| 784 | + * Gets HTML for laying out a deeply-nested array (and objects) in a format |
|
| 785 | + * that's nice for presenting in the wp admin |
|
| 786 | + * |
|
| 787 | + * @param mixed $data |
|
| 788 | + * @return string |
|
| 789 | + */ |
|
| 790 | + public static function layout_array_as_table($data) |
|
| 791 | + { |
|
| 792 | + if (is_object($data) || $data instanceof __PHP_Incomplete_Class) { |
|
| 793 | + $data = (array) $data; |
|
| 794 | + } |
|
| 795 | + ob_start(); |
|
| 796 | + if (is_array($data)) { |
|
| 797 | + if (EEH_Array::is_associative_array($data)) { ?> |
|
| 798 | 798 | <table class="widefat"> |
| 799 | 799 | <tbody> |
| 800 | 800 | <?php foreach ($data as $data_key => $data_values) { ?> |
@@ -804,7 +804,7 @@ discard block |
||
| 804 | 804 | </td> |
| 805 | 805 | <td> |
| 806 | 806 | <?php |
| 807 | - echo EEH_Template::layout_array_as_table($data_values); ?> |
|
| 807 | + echo EEH_Template::layout_array_as_table($data_values); ?> |
|
| 808 | 808 | </td> |
| 809 | 809 | </tr> |
| 810 | 810 | <?php } ?> |
@@ -813,292 +813,292 @@ discard block |
||
| 813 | 813 | <?php } else { ?> |
| 814 | 814 | <ul> |
| 815 | 815 | <?php |
| 816 | - foreach ($data as $datum) { |
|
| 817 | - echo "<li>"; |
|
| 818 | - echo EEH_Template::layout_array_as_table($datum); |
|
| 819 | - echo "</li>"; |
|
| 820 | - } ?> |
|
| 816 | + foreach ($data as $datum) { |
|
| 817 | + echo "<li>"; |
|
| 818 | + echo EEH_Template::layout_array_as_table($datum); |
|
| 819 | + echo "</li>"; |
|
| 820 | + } ?> |
|
| 821 | 821 | </ul> |
| 822 | 822 | <?php } |
| 823 | - } else { |
|
| 824 | - // simple value |
|
| 825 | - echo esc_html($data); |
|
| 826 | - } |
|
| 827 | - return ob_get_clean(); |
|
| 828 | - } |
|
| 829 | - |
|
| 830 | - |
|
| 831 | - /** |
|
| 832 | - * wrapper for EEH_Template::get_paging_html() that simply echos the generated paging html |
|
| 833 | - * |
|
| 834 | - * @param $total_items |
|
| 835 | - * @param $current |
|
| 836 | - * @param $per_page |
|
| 837 | - * @param $url |
|
| 838 | - * @param bool $show_num_field |
|
| 839 | - * @param string $paged_arg_name |
|
| 840 | - * @param array $items_label |
|
| 841 | - * @see self:get_paging_html() for argument docs. |
|
| 842 | - * @since 4.4.0 |
|
| 843 | - */ |
|
| 844 | - public static function paging_html( |
|
| 845 | - $total_items, |
|
| 846 | - $current, |
|
| 847 | - $per_page, |
|
| 848 | - $url, |
|
| 849 | - $show_num_field = true, |
|
| 850 | - $paged_arg_name = 'paged', |
|
| 851 | - $items_label = [] |
|
| 852 | - ) { |
|
| 853 | - echo EEH_Template::get_paging_html( |
|
| 854 | - $total_items, |
|
| 855 | - $current, |
|
| 856 | - $per_page, |
|
| 857 | - $url, |
|
| 858 | - $show_num_field, |
|
| 859 | - $paged_arg_name, |
|
| 860 | - $items_label |
|
| 861 | - ); |
|
| 862 | - } |
|
| 863 | - |
|
| 864 | - |
|
| 865 | - /** |
|
| 866 | - * A method for generating paging similar to WP_List_Table |
|
| 867 | - * |
|
| 868 | - * @param integer $total_items How many total items there are to page. |
|
| 869 | - * @param integer $current What the current page is. |
|
| 870 | - * @param integer $per_page How many items per page. |
|
| 871 | - * @param string $url What the base url for page links is. |
|
| 872 | - * @param boolean $show_num_field Whether to show the input for changing page number. |
|
| 873 | - * @param string $paged_arg_name The name of the key for the paged query argument. |
|
| 874 | - * @param array $items_label An array of singular/plural values for the items label: |
|
| 875 | - * array( |
|
| 876 | - * 'single' => 'item', |
|
| 877 | - * 'plural' => 'items' |
|
| 878 | - * ) |
|
| 879 | - * @return string |
|
| 880 | - * @since 4.4.0 |
|
| 881 | - * @see wp-admin/includes/class-wp-list-table.php WP_List_Table::pagination() |
|
| 882 | - */ |
|
| 883 | - public static function get_paging_html( |
|
| 884 | - $total_items, |
|
| 885 | - $current, |
|
| 886 | - $per_page, |
|
| 887 | - $url, |
|
| 888 | - $show_num_field = true, |
|
| 889 | - $paged_arg_name = 'paged', |
|
| 890 | - $items_label = [] |
|
| 891 | - ) { |
|
| 892 | - $page_links = []; |
|
| 893 | - $disable_first = $disable_last = ''; |
|
| 894 | - $total_items = (int) $total_items; |
|
| 895 | - $per_page = (int) $per_page; |
|
| 896 | - $current = (int) $current; |
|
| 897 | - $paged_arg_name = empty($paged_arg_name) ? 'paged' : sanitize_key($paged_arg_name); |
|
| 898 | - |
|
| 899 | - // filter items_label |
|
| 900 | - $items_label = apply_filters( |
|
| 901 | - 'FHEE__EEH_Template__get_paging_html__items_label', |
|
| 902 | - $items_label |
|
| 903 | - ); |
|
| 904 | - |
|
| 905 | - if ( |
|
| 906 | - empty($items_label) |
|
| 907 | - || ! is_array($items_label) |
|
| 908 | - || ! isset($items_label['single'], $items_label['plural']) |
|
| 909 | - ) { |
|
| 910 | - $items_label = [ |
|
| 911 | - 'single' => esc_html__('1 item', 'event_espresso'), |
|
| 912 | - 'plural' => esc_html__('%s items', 'event_espresso'), |
|
| 913 | - ]; |
|
| 914 | - } else { |
|
| 915 | - $items_label = [ |
|
| 916 | - 'single' => '1 ' . esc_html($items_label['single']), |
|
| 917 | - 'plural' => '%s ' . esc_html($items_label['plural']), |
|
| 918 | - ]; |
|
| 919 | - } |
|
| 920 | - |
|
| 921 | - $total_pages = ceil($total_items / $per_page); |
|
| 922 | - |
|
| 923 | - if ($total_pages <= 1) { |
|
| 924 | - return ''; |
|
| 925 | - } |
|
| 926 | - |
|
| 927 | - $item_label = $total_items > 1 ? sprintf($items_label['plural'], $total_items) : $items_label['single']; |
|
| 928 | - |
|
| 929 | - $output = '<span class="displaying-num">' . $item_label . '</span>'; |
|
| 930 | - |
|
| 931 | - if ($current === 1) { |
|
| 932 | - $disable_first = ' disabled'; |
|
| 933 | - } |
|
| 934 | - if ($current === $total_pages) { |
|
| 935 | - $disable_last = ' disabled'; |
|
| 936 | - } |
|
| 937 | - |
|
| 938 | - $page_links[] = sprintf( |
|
| 939 | - "<a class='%s' title='%s' href='%s'>%s</a>", |
|
| 940 | - 'first-page' . $disable_first, |
|
| 941 | - esc_attr__('Go to the first page', 'event_espresso'), |
|
| 942 | - esc_url_raw(remove_query_arg($paged_arg_name, $url)), |
|
| 943 | - '«' |
|
| 944 | - ); |
|
| 945 | - |
|
| 946 | - $page_links[] = sprintf( |
|
| 947 | - '<a class="%s" title="%s" href="%s">%s</a>', |
|
| 948 | - 'prev-page' . $disable_first, |
|
| 949 | - esc_attr__('Go to the previous page', 'event_espresso'), |
|
| 950 | - esc_url_raw(add_query_arg($paged_arg_name, max(1, $current - 1), $url)), |
|
| 951 | - '‹' |
|
| 952 | - ); |
|
| 953 | - |
|
| 954 | - if (! $show_num_field) { |
|
| 955 | - $html_current_page = $current; |
|
| 956 | - } else { |
|
| 957 | - $html_current_page = sprintf( |
|
| 958 | - "<input class='current-page' title='%s' type='text' name=$paged_arg_name value='%s' size='%d' />", |
|
| 959 | - esc_attr__('Current page', 'event_espresso'), |
|
| 960 | - esc_attr($current), |
|
| 961 | - strlen($total_pages) |
|
| 962 | - ); |
|
| 963 | - } |
|
| 964 | - |
|
| 965 | - $html_total_pages = sprintf( |
|
| 966 | - '<span class="total-pages">%s</span>', |
|
| 967 | - number_format_i18n($total_pages) |
|
| 968 | - ); |
|
| 969 | - $page_links[] = sprintf( |
|
| 970 | - _x('%3$s%1$s of %2$s%4$s', 'paging', 'event_espresso'), |
|
| 971 | - $html_current_page, |
|
| 972 | - $html_total_pages, |
|
| 973 | - '<span class="paging-input">', |
|
| 974 | - '</span>' |
|
| 975 | - ); |
|
| 976 | - |
|
| 977 | - $page_links[] = sprintf( |
|
| 978 | - '<a class="%s" title="%s" href="%s">%s</a>', |
|
| 979 | - 'next-page' . $disable_last, |
|
| 980 | - esc_attr__('Go to the next page', 'event_espresso'), |
|
| 981 | - esc_url_raw(add_query_arg($paged_arg_name, min($total_pages, $current + 1), $url)), |
|
| 982 | - '›' |
|
| 983 | - ); |
|
| 984 | - |
|
| 985 | - $page_links[] = sprintf( |
|
| 986 | - '<a class="%s" title="%s" href="%s">%s</a>', |
|
| 987 | - 'last-page' . $disable_last, |
|
| 988 | - esc_attr__('Go to the last page', 'event_espresso'), |
|
| 989 | - esc_url_raw(add_query_arg($paged_arg_name, $total_pages, $url)), |
|
| 990 | - '»' |
|
| 991 | - ); |
|
| 992 | - |
|
| 993 | - $output .= "\n" . '<span class="pagination-links">' . join("\n", $page_links) . '</span>'; |
|
| 994 | - // set page class |
|
| 995 | - if ($total_pages) { |
|
| 996 | - $page_class = $total_pages < 2 ? ' one-page' : ''; |
|
| 997 | - } else { |
|
| 998 | - $page_class = ' no-pages'; |
|
| 999 | - } |
|
| 1000 | - |
|
| 1001 | - return '<div class="tablenav"><div class="tablenav-pages' . $page_class . '">' . $output . '</div></div>'; |
|
| 1002 | - } |
|
| 1003 | - |
|
| 1004 | - |
|
| 1005 | - /** |
|
| 1006 | - * @param string $wrap_class |
|
| 1007 | - * @param string $wrap_id |
|
| 1008 | - * @param array $query_args |
|
| 1009 | - * @return string |
|
| 1010 | - */ |
|
| 1011 | - public static function powered_by_event_espresso($wrap_class = '', $wrap_id = '', array $query_args = []) |
|
| 1012 | - { |
|
| 1013 | - $admin = is_admin() && ! (defined('DOING_AJAX') && DOING_AJAX); |
|
| 1014 | - if ( |
|
| 1015 | - ! $admin |
|
| 1016 | - && ! apply_filters( |
|
| 1017 | - 'FHEE__EEH_Template__powered_by_event_espresso__show_reg_footer', |
|
| 1018 | - EE_Registry::instance()->CFG->admin->show_reg_footer |
|
| 1019 | - ) |
|
| 1020 | - ) { |
|
| 1021 | - return ''; |
|
| 1022 | - } |
|
| 1023 | - $tag = $admin ? 'span' : 'div'; |
|
| 1024 | - $attributes = ! empty($wrap_id) ? " id=\"{$wrap_id}\"" : ''; |
|
| 1025 | - $wrap_class = $admin ? "{$wrap_class} float-left" : $wrap_class; |
|
| 1026 | - $attributes .= ! empty($wrap_class) |
|
| 1027 | - ? " class=\"{$wrap_class} powered-by-event-espresso-credit\"" |
|
| 1028 | - : ' class="powered-by-event-espresso-credit"'; |
|
| 1029 | - $query_args = array_merge( |
|
| 1030 | - [ |
|
| 1031 | - 'ap_id' => EE_Registry::instance()->CFG->admin->affiliate_id(), |
|
| 1032 | - 'utm_source' => 'powered_by_event_espresso', |
|
| 1033 | - 'utm_medium' => 'link', |
|
| 1034 | - 'utm_campaign' => 'powered_by', |
|
| 1035 | - ], |
|
| 1036 | - $query_args |
|
| 1037 | - ); |
|
| 1038 | - $powered_by = apply_filters( |
|
| 1039 | - 'FHEE__EEH_Template__powered_by_event_espresso_text', |
|
| 1040 | - $admin ? 'Event Espresso - ' . EVENT_ESPRESSO_VERSION : 'Event Espresso' |
|
| 1041 | - ); |
|
| 1042 | - $url = add_query_arg($query_args, 'https://eventespresso.com/'); |
|
| 1043 | - $url = apply_filters('FHEE__EEH_Template__powered_by_event_espresso__url', $url); |
|
| 1044 | - return (string) apply_filters( |
|
| 1045 | - 'FHEE__EEH_Template__powered_by_event_espresso__html', |
|
| 1046 | - sprintf( |
|
| 1047 | - esc_html_x( |
|
| 1048 | - '%3$s%1$sOnline event registration and ticketing powered by %2$s%3$s', |
|
| 1049 | - 'Online event registration and ticketing powered by [link to eventespresso.com]', |
|
| 1050 | - 'event_espresso' |
|
| 1051 | - ), |
|
| 1052 | - "<{$tag}{$attributes}>", |
|
| 1053 | - "<a href=\"{$url}\" target=\"_blank\" rel=\"nofollow\">{$powered_by}</a></{$tag}>", |
|
| 1054 | - $admin ? '' : '<br />' |
|
| 1055 | - ), |
|
| 1056 | - $wrap_class, |
|
| 1057 | - $wrap_id |
|
| 1058 | - ); |
|
| 1059 | - } |
|
| 1060 | - |
|
| 1061 | - |
|
| 1062 | - /** |
|
| 1063 | - * @param string $image_name |
|
| 1064 | - * @return string|null |
|
| 1065 | - * @since 4.10.14.p |
|
| 1066 | - */ |
|
| 1067 | - public static function getScreenshotUrl($image_name) |
|
| 1068 | - { |
|
| 1069 | - return esc_url_raw(EE_GLOBAL_ASSETS_URL . 'images/screenshots/' . $image_name . '.jpg'); |
|
| 1070 | - } |
|
| 823 | + } else { |
|
| 824 | + // simple value |
|
| 825 | + echo esc_html($data); |
|
| 826 | + } |
|
| 827 | + return ob_get_clean(); |
|
| 828 | + } |
|
| 829 | + |
|
| 830 | + |
|
| 831 | + /** |
|
| 832 | + * wrapper for EEH_Template::get_paging_html() that simply echos the generated paging html |
|
| 833 | + * |
|
| 834 | + * @param $total_items |
|
| 835 | + * @param $current |
|
| 836 | + * @param $per_page |
|
| 837 | + * @param $url |
|
| 838 | + * @param bool $show_num_field |
|
| 839 | + * @param string $paged_arg_name |
|
| 840 | + * @param array $items_label |
|
| 841 | + * @see self:get_paging_html() for argument docs. |
|
| 842 | + * @since 4.4.0 |
|
| 843 | + */ |
|
| 844 | + public static function paging_html( |
|
| 845 | + $total_items, |
|
| 846 | + $current, |
|
| 847 | + $per_page, |
|
| 848 | + $url, |
|
| 849 | + $show_num_field = true, |
|
| 850 | + $paged_arg_name = 'paged', |
|
| 851 | + $items_label = [] |
|
| 852 | + ) { |
|
| 853 | + echo EEH_Template::get_paging_html( |
|
| 854 | + $total_items, |
|
| 855 | + $current, |
|
| 856 | + $per_page, |
|
| 857 | + $url, |
|
| 858 | + $show_num_field, |
|
| 859 | + $paged_arg_name, |
|
| 860 | + $items_label |
|
| 861 | + ); |
|
| 862 | + } |
|
| 863 | + |
|
| 864 | + |
|
| 865 | + /** |
|
| 866 | + * A method for generating paging similar to WP_List_Table |
|
| 867 | + * |
|
| 868 | + * @param integer $total_items How many total items there are to page. |
|
| 869 | + * @param integer $current What the current page is. |
|
| 870 | + * @param integer $per_page How many items per page. |
|
| 871 | + * @param string $url What the base url for page links is. |
|
| 872 | + * @param boolean $show_num_field Whether to show the input for changing page number. |
|
| 873 | + * @param string $paged_arg_name The name of the key for the paged query argument. |
|
| 874 | + * @param array $items_label An array of singular/plural values for the items label: |
|
| 875 | + * array( |
|
| 876 | + * 'single' => 'item', |
|
| 877 | + * 'plural' => 'items' |
|
| 878 | + * ) |
|
| 879 | + * @return string |
|
| 880 | + * @since 4.4.0 |
|
| 881 | + * @see wp-admin/includes/class-wp-list-table.php WP_List_Table::pagination() |
|
| 882 | + */ |
|
| 883 | + public static function get_paging_html( |
|
| 884 | + $total_items, |
|
| 885 | + $current, |
|
| 886 | + $per_page, |
|
| 887 | + $url, |
|
| 888 | + $show_num_field = true, |
|
| 889 | + $paged_arg_name = 'paged', |
|
| 890 | + $items_label = [] |
|
| 891 | + ) { |
|
| 892 | + $page_links = []; |
|
| 893 | + $disable_first = $disable_last = ''; |
|
| 894 | + $total_items = (int) $total_items; |
|
| 895 | + $per_page = (int) $per_page; |
|
| 896 | + $current = (int) $current; |
|
| 897 | + $paged_arg_name = empty($paged_arg_name) ? 'paged' : sanitize_key($paged_arg_name); |
|
| 898 | + |
|
| 899 | + // filter items_label |
|
| 900 | + $items_label = apply_filters( |
|
| 901 | + 'FHEE__EEH_Template__get_paging_html__items_label', |
|
| 902 | + $items_label |
|
| 903 | + ); |
|
| 904 | + |
|
| 905 | + if ( |
|
| 906 | + empty($items_label) |
|
| 907 | + || ! is_array($items_label) |
|
| 908 | + || ! isset($items_label['single'], $items_label['plural']) |
|
| 909 | + ) { |
|
| 910 | + $items_label = [ |
|
| 911 | + 'single' => esc_html__('1 item', 'event_espresso'), |
|
| 912 | + 'plural' => esc_html__('%s items', 'event_espresso'), |
|
| 913 | + ]; |
|
| 914 | + } else { |
|
| 915 | + $items_label = [ |
|
| 916 | + 'single' => '1 ' . esc_html($items_label['single']), |
|
| 917 | + 'plural' => '%s ' . esc_html($items_label['plural']), |
|
| 918 | + ]; |
|
| 919 | + } |
|
| 920 | + |
|
| 921 | + $total_pages = ceil($total_items / $per_page); |
|
| 922 | + |
|
| 923 | + if ($total_pages <= 1) { |
|
| 924 | + return ''; |
|
| 925 | + } |
|
| 926 | + |
|
| 927 | + $item_label = $total_items > 1 ? sprintf($items_label['plural'], $total_items) : $items_label['single']; |
|
| 928 | + |
|
| 929 | + $output = '<span class="displaying-num">' . $item_label . '</span>'; |
|
| 930 | + |
|
| 931 | + if ($current === 1) { |
|
| 932 | + $disable_first = ' disabled'; |
|
| 933 | + } |
|
| 934 | + if ($current === $total_pages) { |
|
| 935 | + $disable_last = ' disabled'; |
|
| 936 | + } |
|
| 937 | + |
|
| 938 | + $page_links[] = sprintf( |
|
| 939 | + "<a class='%s' title='%s' href='%s'>%s</a>", |
|
| 940 | + 'first-page' . $disable_first, |
|
| 941 | + esc_attr__('Go to the first page', 'event_espresso'), |
|
| 942 | + esc_url_raw(remove_query_arg($paged_arg_name, $url)), |
|
| 943 | + '«' |
|
| 944 | + ); |
|
| 945 | + |
|
| 946 | + $page_links[] = sprintf( |
|
| 947 | + '<a class="%s" title="%s" href="%s">%s</a>', |
|
| 948 | + 'prev-page' . $disable_first, |
|
| 949 | + esc_attr__('Go to the previous page', 'event_espresso'), |
|
| 950 | + esc_url_raw(add_query_arg($paged_arg_name, max(1, $current - 1), $url)), |
|
| 951 | + '‹' |
|
| 952 | + ); |
|
| 953 | + |
|
| 954 | + if (! $show_num_field) { |
|
| 955 | + $html_current_page = $current; |
|
| 956 | + } else { |
|
| 957 | + $html_current_page = sprintf( |
|
| 958 | + "<input class='current-page' title='%s' type='text' name=$paged_arg_name value='%s' size='%d' />", |
|
| 959 | + esc_attr__('Current page', 'event_espresso'), |
|
| 960 | + esc_attr($current), |
|
| 961 | + strlen($total_pages) |
|
| 962 | + ); |
|
| 963 | + } |
|
| 964 | + |
|
| 965 | + $html_total_pages = sprintf( |
|
| 966 | + '<span class="total-pages">%s</span>', |
|
| 967 | + number_format_i18n($total_pages) |
|
| 968 | + ); |
|
| 969 | + $page_links[] = sprintf( |
|
| 970 | + _x('%3$s%1$s of %2$s%4$s', 'paging', 'event_espresso'), |
|
| 971 | + $html_current_page, |
|
| 972 | + $html_total_pages, |
|
| 973 | + '<span class="paging-input">', |
|
| 974 | + '</span>' |
|
| 975 | + ); |
|
| 976 | + |
|
| 977 | + $page_links[] = sprintf( |
|
| 978 | + '<a class="%s" title="%s" href="%s">%s</a>', |
|
| 979 | + 'next-page' . $disable_last, |
|
| 980 | + esc_attr__('Go to the next page', 'event_espresso'), |
|
| 981 | + esc_url_raw(add_query_arg($paged_arg_name, min($total_pages, $current + 1), $url)), |
|
| 982 | + '›' |
|
| 983 | + ); |
|
| 984 | + |
|
| 985 | + $page_links[] = sprintf( |
|
| 986 | + '<a class="%s" title="%s" href="%s">%s</a>', |
|
| 987 | + 'last-page' . $disable_last, |
|
| 988 | + esc_attr__('Go to the last page', 'event_espresso'), |
|
| 989 | + esc_url_raw(add_query_arg($paged_arg_name, $total_pages, $url)), |
|
| 990 | + '»' |
|
| 991 | + ); |
|
| 992 | + |
|
| 993 | + $output .= "\n" . '<span class="pagination-links">' . join("\n", $page_links) . '</span>'; |
|
| 994 | + // set page class |
|
| 995 | + if ($total_pages) { |
|
| 996 | + $page_class = $total_pages < 2 ? ' one-page' : ''; |
|
| 997 | + } else { |
|
| 998 | + $page_class = ' no-pages'; |
|
| 999 | + } |
|
| 1000 | + |
|
| 1001 | + return '<div class="tablenav"><div class="tablenav-pages' . $page_class . '">' . $output . '</div></div>'; |
|
| 1002 | + } |
|
| 1003 | + |
|
| 1004 | + |
|
| 1005 | + /** |
|
| 1006 | + * @param string $wrap_class |
|
| 1007 | + * @param string $wrap_id |
|
| 1008 | + * @param array $query_args |
|
| 1009 | + * @return string |
|
| 1010 | + */ |
|
| 1011 | + public static function powered_by_event_espresso($wrap_class = '', $wrap_id = '', array $query_args = []) |
|
| 1012 | + { |
|
| 1013 | + $admin = is_admin() && ! (defined('DOING_AJAX') && DOING_AJAX); |
|
| 1014 | + if ( |
|
| 1015 | + ! $admin |
|
| 1016 | + && ! apply_filters( |
|
| 1017 | + 'FHEE__EEH_Template__powered_by_event_espresso__show_reg_footer', |
|
| 1018 | + EE_Registry::instance()->CFG->admin->show_reg_footer |
|
| 1019 | + ) |
|
| 1020 | + ) { |
|
| 1021 | + return ''; |
|
| 1022 | + } |
|
| 1023 | + $tag = $admin ? 'span' : 'div'; |
|
| 1024 | + $attributes = ! empty($wrap_id) ? " id=\"{$wrap_id}\"" : ''; |
|
| 1025 | + $wrap_class = $admin ? "{$wrap_class} float-left" : $wrap_class; |
|
| 1026 | + $attributes .= ! empty($wrap_class) |
|
| 1027 | + ? " class=\"{$wrap_class} powered-by-event-espresso-credit\"" |
|
| 1028 | + : ' class="powered-by-event-espresso-credit"'; |
|
| 1029 | + $query_args = array_merge( |
|
| 1030 | + [ |
|
| 1031 | + 'ap_id' => EE_Registry::instance()->CFG->admin->affiliate_id(), |
|
| 1032 | + 'utm_source' => 'powered_by_event_espresso', |
|
| 1033 | + 'utm_medium' => 'link', |
|
| 1034 | + 'utm_campaign' => 'powered_by', |
|
| 1035 | + ], |
|
| 1036 | + $query_args |
|
| 1037 | + ); |
|
| 1038 | + $powered_by = apply_filters( |
|
| 1039 | + 'FHEE__EEH_Template__powered_by_event_espresso_text', |
|
| 1040 | + $admin ? 'Event Espresso - ' . EVENT_ESPRESSO_VERSION : 'Event Espresso' |
|
| 1041 | + ); |
|
| 1042 | + $url = add_query_arg($query_args, 'https://eventespresso.com/'); |
|
| 1043 | + $url = apply_filters('FHEE__EEH_Template__powered_by_event_espresso__url', $url); |
|
| 1044 | + return (string) apply_filters( |
|
| 1045 | + 'FHEE__EEH_Template__powered_by_event_espresso__html', |
|
| 1046 | + sprintf( |
|
| 1047 | + esc_html_x( |
|
| 1048 | + '%3$s%1$sOnline event registration and ticketing powered by %2$s%3$s', |
|
| 1049 | + 'Online event registration and ticketing powered by [link to eventespresso.com]', |
|
| 1050 | + 'event_espresso' |
|
| 1051 | + ), |
|
| 1052 | + "<{$tag}{$attributes}>", |
|
| 1053 | + "<a href=\"{$url}\" target=\"_blank\" rel=\"nofollow\">{$powered_by}</a></{$tag}>", |
|
| 1054 | + $admin ? '' : '<br />' |
|
| 1055 | + ), |
|
| 1056 | + $wrap_class, |
|
| 1057 | + $wrap_id |
|
| 1058 | + ); |
|
| 1059 | + } |
|
| 1060 | + |
|
| 1061 | + |
|
| 1062 | + /** |
|
| 1063 | + * @param string $image_name |
|
| 1064 | + * @return string|null |
|
| 1065 | + * @since 4.10.14.p |
|
| 1066 | + */ |
|
| 1067 | + public static function getScreenshotUrl($image_name) |
|
| 1068 | + { |
|
| 1069 | + return esc_url_raw(EE_GLOBAL_ASSETS_URL . 'images/screenshots/' . $image_name . '.jpg'); |
|
| 1070 | + } |
|
| 1071 | 1071 | } |
| 1072 | 1072 | |
| 1073 | 1073 | |
| 1074 | 1074 | if (! function_exists('espresso_pagination')) { |
| 1075 | - /** |
|
| 1076 | - * espresso_pagination |
|
| 1077 | - * |
|
| 1078 | - * @access public |
|
| 1079 | - * @return void |
|
| 1080 | - */ |
|
| 1081 | - function espresso_pagination() |
|
| 1082 | - { |
|
| 1083 | - global $wp_query; |
|
| 1084 | - $big = 999999999; // need an unlikely integer |
|
| 1085 | - $pagination = paginate_links( |
|
| 1086 | - [ |
|
| 1087 | - 'base' => str_replace($big, '%#%', esc_url(get_pagenum_link($big))), |
|
| 1088 | - 'format' => '?paged=%#%', |
|
| 1089 | - 'current' => max(1, get_query_var('paged')), |
|
| 1090 | - 'total' => $wp_query->max_num_pages, |
|
| 1091 | - 'show_all' => true, |
|
| 1092 | - 'end_size' => 10, |
|
| 1093 | - 'mid_size' => 6, |
|
| 1094 | - 'prev_next' => true, |
|
| 1095 | - 'prev_text' => esc_html__('‹ PREV', 'event_espresso'), |
|
| 1096 | - 'next_text' => esc_html__('NEXT ›', 'event_espresso'), |
|
| 1097 | - 'type' => 'plain', |
|
| 1098 | - 'add_args' => false, |
|
| 1099 | - 'add_fragment' => '', |
|
| 1100 | - ] |
|
| 1101 | - ); |
|
| 1102 | - echo ! empty($pagination) ? '<div class="ee-pagination-dv ee-clear-float">' . $pagination . '</div>' : ''; |
|
| 1103 | - } |
|
| 1075 | + /** |
|
| 1076 | + * espresso_pagination |
|
| 1077 | + * |
|
| 1078 | + * @access public |
|
| 1079 | + * @return void |
|
| 1080 | + */ |
|
| 1081 | + function espresso_pagination() |
|
| 1082 | + { |
|
| 1083 | + global $wp_query; |
|
| 1084 | + $big = 999999999; // need an unlikely integer |
|
| 1085 | + $pagination = paginate_links( |
|
| 1086 | + [ |
|
| 1087 | + 'base' => str_replace($big, '%#%', esc_url(get_pagenum_link($big))), |
|
| 1088 | + 'format' => '?paged=%#%', |
|
| 1089 | + 'current' => max(1, get_query_var('paged')), |
|
| 1090 | + 'total' => $wp_query->max_num_pages, |
|
| 1091 | + 'show_all' => true, |
|
| 1092 | + 'end_size' => 10, |
|
| 1093 | + 'mid_size' => 6, |
|
| 1094 | + 'prev_next' => true, |
|
| 1095 | + 'prev_text' => esc_html__('‹ PREV', 'event_espresso'), |
|
| 1096 | + 'next_text' => esc_html__('NEXT ›', 'event_espresso'), |
|
| 1097 | + 'type' => 'plain', |
|
| 1098 | + 'add_args' => false, |
|
| 1099 | + 'add_fragment' => '', |
|
| 1100 | + ] |
|
| 1101 | + ); |
|
| 1102 | + echo ! empty($pagination) ? '<div class="ee-pagination-dv ee-clear-float">' . $pagination . '</div>' : ''; |
|
| 1103 | + } |
|
| 1104 | 1104 | } |
@@ -8,7 +8,7 @@ discard block |
||
| 8 | 8 | use EventEspresso\core\services\request\RequestInterface; |
| 9 | 9 | use EventEspresso\core\services\request\sanitizers\AllowedTags; |
| 10 | 10 | |
| 11 | -if (! function_exists('espresso_get_template_part')) { |
|
| 11 | +if ( ! function_exists('espresso_get_template_part')) { |
|
| 12 | 12 | /** |
| 13 | 13 | * espresso_get_template_part |
| 14 | 14 | * basically a copy of the WordPress get_template_part() function but uses EEH_Template::locate_template() instead, and doesn't add base versions of files |
@@ -24,7 +24,7 @@ discard block |
||
| 24 | 24 | } |
| 25 | 25 | |
| 26 | 26 | |
| 27 | -if (! function_exists('espresso_get_object_css_class')) { |
|
| 27 | +if ( ! function_exists('espresso_get_object_css_class')) { |
|
| 28 | 28 | /** |
| 29 | 29 | * espresso_get_object_css_class - attempts to generate a css class based on the type of EE object passed |
| 30 | 30 | * |
@@ -80,14 +80,14 @@ discard block |
||
| 80 | 80 | { |
| 81 | 81 | $currencyISO = $currency_locale->currencyIsoCode(); |
| 82 | 82 | if ( |
| 83 | - ! isset(EEH_Template::$currency_config[ $currencyISO ]) |
|
| 84 | - || ! EEH_Template::$currency_config[ $currencyISO ] instanceof EE_Currency_Config |
|
| 83 | + ! isset(EEH_Template::$currency_config[$currencyISO]) |
|
| 84 | + || ! EEH_Template::$currency_config[$currencyISO] instanceof EE_Currency_Config |
|
| 85 | 85 | ) { |
| 86 | 86 | $CNT_ISO = EEM_Country::instance()->getCountryISOForCurrencyISO($currencyISO); |
| 87 | 87 | $currency_config = new EE_Currency_Config($CNT_ISO); |
| 88 | - EEH_Template::$currency_config[ $currencyISO ] = $currency_config; |
|
| 88 | + EEH_Template::$currency_config[$currencyISO] = $currency_config; |
|
| 89 | 89 | } |
| 90 | - return EEH_Template::$currency_config[ $currencyISO ]; |
|
| 90 | + return EEH_Template::$currency_config[$currencyISO]; |
|
| 91 | 91 | } |
| 92 | 92 | |
| 93 | 93 | |
@@ -97,7 +97,7 @@ discard block |
||
| 97 | 97 | */ |
| 98 | 98 | private static function getCurrencyFormatter() |
| 99 | 99 | { |
| 100 | - if (! EEH_Template::$currency_formatter instanceof CurrencyFormatter) { |
|
| 100 | + if ( ! EEH_Template::$currency_formatter instanceof CurrencyFormatter) { |
|
| 101 | 101 | EEH_Template::$currency_formatter = LoaderFactory::getLoader()->getShared(CurrencyFormatter::class); |
| 102 | 102 | } |
| 103 | 103 | return EEH_Template::$currency_formatter; |
@@ -125,9 +125,9 @@ discard block |
||
| 125 | 125 | { |
| 126 | 126 | if ( |
| 127 | 127 | ! defined('EE_THEME_FUNCTIONS_LOADED') |
| 128 | - && is_readable(EE_PUBLIC . EE_Config::get_current_theme() . '/functions.php') |
|
| 128 | + && is_readable(EE_PUBLIC.EE_Config::get_current_theme().'/functions.php') |
|
| 129 | 129 | ) { |
| 130 | - require_once(EE_PUBLIC . EE_Config::get_current_theme() . '/functions.php'); |
|
| 130 | + require_once(EE_PUBLIC.EE_Config::get_current_theme().'/functions.php'); |
|
| 131 | 131 | } |
| 132 | 132 | } |
| 133 | 133 | |
@@ -140,16 +140,16 @@ discard block |
||
| 140 | 140 | public static function get_espresso_themes() |
| 141 | 141 | { |
| 142 | 142 | if (empty(EEH_Template::$_espresso_themes)) { |
| 143 | - $espresso_themes = glob(EE_PUBLIC . '*', GLOB_ONLYDIR); |
|
| 143 | + $espresso_themes = glob(EE_PUBLIC.'*', GLOB_ONLYDIR); |
|
| 144 | 144 | if (empty($espresso_themes)) { |
| 145 | 145 | return []; |
| 146 | 146 | } |
| 147 | 147 | if (($key = array_search('global_assets', $espresso_themes, true)) !== false) { |
| 148 | - unset($espresso_themes[ $key ]); |
|
| 148 | + unset($espresso_themes[$key]); |
|
| 149 | 149 | } |
| 150 | 150 | EEH_Template::$_espresso_themes = []; |
| 151 | 151 | foreach ($espresso_themes as $espresso_theme) { |
| 152 | - EEH_Template::$_espresso_themes[ basename($espresso_theme) ] = $espresso_theme; |
|
| 152 | + EEH_Template::$_espresso_themes[basename($espresso_theme)] = $espresso_theme; |
|
| 153 | 153 | } |
| 154 | 154 | } |
| 155 | 155 | return EEH_Template::$_espresso_themes; |
@@ -265,10 +265,10 @@ discard block |
||
| 265 | 265 | // get array of EE Custom Post Types |
| 266 | 266 | $EE_CPTs = $custom_post_types->getDefinitions(); |
| 267 | 267 | // build template name based on request |
| 268 | - if (isset($EE_CPTs[ $post_type ])) { |
|
| 268 | + if (isset($EE_CPTs[$post_type])) { |
|
| 269 | 269 | $archive_or_single = is_archive() ? 'archive' : ''; |
| 270 | 270 | $archive_or_single = is_single() ? 'single' : $archive_or_single; |
| 271 | - $templates = $archive_or_single . '-' . $post_type . '.php'; |
|
| 271 | + $templates = $archive_or_single.'-'.$post_type.'.php'; |
|
| 272 | 272 | } |
| 273 | 273 | } |
| 274 | 274 | // currently active EE template theme |
@@ -277,18 +277,18 @@ discard block |
||
| 277 | 277 | // array of paths to folders that may contain templates |
| 278 | 278 | $template_folder_paths = [ |
| 279 | 279 | // first check the /wp-content/uploads/espresso/templates/(current EE theme)/ folder for an EE theme template file |
| 280 | - EVENT_ESPRESSO_TEMPLATE_DIR . $current_theme, |
|
| 280 | + EVENT_ESPRESSO_TEMPLATE_DIR.$current_theme, |
|
| 281 | 281 | // then in the root of the /wp-content/uploads/espresso/templates/ folder |
| 282 | 282 | EVENT_ESPRESSO_TEMPLATE_DIR, |
| 283 | 283 | ]; |
| 284 | 284 | |
| 285 | 285 | // add core plugin folders for checking only if we're not $check_if_custom |
| 286 | - if (! $check_if_custom) { |
|
| 287 | - $core_paths = [ |
|
| 286 | + if ( ! $check_if_custom) { |
|
| 287 | + $core_paths = [ |
|
| 288 | 288 | // in the /wp-content/plugins/(EE4 folder)/public/(current EE theme)/ folder within the plugin |
| 289 | - EE_PUBLIC . $current_theme, |
|
| 289 | + EE_PUBLIC.$current_theme, |
|
| 290 | 290 | // in the /wp-content/plugins/(EE4 folder)/core/templates/(current EE theme)/ folder within the plugin |
| 291 | - EE_TEMPLATES . $current_theme, |
|
| 291 | + EE_TEMPLATES.$current_theme, |
|
| 292 | 292 | // or maybe relative from the plugin root: /wp-content/plugins/(EE4 folder)/ |
| 293 | 293 | EE_PLUGIN_DIR_PATH, |
| 294 | 294 | ]; |
@@ -323,10 +323,10 @@ discard block |
||
| 323 | 323 | ); |
| 324 | 324 | if ($common_base_path !== '') { |
| 325 | 325 | // both paths have a common base, so just tack the filename onto our search path |
| 326 | - $resolved_path = EEH_File::end_with_directory_separator($template_folder_path) . $file_name; |
|
| 326 | + $resolved_path = EEH_File::end_with_directory_separator($template_folder_path).$file_name; |
|
| 327 | 327 | } else { |
| 328 | 328 | // no common base path, so let's just concatenate |
| 329 | - $resolved_path = EEH_File::end_with_directory_separator($template_folder_path) . $template; |
|
| 329 | + $resolved_path = EEH_File::end_with_directory_separator($template_folder_path).$template; |
|
| 330 | 330 | } |
| 331 | 331 | // build up our template locations array by adding our resolved paths |
| 332 | 332 | $full_template_paths[] = $resolved_path; |
@@ -334,7 +334,7 @@ discard block |
||
| 334 | 334 | // if $template is an absolute path, then we'll tack it onto the start of our array so that it gets searched first |
| 335 | 335 | array_unshift($full_template_paths, $template); |
| 336 | 336 | // path to the directory of the current theme: /wp-content/themes/(current WP theme)/ |
| 337 | - array_unshift($full_template_paths, get_stylesheet_directory() . '/' . $file_name); |
|
| 337 | + array_unshift($full_template_paths, get_stylesheet_directory().'/'.$file_name); |
|
| 338 | 338 | } |
| 339 | 339 | // filter final array of full template paths |
| 340 | 340 | $full_template_paths = apply_filters( |
@@ -385,7 +385,7 @@ discard block |
||
| 385 | 385 | } |
| 386 | 386 | } |
| 387 | 387 | $common_base_path .= $directory; |
| 388 | - $last_offset = $index + 1; |
|
| 388 | + $last_offset = $index + 1; |
|
| 389 | 389 | } |
| 390 | 390 | return substr($common_base_path, 0, -1); |
| 391 | 391 | } |
@@ -422,7 +422,7 @@ discard block |
||
| 422 | 422 | $template_args = (array) apply_filters('FHEE__EEH_Template__display_template__template_args', $template_args); |
| 423 | 423 | |
| 424 | 424 | // you gimme nuttin - YOU GET NUTTIN !! |
| 425 | - if (! $template_path || ! is_readable($template_path)) { |
|
| 425 | + if ( ! $template_path || ! is_readable($template_path)) { |
|
| 426 | 426 | // ignore whether template is accessible ? |
| 427 | 427 | if ($throw_exceptions) { |
| 428 | 428 | throw new DomainException( |
@@ -432,7 +432,7 @@ discard block |
||
| 432 | 432 | return ''; |
| 433 | 433 | } |
| 434 | 434 | // if $template_args are not in an array, then make it so |
| 435 | - if (! is_array($template_args) && ! is_object($template_args)) { |
|
| 435 | + if ( ! is_array($template_args) && ! is_object($template_args)) { |
|
| 436 | 436 | $template_args = [$template_args]; |
| 437 | 437 | } |
| 438 | 438 | extract($template_args, EXTR_SKIP); |
@@ -471,20 +471,20 @@ discard block |
||
| 471 | 471 | public static function get_object_css_class($object = null, $prefix = '', $suffix = '') |
| 472 | 472 | { |
| 473 | 473 | // in the beginning... |
| 474 | - $prefix = ! empty($prefix) ? rtrim($prefix, '-') . '-' : ''; |
|
| 474 | + $prefix = ! empty($prefix) ? rtrim($prefix, '-').'-' : ''; |
|
| 475 | 475 | // da muddle |
| 476 | 476 | $class = ''; |
| 477 | 477 | // the end |
| 478 | - $suffix = ! empty($suffix) ? '-' . ltrim($suffix, '-') : ''; |
|
| 478 | + $suffix = ! empty($suffix) ? '-'.ltrim($suffix, '-') : ''; |
|
| 479 | 479 | // is the passed object an EE object ? |
| 480 | 480 | if ($object instanceof EE_Base_Class) { |
| 481 | 481 | // grab the exact type of object |
| 482 | 482 | $obj_class = get_class($object); |
| 483 | 483 | // depending on the type of object... |
| 484 | 484 | $class = strtolower(str_replace('_', '-', $obj_class)); |
| 485 | - $class .= method_exists($obj_class, 'name') ? '-' . sanitize_title($object->name()) : ''; |
|
| 485 | + $class .= method_exists($obj_class, 'name') ? '-'.sanitize_title($object->name()) : ''; |
|
| 486 | 486 | } |
| 487 | - return $prefix . $class . $suffix; |
|
| 487 | + return $prefix.$class.$suffix; |
|
| 488 | 488 | } |
| 489 | 489 | |
| 490 | 490 | |
@@ -526,7 +526,7 @@ discard block |
||
| 526 | 526 | // filter raw amount (allows 0.00 to be changed to "free" for example) |
| 527 | 527 | $amount_formatted = apply_filters('FHEE__EEH_Template__format_currency__amount', $amount, $return_raw); |
| 528 | 528 | // still a number or was amount converted to a string like "free" ? |
| 529 | - if (! is_float($amount_formatted)) { |
|
| 529 | + if ( ! is_float($amount_formatted)) { |
|
| 530 | 530 | return esc_html($amount_formatted); |
| 531 | 531 | } |
| 532 | 532 | |
@@ -595,7 +595,7 @@ discard block |
||
| 595 | 595 | $plural, |
| 596 | 596 | $schema |
| 597 | 597 | ); |
| 598 | - return $status[ $status_id ]; |
|
| 598 | + return $status[$status_id]; |
|
| 599 | 599 | } |
| 600 | 600 | |
| 601 | 601 | |
@@ -612,14 +612,14 @@ discard block |
||
| 612 | 612 | public static function get_button_or_link($url, $label, $class = 'button button--primary', $icon = '', $title = '') |
| 613 | 613 | { |
| 614 | 614 | $icon_html = ''; |
| 615 | - if (! empty($icon)) { |
|
| 615 | + if ( ! empty($icon)) { |
|
| 616 | 616 | $dashicons = preg_split("(ee-icon |dashicons )", $icon); |
| 617 | 617 | $dashicons = array_filter($dashicons); |
| 618 | 618 | $count = count($dashicons); |
| 619 | 619 | $icon_html .= $count > 1 ? '<span class="ee-composite-dashicon">' : ''; |
| 620 | 620 | foreach ($dashicons as $dashicon) { |
| 621 | - $type = strpos($dashicon, 'ee-icon') !== false ? 'ee-icon ' : 'dashicons '; |
|
| 622 | - $icon_html .= '<span class="' . $type . $dashicon . '"></span>'; |
|
| 621 | + $type = strpos($dashicon, 'ee-icon') !== false ? 'ee-icon ' : 'dashicons '; |
|
| 622 | + $icon_html .= '<span class="'.$type.$dashicon.'"></span>'; |
|
| 623 | 623 | } |
| 624 | 624 | $icon_html .= $count > 1 ? '</span>' : ''; |
| 625 | 625 | } |
@@ -657,16 +657,16 @@ discard block |
||
| 657 | 657 | $action = $action ?: $request->getRequestParam('action', 'default', 'key'); |
| 658 | 658 | |
| 659 | 659 | |
| 660 | - $help_tab_lnk = $page . '-' . $action . '-' . $help_tab_id; |
|
| 660 | + $help_tab_lnk = $page.'-'.$action.'-'.$help_tab_id; |
|
| 661 | 661 | $icon = $icon_style ? $icon_style : ' dashicons-editor-help'; |
| 662 | 662 | $help_text = $help_text ? $help_text : ''; |
| 663 | - $link = '<a id="' . $help_tab_lnk . '"'; |
|
| 664 | - $link .= ' class="ee-clickable espresso-help-tab-lnk dashicons' . $icon . '"'; |
|
| 665 | - $link .= ' title="' . esc_attr__( |
|
| 663 | + $link = '<a id="'.$help_tab_lnk.'"'; |
|
| 664 | + $link .= ' class="ee-clickable espresso-help-tab-lnk dashicons'.$icon.'"'; |
|
| 665 | + $link .= ' title="'.esc_attr__( |
|
| 666 | 666 | 'Click to open the \'Help\' tab for more information about this feature.', |
| 667 | 667 | 'event_espresso' |
| 668 | - ) . '"'; |
|
| 669 | - $link .= ' > ' . $help_text . ' </a>'; |
|
| 668 | + ).'"'; |
|
| 669 | + $link .= ' > '.$help_text.' </a>'; |
|
| 670 | 670 | return $link; |
| 671 | 671 | } |
| 672 | 672 | |
@@ -685,11 +685,11 @@ discard block |
||
| 685 | 685 | $id = $tour->get_slug(); |
| 686 | 686 | $stops = $tour->get_stops(); |
| 687 | 687 | |
| 688 | - $content = '<ol style="display:none" id="' . $id . '">'; |
|
| 688 | + $content = '<ol style="display:none" id="'.$id.'">'; |
|
| 689 | 689 | |
| 690 | 690 | foreach ($stops as $stop) { |
| 691 | - $data_id = ! empty($stop['id']) ? ' data-id="' . $stop['id'] . '"' : ''; |
|
| 692 | - $data_class = empty($data_id) && ! empty($stop['class']) ? ' data-class="' . $stop['class'] . '"' : ''; |
|
| 691 | + $data_id = ! empty($stop['id']) ? ' data-id="'.$stop['id'].'"' : ''; |
|
| 692 | + $data_class = empty($data_id) && ! empty($stop['class']) ? ' data-class="'.$stop['class'].'"' : ''; |
|
| 693 | 693 | |
| 694 | 694 | // if container is set to modal then let's make sure we set the options accordingly |
| 695 | 695 | if (empty($data_id) && empty($data_class)) { |
@@ -697,15 +697,15 @@ discard block |
||
| 697 | 697 | $stop['options']['expose'] = true; |
| 698 | 698 | } |
| 699 | 699 | |
| 700 | - $custom_class = ! empty($stop['custom_class']) ? ' class="' . $stop['custom_class'] . '"' : ''; |
|
| 701 | - $button_text = ! empty($stop['button_text']) ? ' data-button="' . $stop['button_text'] . '"' : ''; |
|
| 700 | + $custom_class = ! empty($stop['custom_class']) ? ' class="'.$stop['custom_class'].'"' : ''; |
|
| 701 | + $button_text = ! empty($stop['button_text']) ? ' data-button="'.$stop['button_text'].'"' : ''; |
|
| 702 | 702 | $inner_content = isset($stop['content']) ? $stop['content'] : ''; |
| 703 | 703 | |
| 704 | 704 | // options |
| 705 | 705 | if (isset($stop['options']) && is_array($stop['options'])) { |
| 706 | 706 | $options = ' data-options="'; |
| 707 | 707 | foreach ($stop['options'] as $option => $value) { |
| 708 | - $options .= $option . ':' . $value . ';'; |
|
| 708 | + $options .= $option.':'.$value.';'; |
|
| 709 | 709 | } |
| 710 | 710 | $options .= '"'; |
| 711 | 711 | } else { |
@@ -745,7 +745,7 @@ discard block |
||
| 745 | 745 | */ |
| 746 | 746 | public static function status_legend($status_array, $active_status = '') |
| 747 | 747 | { |
| 748 | - if (! is_array($status_array)) { |
|
| 748 | + if ( ! is_array($status_array)) { |
|
| 749 | 749 | throw new EE_Error( |
| 750 | 750 | esc_html__( |
| 751 | 751 | 'The EEH_Template::status_legend helper required the incoming status_array argument to be an array!', |
@@ -757,17 +757,17 @@ discard block |
||
| 757 | 757 | $content = ' |
| 758 | 758 | <div class="ee-list-table-legend-container"> |
| 759 | 759 | <h4 class="status-legend-title"> |
| 760 | - ' . esc_html__('Status Legend', 'event_espresso') . ' |
|
| 760 | + ' . esc_html__('Status Legend', 'event_espresso').' |
|
| 761 | 761 | </h4> |
| 762 | 762 | <dl class="ee-list-table-legend">'; |
| 763 | 763 | |
| 764 | 764 | foreach ($status_array as $item => $status) { |
| 765 | 765 | $active_class = $active_status == $status ? 'class="ee-is-active-status"' : ''; |
| 766 | - $content .= ' |
|
| 767 | - <dt id="' . esc_attr('ee-legend-item-tooltip-' . $item) . '" ' . $active_class . '> |
|
| 768 | - <span class="' . esc_attr('ee-status-legend ee-status-legend-' . $status) . '"></span> |
|
| 766 | + $content .= ' |
|
| 767 | + <dt id="' . esc_attr('ee-legend-item-tooltip-'.$item).'" '.$active_class.'> |
|
| 768 | + <span class="' . esc_attr('ee-status-legend ee-status-legend-'.$status).'"></span> |
|
| 769 | 769 | <span class="ee-legend-description"> |
| 770 | - ' . EEH_Template::pretty_status($status, false, 'sentence') . ' |
|
| 770 | + ' . EEH_Template::pretty_status($status, false, 'sentence').' |
|
| 771 | 771 | </span> |
| 772 | 772 | </dt>'; |
| 773 | 773 | } |
@@ -913,8 +913,8 @@ discard block |
||
| 913 | 913 | ]; |
| 914 | 914 | } else { |
| 915 | 915 | $items_label = [ |
| 916 | - 'single' => '1 ' . esc_html($items_label['single']), |
|
| 917 | - 'plural' => '%s ' . esc_html($items_label['plural']), |
|
| 916 | + 'single' => '1 '.esc_html($items_label['single']), |
|
| 917 | + 'plural' => '%s '.esc_html($items_label['plural']), |
|
| 918 | 918 | ]; |
| 919 | 919 | } |
| 920 | 920 | |
@@ -926,7 +926,7 @@ discard block |
||
| 926 | 926 | |
| 927 | 927 | $item_label = $total_items > 1 ? sprintf($items_label['plural'], $total_items) : $items_label['single']; |
| 928 | 928 | |
| 929 | - $output = '<span class="displaying-num">' . $item_label . '</span>'; |
|
| 929 | + $output = '<span class="displaying-num">'.$item_label.'</span>'; |
|
| 930 | 930 | |
| 931 | 931 | if ($current === 1) { |
| 932 | 932 | $disable_first = ' disabled'; |
@@ -937,7 +937,7 @@ discard block |
||
| 937 | 937 | |
| 938 | 938 | $page_links[] = sprintf( |
| 939 | 939 | "<a class='%s' title='%s' href='%s'>%s</a>", |
| 940 | - 'first-page' . $disable_first, |
|
| 940 | + 'first-page'.$disable_first, |
|
| 941 | 941 | esc_attr__('Go to the first page', 'event_espresso'), |
| 942 | 942 | esc_url_raw(remove_query_arg($paged_arg_name, $url)), |
| 943 | 943 | '«' |
@@ -945,13 +945,13 @@ discard block |
||
| 945 | 945 | |
| 946 | 946 | $page_links[] = sprintf( |
| 947 | 947 | '<a class="%s" title="%s" href="%s">%s</a>', |
| 948 | - 'prev-page' . $disable_first, |
|
| 948 | + 'prev-page'.$disable_first, |
|
| 949 | 949 | esc_attr__('Go to the previous page', 'event_espresso'), |
| 950 | 950 | esc_url_raw(add_query_arg($paged_arg_name, max(1, $current - 1), $url)), |
| 951 | 951 | '‹' |
| 952 | 952 | ); |
| 953 | 953 | |
| 954 | - if (! $show_num_field) { |
|
| 954 | + if ( ! $show_num_field) { |
|
| 955 | 955 | $html_current_page = $current; |
| 956 | 956 | } else { |
| 957 | 957 | $html_current_page = sprintf( |
@@ -966,7 +966,7 @@ discard block |
||
| 966 | 966 | '<span class="total-pages">%s</span>', |
| 967 | 967 | number_format_i18n($total_pages) |
| 968 | 968 | ); |
| 969 | - $page_links[] = sprintf( |
|
| 969 | + $page_links[] = sprintf( |
|
| 970 | 970 | _x('%3$s%1$s of %2$s%4$s', 'paging', 'event_espresso'), |
| 971 | 971 | $html_current_page, |
| 972 | 972 | $html_total_pages, |
@@ -976,7 +976,7 @@ discard block |
||
| 976 | 976 | |
| 977 | 977 | $page_links[] = sprintf( |
| 978 | 978 | '<a class="%s" title="%s" href="%s">%s</a>', |
| 979 | - 'next-page' . $disable_last, |
|
| 979 | + 'next-page'.$disable_last, |
|
| 980 | 980 | esc_attr__('Go to the next page', 'event_espresso'), |
| 981 | 981 | esc_url_raw(add_query_arg($paged_arg_name, min($total_pages, $current + 1), $url)), |
| 982 | 982 | '›' |
@@ -984,13 +984,13 @@ discard block |
||
| 984 | 984 | |
| 985 | 985 | $page_links[] = sprintf( |
| 986 | 986 | '<a class="%s" title="%s" href="%s">%s</a>', |
| 987 | - 'last-page' . $disable_last, |
|
| 987 | + 'last-page'.$disable_last, |
|
| 988 | 988 | esc_attr__('Go to the last page', 'event_espresso'), |
| 989 | 989 | esc_url_raw(add_query_arg($paged_arg_name, $total_pages, $url)), |
| 990 | 990 | '»' |
| 991 | 991 | ); |
| 992 | 992 | |
| 993 | - $output .= "\n" . '<span class="pagination-links">' . join("\n", $page_links) . '</span>'; |
|
| 993 | + $output .= "\n".'<span class="pagination-links">'.join("\n", $page_links).'</span>'; |
|
| 994 | 994 | // set page class |
| 995 | 995 | if ($total_pages) { |
| 996 | 996 | $page_class = $total_pages < 2 ? ' one-page' : ''; |
@@ -998,7 +998,7 @@ discard block |
||
| 998 | 998 | $page_class = ' no-pages'; |
| 999 | 999 | } |
| 1000 | 1000 | |
| 1001 | - return '<div class="tablenav"><div class="tablenav-pages' . $page_class . '">' . $output . '</div></div>'; |
|
| 1001 | + return '<div class="tablenav"><div class="tablenav-pages'.$page_class.'">'.$output.'</div></div>'; |
|
| 1002 | 1002 | } |
| 1003 | 1003 | |
| 1004 | 1004 | |
@@ -1037,7 +1037,7 @@ discard block |
||
| 1037 | 1037 | ); |
| 1038 | 1038 | $powered_by = apply_filters( |
| 1039 | 1039 | 'FHEE__EEH_Template__powered_by_event_espresso_text', |
| 1040 | - $admin ? 'Event Espresso - ' . EVENT_ESPRESSO_VERSION : 'Event Espresso' |
|
| 1040 | + $admin ? 'Event Espresso - '.EVENT_ESPRESSO_VERSION : 'Event Espresso' |
|
| 1041 | 1041 | ); |
| 1042 | 1042 | $url = add_query_arg($query_args, 'https://eventespresso.com/'); |
| 1043 | 1043 | $url = apply_filters('FHEE__EEH_Template__powered_by_event_espresso__url', $url); |
@@ -1066,12 +1066,12 @@ discard block |
||
| 1066 | 1066 | */ |
| 1067 | 1067 | public static function getScreenshotUrl($image_name) |
| 1068 | 1068 | { |
| 1069 | - return esc_url_raw(EE_GLOBAL_ASSETS_URL . 'images/screenshots/' . $image_name . '.jpg'); |
|
| 1069 | + return esc_url_raw(EE_GLOBAL_ASSETS_URL.'images/screenshots/'.$image_name.'.jpg'); |
|
| 1070 | 1070 | } |
| 1071 | 1071 | } |
| 1072 | 1072 | |
| 1073 | 1073 | |
| 1074 | -if (! function_exists('espresso_pagination')) { |
|
| 1074 | +if ( ! function_exists('espresso_pagination')) { |
|
| 1075 | 1075 | /** |
| 1076 | 1076 | * espresso_pagination |
| 1077 | 1077 | * |
@@ -1099,6 +1099,6 @@ discard block |
||
| 1099 | 1099 | 'add_fragment' => '', |
| 1100 | 1100 | ] |
| 1101 | 1101 | ); |
| 1102 | - echo ! empty($pagination) ? '<div class="ee-pagination-dv ee-clear-float">' . $pagination . '</div>' : ''; |
|
| 1102 | + echo ! empty($pagination) ? '<div class="ee-pagination-dv ee-clear-float">'.$pagination.'</div>' : ''; |
|
| 1103 | 1103 | } |
| 1104 | 1104 | } |
@@ -23,2109 +23,2109 @@ |
||
| 23 | 23 | */ |
| 24 | 24 | class EEH_Line_Item |
| 25 | 25 | { |
| 26 | - /** |
|
| 27 | - * @return CurrencyFormatter |
|
| 28 | - * @since $VID:$ |
|
| 29 | - */ |
|
| 30 | - private static function currencyFormatter() |
|
| 31 | - { |
|
| 32 | - static $currency_formatter; |
|
| 33 | - if (! $currency_formatter instanceof CurrencyFormatter) { |
|
| 34 | - $currency_formatter = LoaderFactory::getLoader()->getShared(CurrencyFormatter::class); |
|
| 35 | - } |
|
| 36 | - return $currency_formatter; |
|
| 37 | - } |
|
| 38 | - |
|
| 39 | - /** |
|
| 40 | - * Adds a simple item (unrelated to any other model object) to the provided PARENT line item. |
|
| 41 | - * Does NOT automatically re-calculate the line item totals or update the related transaction. |
|
| 42 | - * You should call recalculate_total_including_taxes() on the grant total line item after this |
|
| 43 | - * to update the subtotals, and EE_Registration_Processor::calculate_reg_final_prices_per_line_item() |
|
| 44 | - * to keep the registration final prices in-sync with the transaction's total. |
|
| 45 | - * |
|
| 46 | - * @param EE_Line_Item $parent_line_item |
|
| 47 | - * @param string $name |
|
| 48 | - * @param float $unit_price |
|
| 49 | - * @param string $description |
|
| 50 | - * @param int $quantity |
|
| 51 | - * @param boolean $taxable |
|
| 52 | - * @param boolean $code if set to a value, ensures there is only one line item with that code |
|
| 53 | - * @return boolean success |
|
| 54 | - * @throws EE_Error |
|
| 55 | - * @throws InvalidArgumentException |
|
| 56 | - * @throws InvalidDataTypeException |
|
| 57 | - * @throws InvalidInterfaceException |
|
| 58 | - * @throws ReflectionException |
|
| 59 | - */ |
|
| 60 | - public static function add_unrelated_item( |
|
| 61 | - EE_Line_Item $parent_line_item, |
|
| 62 | - $name, |
|
| 63 | - $unit_price, |
|
| 64 | - $description = '', |
|
| 65 | - $quantity = 1, |
|
| 66 | - $taxable = false, |
|
| 67 | - $code = null |
|
| 68 | - ) { |
|
| 69 | - $items_subtotal = self::get_pre_tax_subtotal($parent_line_item); |
|
| 70 | - $line_item = EE_Line_Item::new_instance( |
|
| 71 | - [ |
|
| 72 | - 'LIN_name' => $name, |
|
| 73 | - 'LIN_desc' => $description, |
|
| 74 | - 'LIN_unit_price' => $unit_price, |
|
| 75 | - 'LIN_quantity' => $quantity, |
|
| 76 | - 'LIN_percent' => null, |
|
| 77 | - 'LIN_is_taxable' => $taxable, |
|
| 78 | - 'LIN_order' => $items_subtotal instanceof EE_Line_Item ? count($items_subtotal->children()) : 0, |
|
| 79 | - 'LIN_total' => EEH_Line_Item::currencyFormatter()->roundForLocale( |
|
| 80 | - (float) $unit_price * (int) $quantity |
|
| 81 | - ), |
|
| 82 | - 'LIN_type' => EEM_Line_Item::type_line_item, |
|
| 83 | - 'LIN_code' => $code, |
|
| 84 | - ] |
|
| 85 | - ); |
|
| 86 | - $line_item = apply_filters( |
|
| 87 | - 'FHEE__EEH_Line_Item__add_unrelated_item__line_item', |
|
| 88 | - $line_item, |
|
| 89 | - $parent_line_item |
|
| 90 | - ); |
|
| 91 | - return self::add_item($parent_line_item, $line_item); |
|
| 92 | - } |
|
| 93 | - |
|
| 94 | - |
|
| 95 | - /** |
|
| 96 | - * Adds a simple item ( unrelated to any other model object) to the total line item, |
|
| 97 | - * in the correct spot in the line item tree. Does not automatically |
|
| 98 | - * re-calculate the line item totals, nor update the related transaction, nor upgrade the transaction's |
|
| 99 | - * registrations' final prices (which should probably change because of this). |
|
| 100 | - * You should call recalculate_total_including_taxes() on the grand total line item, then |
|
| 101 | - * update the transaction's total, and EE_Registration_Processor::update_registration_final_prices() |
|
| 102 | - * after using this, to keep the registration final prices in-sync with the transaction's total. |
|
| 103 | - * |
|
| 104 | - * @param EE_Line_Item $parent_line_item |
|
| 105 | - * @param string $name |
|
| 106 | - * @param float $percentage_amount |
|
| 107 | - * @param string $description |
|
| 108 | - * @param boolean $taxable |
|
| 109 | - * @return boolean success |
|
| 110 | - * @throws EE_Error|ReflectionException |
|
| 111 | - */ |
|
| 112 | - public static function add_percentage_based_item( |
|
| 113 | - EE_Line_Item $parent_line_item, |
|
| 114 | - $name, |
|
| 115 | - $percentage_amount, |
|
| 116 | - $description = '', |
|
| 117 | - $taxable = false |
|
| 118 | - ) { |
|
| 119 | - $line_item = EE_Line_Item::new_instance( |
|
| 120 | - [ |
|
| 121 | - 'LIN_name' => $name, |
|
| 122 | - 'LIN_desc' => $description, |
|
| 123 | - 'LIN_unit_price' => 0, |
|
| 124 | - 'LIN_percent' => $percentage_amount, |
|
| 125 | - 'LIN_quantity' => 1, |
|
| 126 | - 'LIN_is_taxable' => $taxable, |
|
| 127 | - 'LIN_total' => EEH_Line_Item::currencyFormatter()->roundForLocale( |
|
| 128 | - $percentage_amount * $parent_line_item->total() / 100 |
|
| 129 | - ), |
|
| 130 | - 'LIN_type' => EEM_Line_Item::type_line_item, |
|
| 131 | - 'LIN_parent' => $parent_line_item->ID(), |
|
| 132 | - ] |
|
| 133 | - ); |
|
| 134 | - $line_item = apply_filters( |
|
| 135 | - 'FHEE__EEH_Line_Item__add_percentage_based_item__line_item', |
|
| 136 | - $line_item |
|
| 137 | - ); |
|
| 138 | - return $parent_line_item->add_child_line_item($line_item, false); |
|
| 139 | - } |
|
| 140 | - |
|
| 141 | - |
|
| 142 | - /** |
|
| 143 | - * Returns the new line item created by adding a purchase of the ticket |
|
| 144 | - * ensures that ticket line item is saved, and that cart total has been recalculated. |
|
| 145 | - * If this ticket has already been purchased, just increments its count. |
|
| 146 | - * Automatically re-calculates the line item totals and updates the related transaction. But |
|
| 147 | - * DOES NOT automatically upgrade the transaction's registrations' final prices (which |
|
| 148 | - * should probably change because of this). |
|
| 149 | - * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item() |
|
| 150 | - * after using this, to keep the registration final prices in-sync with the transaction's total. |
|
| 151 | - * |
|
| 152 | - * @param EE_Line_Item $total_line_item grand total line item of type EEM_Line_Item::type_total |
|
| 153 | - * @param EE_Ticket $ticket |
|
| 154 | - * @param int $qty |
|
| 155 | - * @return EE_Line_Item |
|
| 156 | - * @throws EE_Error |
|
| 157 | - * @throws InvalidArgumentException |
|
| 158 | - * @throws InvalidDataTypeException |
|
| 159 | - * @throws InvalidInterfaceException |
|
| 160 | - * @throws ReflectionException |
|
| 161 | - */ |
|
| 162 | - public static function add_ticket_purchase(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1) |
|
| 163 | - { |
|
| 164 | - if (! $total_line_item instanceof EE_Line_Item || ! $total_line_item->is_total()) { |
|
| 165 | - throw new EE_Error( |
|
| 166 | - sprintf( |
|
| 167 | - esc_html__( |
|
| 168 | - 'A valid line item total is required in order to add tickets. A line item of type "%s" was passed.', |
|
| 169 | - 'event_espresso' |
|
| 170 | - ), |
|
| 171 | - $ticket->ID(), |
|
| 172 | - $total_line_item->ID() |
|
| 173 | - ) |
|
| 174 | - ); |
|
| 175 | - } |
|
| 176 | - // either increment the qty for an existing ticket |
|
| 177 | - $line_item = self::increment_ticket_qty_if_already_in_cart($total_line_item, $ticket, $qty); |
|
| 178 | - // or add a new one |
|
| 179 | - if (! $line_item instanceof EE_Line_Item) { |
|
| 180 | - $line_item = self::create_ticket_line_item($total_line_item, $ticket, $qty); |
|
| 181 | - } |
|
| 182 | - $total_line_item->recalculate_total_including_taxes(); |
|
| 183 | - return $line_item; |
|
| 184 | - } |
|
| 185 | - |
|
| 186 | - |
|
| 187 | - /** |
|
| 188 | - * Returns the new line item created by adding a purchase of the ticket |
|
| 189 | - * |
|
| 190 | - * @param EE_Line_Item $total_line_item |
|
| 191 | - * @param EE_Ticket $ticket |
|
| 192 | - * @param int $qty |
|
| 193 | - * @return EE_Line_Item |
|
| 194 | - * @throws EE_Error |
|
| 195 | - * @throws InvalidArgumentException |
|
| 196 | - * @throws InvalidDataTypeException |
|
| 197 | - * @throws InvalidInterfaceException |
|
| 198 | - * @throws ReflectionException |
|
| 199 | - */ |
|
| 200 | - public static function increment_ticket_qty_if_already_in_cart( |
|
| 201 | - EE_Line_Item $total_line_item, |
|
| 202 | - EE_Ticket $ticket, |
|
| 203 | - $qty = 1 |
|
| 204 | - ) { |
|
| 205 | - $line_item = null; |
|
| 206 | - if ($total_line_item instanceof EE_Line_Item && $total_line_item->is_total()) { |
|
| 207 | - $ticket_line_items = EEH_Line_Item::get_ticket_line_items($total_line_item); |
|
| 208 | - foreach ((array) $ticket_line_items as $ticket_line_item) { |
|
| 209 | - if ( |
|
| 210 | - $ticket_line_item instanceof EE_Line_Item |
|
| 211 | - && (int) $ticket_line_item->OBJ_ID() === (int) $ticket->ID() |
|
| 212 | - ) { |
|
| 213 | - $line_item = $ticket_line_item; |
|
| 214 | - break; |
|
| 215 | - } |
|
| 216 | - } |
|
| 217 | - } |
|
| 218 | - if ($line_item instanceof EE_Line_Item) { |
|
| 219 | - EEH_Line_Item::increment_quantity($line_item, $qty); |
|
| 220 | - return $line_item; |
|
| 221 | - } |
|
| 222 | - return null; |
|
| 223 | - } |
|
| 224 | - |
|
| 225 | - |
|
| 226 | - /** |
|
| 227 | - * Increments the line item and all its children's quantity by $qty (but percent line items are unaffected). |
|
| 228 | - * Does NOT save or recalculate other line items totals |
|
| 229 | - * |
|
| 230 | - * @param EE_Line_Item $line_item |
|
| 231 | - * @param int $qty |
|
| 232 | - * @return void |
|
| 233 | - * @throws EE_Error |
|
| 234 | - * @throws InvalidArgumentException |
|
| 235 | - * @throws InvalidDataTypeException |
|
| 236 | - * @throws InvalidInterfaceException |
|
| 237 | - * @throws ReflectionException |
|
| 238 | - */ |
|
| 239 | - public static function increment_quantity(EE_Line_Item $line_item, $qty = 1) |
|
| 240 | - { |
|
| 241 | - if (! $line_item->is_percent()) { |
|
| 242 | - $qty += $line_item->quantity(); |
|
| 243 | - $line_item->set_quantity($qty); |
|
| 244 | - $line_item->set_total($line_item->unit_price() * $qty); |
|
| 245 | - $line_item->save(); |
|
| 246 | - } |
|
| 247 | - foreach ($line_item->children() as $child) { |
|
| 248 | - if ($child->is_sub_line_item()) { |
|
| 249 | - EEH_Line_Item::update_quantity($child, $qty); |
|
| 250 | - } |
|
| 251 | - } |
|
| 252 | - } |
|
| 253 | - |
|
| 254 | - |
|
| 255 | - /** |
|
| 256 | - * Decrements the line item and all its children's quantity by $qty (but percent line items are unaffected). |
|
| 257 | - * Does NOT save or recalculate other line items totals |
|
| 258 | - * |
|
| 259 | - * @param EE_Line_Item $line_item |
|
| 260 | - * @param int $qty |
|
| 261 | - * @return void |
|
| 262 | - * @throws EE_Error |
|
| 263 | - * @throws InvalidArgumentException |
|
| 264 | - * @throws InvalidDataTypeException |
|
| 265 | - * @throws InvalidInterfaceException |
|
| 266 | - * @throws ReflectionException |
|
| 267 | - */ |
|
| 268 | - public static function decrement_quantity(EE_Line_Item $line_item, $qty = 1) |
|
| 269 | - { |
|
| 270 | - if (! $line_item->is_percent()) { |
|
| 271 | - $qty = $line_item->quantity() - $qty; |
|
| 272 | - $qty = max($qty, 0); |
|
| 273 | - $line_item->set_quantity($qty); |
|
| 274 | - $line_item->set_total($line_item->unit_price() * $qty); |
|
| 275 | - $line_item->save(); |
|
| 276 | - } |
|
| 277 | - foreach ($line_item->children() as $child) { |
|
| 278 | - if ($child->is_sub_line_item()) { |
|
| 279 | - EEH_Line_Item::update_quantity($child, $qty); |
|
| 280 | - } |
|
| 281 | - } |
|
| 282 | - } |
|
| 283 | - |
|
| 284 | - |
|
| 285 | - /** |
|
| 286 | - * Updates the line item and its children's quantities to the specified number. |
|
| 287 | - * Does NOT save them or recalculate totals. |
|
| 288 | - * |
|
| 289 | - * @param EE_Line_Item $line_item |
|
| 290 | - * @param int $new_quantity |
|
| 291 | - * @throws EE_Error |
|
| 292 | - * @throws InvalidArgumentException |
|
| 293 | - * @throws InvalidDataTypeException |
|
| 294 | - * @throws InvalidInterfaceException |
|
| 295 | - * @throws ReflectionException |
|
| 296 | - */ |
|
| 297 | - public static function update_quantity(EE_Line_Item $line_item, $new_quantity) |
|
| 298 | - { |
|
| 299 | - if (! $line_item->is_percent()) { |
|
| 300 | - $line_item->set_quantity($new_quantity); |
|
| 301 | - $line_item->set_total($line_item->unit_price() * $new_quantity); |
|
| 302 | - $line_item->save(); |
|
| 303 | - } |
|
| 304 | - foreach ($line_item->children() as $child) { |
|
| 305 | - if ($child->is_sub_line_item()) { |
|
| 306 | - EEH_Line_Item::update_quantity($child, $new_quantity); |
|
| 307 | - } |
|
| 308 | - } |
|
| 309 | - } |
|
| 310 | - |
|
| 311 | - |
|
| 312 | - /** |
|
| 313 | - * Returns the new line item created by adding a purchase of the ticket |
|
| 314 | - * |
|
| 315 | - * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total |
|
| 316 | - * @param EE_Ticket $ticket |
|
| 317 | - * @param int $qty |
|
| 318 | - * @return EE_Line_Item |
|
| 319 | - * @throws EE_Error |
|
| 320 | - * @throws InvalidArgumentException |
|
| 321 | - * @throws InvalidDataTypeException |
|
| 322 | - * @throws InvalidInterfaceException |
|
| 323 | - * @throws ReflectionException |
|
| 324 | - */ |
|
| 325 | - public static function create_ticket_line_item(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1) |
|
| 326 | - { |
|
| 327 | - $datetimes = $ticket->datetimes(); |
|
| 328 | - $first_datetime = reset($datetimes); |
|
| 329 | - $first_datetime_name = esc_html__('Event', 'event_espresso'); |
|
| 330 | - if ($first_datetime instanceof EE_Datetime && $first_datetime->event() instanceof EE_Event) { |
|
| 331 | - $first_datetime_name = $first_datetime->event()->name(); |
|
| 332 | - } |
|
| 333 | - $event = sprintf(_x('(For %1$s)', '(For Event Name)', 'event_espresso'), $first_datetime_name); |
|
| 334 | - // get event subtotal line |
|
| 335 | - $events_sub_total = self::get_event_line_item_for_ticket($total_line_item, $ticket); |
|
| 336 | - // add $ticket to cart |
|
| 337 | - $line_item = EE_Line_Item::new_instance( |
|
| 338 | - [ |
|
| 339 | - 'LIN_name' => $ticket->name(), |
|
| 340 | - 'LIN_desc' => $ticket->description() !== '' ? $ticket->description() . ' ' . $event : $event, |
|
| 341 | - 'LIN_unit_price' => $ticket->price(), |
|
| 342 | - 'LIN_quantity' => $qty, |
|
| 343 | - 'LIN_is_taxable' => $ticket->taxable(), |
|
| 344 | - 'LIN_order' => count($events_sub_total->children()), |
|
| 345 | - 'LIN_total' => $ticket->price() * $qty, |
|
| 346 | - 'LIN_type' => EEM_Line_Item::type_line_item, |
|
| 347 | - 'OBJ_ID' => $ticket->ID(), |
|
| 348 | - 'OBJ_type' => EEM_Line_Item::OBJ_TYPE_TICKET, |
|
| 349 | - ] |
|
| 350 | - ); |
|
| 351 | - $line_item = apply_filters( |
|
| 352 | - 'FHEE__EEH_Line_Item__create_ticket_line_item__line_item', |
|
| 353 | - $line_item |
|
| 354 | - ); |
|
| 355 | - $events_sub_total->add_child_line_item($line_item); |
|
| 356 | - // now add the sub-line items |
|
| 357 | - $running_total_for_ticket = 0; |
|
| 358 | - foreach ($ticket->prices(['order_by' => ['PRC_order' => 'ASC']]) as $price) { |
|
| 359 | - $sign = $price->is_discount() ? -1 : 1; |
|
| 360 | - $price_total = $price->is_percent() |
|
| 361 | - ? $sign * $running_total_for_ticket * $price->amount() / 100 |
|
| 362 | - : $sign * $price->amount() * $qty; |
|
| 363 | - $price_total = EEH_Line_Item::currencyFormatter()->roundForLocale($price_total); |
|
| 364 | - $sub_line_item = EE_Line_Item::new_instance( |
|
| 365 | - [ |
|
| 366 | - 'LIN_name' => $price->name(), |
|
| 367 | - 'LIN_desc' => $price->desc(), |
|
| 368 | - 'LIN_quantity' => $price->is_percent() ? null : $qty, |
|
| 369 | - 'LIN_is_taxable' => false, |
|
| 370 | - 'LIN_order' => $price->order(), |
|
| 371 | - 'LIN_total' => $price_total, |
|
| 372 | - 'LIN_type' => EEM_Line_Item::type_sub_line_item, |
|
| 373 | - 'OBJ_ID' => $price->ID(), |
|
| 374 | - 'OBJ_type' => EEM_Line_Item::OBJ_TYPE_PRICE, |
|
| 375 | - ] |
|
| 376 | - ); |
|
| 377 | - /** @var EE_Line_Item $sub_line_item */ |
|
| 378 | - $sub_line_item = apply_filters( |
|
| 379 | - 'FHEE__EEH_Line_Item__create_ticket_line_item__sub_line_item', |
|
| 380 | - $sub_line_item |
|
| 381 | - ); |
|
| 382 | - if ($price->is_percent()) { |
|
| 383 | - $sub_line_item->set_percent($sign * $price->amount()); |
|
| 384 | - } else { |
|
| 385 | - $sub_line_item->set_unit_price($sign * $price->amount()); |
|
| 386 | - } |
|
| 387 | - $running_total_for_ticket += $price_total; |
|
| 388 | - $line_item->add_child_line_item($sub_line_item); |
|
| 389 | - } |
|
| 390 | - return $line_item; |
|
| 391 | - } |
|
| 392 | - |
|
| 393 | - |
|
| 394 | - /** |
|
| 395 | - * Adds the specified item under the pre-tax-sub-total line item. Automatically |
|
| 396 | - * re-calculates the line item totals and updates the related transaction. But |
|
| 397 | - * DOES NOT automatically upgrade the transaction's registrations' final prices (which |
|
| 398 | - * should probably change because of this). |
|
| 399 | - * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item() |
|
| 400 | - * after using this, to keep the registration final prices in-sync with the transaction's total. |
|
| 401 | - * |
|
| 402 | - * @param EE_Line_Item $total_line_item |
|
| 403 | - * @param EE_Line_Item $item to be added |
|
| 404 | - * @return boolean |
|
| 405 | - * @throws EE_Error |
|
| 406 | - * @throws InvalidArgumentException |
|
| 407 | - * @throws InvalidDataTypeException |
|
| 408 | - * @throws InvalidInterfaceException |
|
| 409 | - * @throws ReflectionException |
|
| 410 | - */ |
|
| 411 | - public static function add_item(EE_Line_Item $total_line_item, EE_Line_Item $item) |
|
| 412 | - { |
|
| 413 | - $pre_tax_subtotal = self::get_pre_tax_subtotal($total_line_item); |
|
| 414 | - if ($pre_tax_subtotal instanceof EE_Line_Item) { |
|
| 415 | - $success = $pre_tax_subtotal->add_child_line_item($item); |
|
| 416 | - } else { |
|
| 417 | - return false; |
|
| 418 | - } |
|
| 419 | - $total_line_item->recalculate_total_including_taxes(); |
|
| 420 | - return $success; |
|
| 421 | - } |
|
| 422 | - |
|
| 423 | - |
|
| 424 | - /** |
|
| 425 | - * cancels an existing ticket line item, |
|
| 426 | - * by decrementing it's quantity by 1 and adding a new "type_cancellation" sub-line-item. |
|
| 427 | - * ALL totals and subtotals will NEED TO BE UPDATED after performing this action |
|
| 428 | - * |
|
| 429 | - * @param EE_Line_Item $ticket_line_item |
|
| 430 | - * @param int $qty |
|
| 431 | - * @return bool success |
|
| 432 | - * @throws EE_Error |
|
| 433 | - * @throws InvalidArgumentException |
|
| 434 | - * @throws InvalidDataTypeException |
|
| 435 | - * @throws InvalidInterfaceException |
|
| 436 | - * @throws ReflectionException |
|
| 437 | - */ |
|
| 438 | - public static function cancel_ticket_line_item(EE_Line_Item $ticket_line_item, $qty = 1) |
|
| 439 | - { |
|
| 440 | - // validate incoming line_item |
|
| 441 | - if ($ticket_line_item->OBJ_type() !== EEM_Line_Item::OBJ_TYPE_TICKET) { |
|
| 442 | - throw new EE_Error( |
|
| 443 | - sprintf( |
|
| 444 | - esc_html__( |
|
| 445 | - 'The supplied line item must have an Object Type of "Ticket", not %1$s.', |
|
| 446 | - 'event_espresso' |
|
| 447 | - ), |
|
| 448 | - $ticket_line_item->type() |
|
| 449 | - ) |
|
| 450 | - ); |
|
| 451 | - } |
|
| 452 | - if ($ticket_line_item->quantity() < $qty) { |
|
| 453 | - throw new EE_Error( |
|
| 454 | - sprintf( |
|
| 455 | - esc_html__( |
|
| 456 | - 'Can not cancel %1$d ticket(s) because the supplied line item has a quantity of %2$d.', |
|
| 457 | - 'event_espresso' |
|
| 458 | - ), |
|
| 459 | - $qty, |
|
| 460 | - $ticket_line_item->quantity() |
|
| 461 | - ) |
|
| 462 | - ); |
|
| 463 | - } |
|
| 464 | - // decrement ticket quantity; don't rely on auto-fixing when recalculating totals to do this |
|
| 465 | - $ticket_line_item->set_quantity($ticket_line_item->quantity() - $qty); |
|
| 466 | - foreach ($ticket_line_item->children() as $child_line_item) { |
|
| 467 | - if ( |
|
| 468 | - $child_line_item->is_sub_line_item() |
|
| 469 | - && ! $child_line_item->is_percent() |
|
| 470 | - && ! $child_line_item->is_cancellation() |
|
| 471 | - ) { |
|
| 472 | - $child_line_item->set_quantity($child_line_item->quantity() - $qty); |
|
| 473 | - } |
|
| 474 | - } |
|
| 475 | - // get cancellation sub line item |
|
| 476 | - $cancellation_line_item = EEH_Line_Item::get_descendants_of_type( |
|
| 477 | - $ticket_line_item, |
|
| 478 | - EEM_Line_Item::type_cancellation |
|
| 479 | - ); |
|
| 480 | - $cancellation_line_item = reset($cancellation_line_item); |
|
| 481 | - // verify that this ticket was indeed previously cancelled |
|
| 482 | - if ($cancellation_line_item instanceof EE_Line_Item) { |
|
| 483 | - // increment cancelled quantity |
|
| 484 | - $cancellation_line_item->set_quantity($cancellation_line_item->quantity() + $qty); |
|
| 485 | - } else { |
|
| 486 | - // create cancellation sub line item |
|
| 487 | - $cancellation_line_item = EE_Line_Item::new_instance( |
|
| 488 | - [ |
|
| 489 | - 'LIN_name' => esc_html__('Cancellation', 'event_espresso'), |
|
| 490 | - 'LIN_desc' => sprintf( |
|
| 491 | - esc_html_x( |
|
| 492 | - 'Cancelled %1$s : %2$s', |
|
| 493 | - 'Cancelled Ticket Name : 2015-01-01 11:11', |
|
| 494 | - 'event_espresso' |
|
| 495 | - ), |
|
| 496 | - $ticket_line_item->name(), |
|
| 497 | - current_time(get_option('date_format') . ' ' . get_option('time_format')) |
|
| 498 | - ), |
|
| 499 | - 'LIN_unit_price' => 0, // $ticket_line_item->unit_price() |
|
| 500 | - 'LIN_quantity' => $qty, |
|
| 501 | - 'LIN_is_taxable' => $ticket_line_item->is_taxable(), |
|
| 502 | - 'LIN_order' => count($ticket_line_item->children()), |
|
| 503 | - 'LIN_total' => 0, // $ticket_line_item->unit_price() |
|
| 504 | - 'LIN_type' => EEM_Line_Item::type_cancellation, |
|
| 505 | - ] |
|
| 506 | - ); |
|
| 507 | - $ticket_line_item->add_child_line_item($cancellation_line_item); |
|
| 508 | - } |
|
| 509 | - if ($ticket_line_item->save_this_and_descendants() > 0) { |
|
| 510 | - // decrement parent line item quantity |
|
| 511 | - $event_line_item = $ticket_line_item->parent(); |
|
| 512 | - if ( |
|
| 513 | - $event_line_item instanceof EE_Line_Item |
|
| 514 | - && $event_line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_EVENT |
|
| 515 | - ) { |
|
| 516 | - $event_line_item->set_quantity($event_line_item->quantity() - $qty); |
|
| 517 | - $event_line_item->save(); |
|
| 518 | - } |
|
| 519 | - EEH_Line_Item::get_grand_total_and_recalculate_everything($ticket_line_item); |
|
| 520 | - return true; |
|
| 521 | - } |
|
| 522 | - return false; |
|
| 523 | - } |
|
| 524 | - |
|
| 525 | - |
|
| 526 | - /** |
|
| 527 | - * reinstates (un-cancels?) a previously canceled ticket line item, |
|
| 528 | - * by incrementing it's quantity by 1, and decrementing it's "type_cancellation" sub-line-item. |
|
| 529 | - * ALL totals and subtotals will NEED TO BE UPDATED after performing this action |
|
| 530 | - * |
|
| 531 | - * @param EE_Line_Item $ticket_line_item |
|
| 532 | - * @param int $qty |
|
| 533 | - * @return bool success |
|
| 534 | - * @throws EE_Error |
|
| 535 | - * @throws InvalidArgumentException |
|
| 536 | - * @throws InvalidDataTypeException |
|
| 537 | - * @throws InvalidInterfaceException |
|
| 538 | - * @throws ReflectionException |
|
| 539 | - */ |
|
| 540 | - public static function reinstate_canceled_ticket_line_item(EE_Line_Item $ticket_line_item, $qty = 1) |
|
| 541 | - { |
|
| 542 | - // validate incoming line_item |
|
| 543 | - if ($ticket_line_item->OBJ_type() !== EEM_Line_Item::OBJ_TYPE_TICKET) { |
|
| 544 | - throw new EE_Error( |
|
| 545 | - sprintf( |
|
| 546 | - esc_html__( |
|
| 547 | - 'The supplied line item must have an Object Type of "Ticket", not %1$s.', |
|
| 548 | - 'event_espresso' |
|
| 549 | - ), |
|
| 550 | - $ticket_line_item->type() |
|
| 551 | - ) |
|
| 552 | - ); |
|
| 553 | - } |
|
| 554 | - // get cancellation sub line item |
|
| 555 | - $cancellation_line_item = EEH_Line_Item::get_descendants_of_type( |
|
| 556 | - $ticket_line_item, |
|
| 557 | - EEM_Line_Item::type_cancellation |
|
| 558 | - ); |
|
| 559 | - $cancellation_line_item = reset($cancellation_line_item); |
|
| 560 | - // verify that this ticket was indeed previously cancelled |
|
| 561 | - if (! $cancellation_line_item instanceof EE_Line_Item) { |
|
| 562 | - return false; |
|
| 563 | - } |
|
| 564 | - if ($cancellation_line_item->quantity() > $qty) { |
|
| 565 | - // decrement cancelled quantity |
|
| 566 | - $cancellation_line_item->set_quantity($cancellation_line_item->quantity() - $qty); |
|
| 567 | - } elseif ($cancellation_line_item->quantity() === $qty) { |
|
| 568 | - // decrement cancelled quantity in case anyone still has the object kicking around |
|
| 569 | - $cancellation_line_item->set_quantity($cancellation_line_item->quantity() - $qty); |
|
| 570 | - // delete because quantity will end up as 0 |
|
| 571 | - $cancellation_line_item->delete(); |
|
| 572 | - // and attempt to destroy the object, |
|
| 573 | - // even though PHP won't actually destroy it until it needs the memory |
|
| 574 | - unset($cancellation_line_item); |
|
| 575 | - } else { |
|
| 576 | - // what ?!?! negative quantity ?!?! |
|
| 577 | - throw new EE_Error( |
|
| 578 | - sprintf( |
|
| 579 | - esc_html__( |
|
| 580 | - 'Can not reinstate %1$d cancelled ticket(s) because the cancelled ticket quantity is only %2$d.', |
|
| 581 | - 'event_espresso' |
|
| 582 | - ), |
|
| 583 | - $qty, |
|
| 584 | - $cancellation_line_item->quantity() |
|
| 585 | - ) |
|
| 586 | - ); |
|
| 587 | - } |
|
| 588 | - // increment ticket quantity |
|
| 589 | - $ticket_line_item->set_quantity($ticket_line_item->quantity() + $qty); |
|
| 590 | - if ($ticket_line_item->save_this_and_descendants() > 0) { |
|
| 591 | - // increment parent line item quantity |
|
| 592 | - $event_line_item = $ticket_line_item->parent(); |
|
| 593 | - if ( |
|
| 594 | - $event_line_item instanceof EE_Line_Item |
|
| 595 | - && $event_line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_EVENT |
|
| 596 | - ) { |
|
| 597 | - $event_line_item->set_quantity($event_line_item->quantity() + $qty); |
|
| 598 | - } |
|
| 599 | - EEH_Line_Item::get_grand_total_and_recalculate_everything($ticket_line_item); |
|
| 600 | - return true; |
|
| 601 | - } |
|
| 602 | - return false; |
|
| 603 | - } |
|
| 604 | - |
|
| 605 | - |
|
| 606 | - /** |
|
| 607 | - * calls EEH_Line_Item::find_transaction_grand_total_for_line_item() |
|
| 608 | - * then EE_Line_Item::recalculate_total_including_taxes() on the result |
|
| 609 | - * |
|
| 610 | - * @param EE_Line_Item $line_item |
|
| 611 | - * @return float |
|
| 612 | - * @throws EE_Error |
|
| 613 | - * @throws InvalidArgumentException |
|
| 614 | - * @throws InvalidDataTypeException |
|
| 615 | - * @throws InvalidInterfaceException |
|
| 616 | - * @throws ReflectionException |
|
| 617 | - */ |
|
| 618 | - public static function get_grand_total_and_recalculate_everything(EE_Line_Item $line_item) |
|
| 619 | - { |
|
| 620 | - $grand_total_line_item = EEH_Line_Item::find_transaction_grand_total_for_line_item($line_item); |
|
| 621 | - return $grand_total_line_item->recalculate_total_including_taxes(); |
|
| 622 | - } |
|
| 623 | - |
|
| 624 | - |
|
| 625 | - /** |
|
| 626 | - * Gets the line item which contains the subtotal of all the items |
|
| 627 | - * |
|
| 628 | - * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total |
|
| 629 | - * @return EE_Line_Item |
|
| 630 | - * @throws EE_Error |
|
| 631 | - * @throws InvalidArgumentException |
|
| 632 | - * @throws InvalidDataTypeException |
|
| 633 | - * @throws InvalidInterfaceException |
|
| 634 | - * @throws ReflectionException |
|
| 635 | - */ |
|
| 636 | - public static function get_pre_tax_subtotal(EE_Line_Item $total_line_item) |
|
| 637 | - { |
|
| 638 | - $pre_tax_subtotal = $total_line_item->get_child_line_item('pre-tax-subtotal'); |
|
| 639 | - return $pre_tax_subtotal instanceof EE_Line_Item |
|
| 640 | - ? $pre_tax_subtotal |
|
| 641 | - : self::create_pre_tax_subtotal($total_line_item); |
|
| 642 | - } |
|
| 643 | - |
|
| 644 | - |
|
| 645 | - /** |
|
| 646 | - * Gets the line item for the taxes subtotal |
|
| 647 | - * |
|
| 648 | - * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total |
|
| 649 | - * @return EE_Line_Item |
|
| 650 | - * @throws EE_Error |
|
| 651 | - * @throws InvalidArgumentException |
|
| 652 | - * @throws InvalidDataTypeException |
|
| 653 | - * @throws InvalidInterfaceException |
|
| 654 | - * @throws ReflectionException |
|
| 655 | - */ |
|
| 656 | - public static function get_taxes_subtotal(EE_Line_Item $total_line_item) |
|
| 657 | - { |
|
| 658 | - $taxes = $total_line_item->get_child_line_item('taxes'); |
|
| 659 | - return $taxes ? $taxes : self::create_taxes_subtotal($total_line_item); |
|
| 660 | - } |
|
| 661 | - |
|
| 662 | - |
|
| 663 | - /** |
|
| 664 | - * sets the TXN ID on an EE_Line_Item if passed a valid EE_Transaction object |
|
| 665 | - * |
|
| 666 | - * @param EE_Line_Item $line_item |
|
| 667 | - * @param EE_Transaction $transaction |
|
| 668 | - * @return void |
|
| 669 | - * @throws EE_Error |
|
| 670 | - * @throws InvalidArgumentException |
|
| 671 | - * @throws InvalidDataTypeException |
|
| 672 | - * @throws InvalidInterfaceException |
|
| 673 | - * @throws ReflectionException |
|
| 674 | - */ |
|
| 675 | - public static function set_TXN_ID(EE_Line_Item $line_item, $transaction = null) |
|
| 676 | - { |
|
| 677 | - if ($transaction) { |
|
| 678 | - /** @type EEM_Transaction $EEM_Transaction */ |
|
| 679 | - $EEM_Transaction = EE_Registry::instance()->load_model('Transaction'); |
|
| 680 | - $TXN_ID = $EEM_Transaction->ensure_is_ID($transaction); |
|
| 681 | - $line_item->set_TXN_ID($TXN_ID); |
|
| 682 | - } |
|
| 683 | - } |
|
| 684 | - |
|
| 685 | - |
|
| 686 | - /** |
|
| 687 | - * Creates a new default total line item for the transaction, |
|
| 688 | - * and its tickets subtotal and taxes subtotal line items (and adds the |
|
| 689 | - * existing taxes as children of the taxes subtotal line item) |
|
| 690 | - * |
|
| 691 | - * @param EE_Transaction $transaction |
|
| 692 | - * @return EE_Line_Item of type total |
|
| 693 | - * @throws EE_Error |
|
| 694 | - * @throws InvalidArgumentException |
|
| 695 | - * @throws InvalidDataTypeException |
|
| 696 | - * @throws InvalidInterfaceException |
|
| 697 | - * @throws ReflectionException |
|
| 698 | - */ |
|
| 699 | - public static function create_total_line_item($transaction = null) |
|
| 700 | - { |
|
| 701 | - $total_line_item = EE_Line_Item::new_instance( |
|
| 702 | - [ |
|
| 703 | - 'LIN_code' => 'total', |
|
| 704 | - 'LIN_name' => esc_html__('Grand Total', 'event_espresso'), |
|
| 705 | - 'LIN_type' => EEM_Line_Item::type_total, |
|
| 706 | - 'OBJ_type' => EEM_Line_Item::OBJ_TYPE_TRANSACTION, |
|
| 707 | - ] |
|
| 708 | - ); |
|
| 709 | - $total_line_item = apply_filters( |
|
| 710 | - 'FHEE__EEH_Line_Item__create_total_line_item__total_line_item', |
|
| 711 | - $total_line_item |
|
| 712 | - ); |
|
| 713 | - self::set_TXN_ID($total_line_item, $transaction); |
|
| 714 | - self::create_pre_tax_subtotal($total_line_item, $transaction); |
|
| 715 | - self::create_taxes_subtotal($total_line_item, $transaction); |
|
| 716 | - return $total_line_item; |
|
| 717 | - } |
|
| 718 | - |
|
| 719 | - |
|
| 720 | - /** |
|
| 721 | - * Creates a default items subtotal line item |
|
| 722 | - * |
|
| 723 | - * @param EE_Line_Item $total_line_item |
|
| 724 | - * @param EE_Transaction $transaction |
|
| 725 | - * @return EE_Line_Item |
|
| 726 | - * @throws EE_Error |
|
| 727 | - * @throws InvalidArgumentException |
|
| 728 | - * @throws InvalidDataTypeException |
|
| 729 | - * @throws InvalidInterfaceException |
|
| 730 | - * @throws ReflectionException |
|
| 731 | - */ |
|
| 732 | - protected static function create_pre_tax_subtotal(EE_Line_Item $total_line_item, $transaction = null) |
|
| 733 | - { |
|
| 734 | - $pre_tax_line_item = EE_Line_Item::new_instance( |
|
| 735 | - [ |
|
| 736 | - 'LIN_code' => 'pre-tax-subtotal', |
|
| 737 | - 'LIN_name' => esc_html__('Pre-Tax Subtotal', 'event_espresso'), |
|
| 738 | - 'LIN_type' => EEM_Line_Item::type_sub_total, |
|
| 739 | - ] |
|
| 740 | - ); |
|
| 741 | - $pre_tax_line_item = apply_filters( |
|
| 742 | - 'FHEE__EEH_Line_Item__create_pre_tax_subtotal__pre_tax_line_item', |
|
| 743 | - $pre_tax_line_item |
|
| 744 | - ); |
|
| 745 | - self::set_TXN_ID($pre_tax_line_item, $transaction); |
|
| 746 | - $total_line_item->add_child_line_item($pre_tax_line_item); |
|
| 747 | - self::create_event_subtotal($pre_tax_line_item, $transaction); |
|
| 748 | - return $pre_tax_line_item; |
|
| 749 | - } |
|
| 750 | - |
|
| 751 | - |
|
| 752 | - /** |
|
| 753 | - * Creates a line item for the taxes subtotal and finds all the tax prices |
|
| 754 | - * and applies taxes to it |
|
| 755 | - * |
|
| 756 | - * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total |
|
| 757 | - * @param EE_Transaction $transaction |
|
| 758 | - * @return EE_Line_Item |
|
| 759 | - * @throws EE_Error |
|
| 760 | - * @throws InvalidArgumentException |
|
| 761 | - * @throws InvalidDataTypeException |
|
| 762 | - * @throws InvalidInterfaceException |
|
| 763 | - * @throws ReflectionException |
|
| 764 | - */ |
|
| 765 | - protected static function create_taxes_subtotal(EE_Line_Item $total_line_item, $transaction = null) |
|
| 766 | - { |
|
| 767 | - $tax_line_item = EE_Line_Item::new_instance( |
|
| 768 | - [ |
|
| 769 | - 'LIN_code' => 'taxes', |
|
| 770 | - 'LIN_name' => esc_html__('Taxes', 'event_espresso'), |
|
| 771 | - 'LIN_type' => EEM_Line_Item::type_tax_sub_total, |
|
| 772 | - 'LIN_order' => 1000,// this should always come last |
|
| 773 | - ] |
|
| 774 | - ); |
|
| 775 | - $tax_line_item = apply_filters( |
|
| 776 | - 'FHEE__EEH_Line_Item__create_taxes_subtotal__tax_line_item', |
|
| 777 | - $tax_line_item |
|
| 778 | - ); |
|
| 779 | - self::set_TXN_ID($tax_line_item, $transaction); |
|
| 780 | - $total_line_item->add_child_line_item($tax_line_item); |
|
| 781 | - // and lastly, add the actual taxes |
|
| 782 | - self::apply_taxes($total_line_item); |
|
| 783 | - return $tax_line_item; |
|
| 784 | - } |
|
| 785 | - |
|
| 786 | - |
|
| 787 | - /** |
|
| 788 | - * Creates a default items subtotal line item |
|
| 789 | - * |
|
| 790 | - * @param EE_Line_Item $pre_tax_line_item |
|
| 791 | - * @param EE_Transaction $transaction |
|
| 792 | - * @param EE_Event $event |
|
| 793 | - * @return EE_Line_Item |
|
| 794 | - * @throws EE_Error |
|
| 795 | - * @throws InvalidArgumentException |
|
| 796 | - * @throws InvalidDataTypeException |
|
| 797 | - * @throws InvalidInterfaceException |
|
| 798 | - * @throws ReflectionException |
|
| 799 | - */ |
|
| 800 | - public static function create_event_subtotal(EE_Line_Item $pre_tax_line_item, $transaction = null, $event = null) |
|
| 801 | - { |
|
| 802 | - $event_line_item = EE_Line_Item::new_instance( |
|
| 803 | - [ |
|
| 804 | - 'LIN_code' => self::get_event_code($event), |
|
| 805 | - 'LIN_name' => self::get_event_name($event), |
|
| 806 | - 'LIN_desc' => self::get_event_desc($event), |
|
| 807 | - 'LIN_type' => EEM_Line_Item::type_sub_total, |
|
| 808 | - 'OBJ_type' => EEM_Line_Item::OBJ_TYPE_EVENT, |
|
| 809 | - 'OBJ_ID' => $event instanceof EE_Event ? $event->ID() : 0, |
|
| 810 | - ] |
|
| 811 | - ); |
|
| 812 | - $event_line_item = apply_filters( |
|
| 813 | - 'FHEE__EEH_Line_Item__create_event_subtotal__event_line_item', |
|
| 814 | - $event_line_item |
|
| 815 | - ); |
|
| 816 | - self::set_TXN_ID($event_line_item, $transaction); |
|
| 817 | - $pre_tax_line_item->add_child_line_item($event_line_item); |
|
| 818 | - return $event_line_item; |
|
| 819 | - } |
|
| 820 | - |
|
| 821 | - |
|
| 822 | - /** |
|
| 823 | - * Gets what the event ticket's code SHOULD be |
|
| 824 | - * |
|
| 825 | - * @param EE_Event $event |
|
| 826 | - * @return string |
|
| 827 | - * @throws EE_Error|ReflectionException |
|
| 828 | - */ |
|
| 829 | - public static function get_event_code($event) |
|
| 830 | - { |
|
| 831 | - return 'event-' . ($event instanceof EE_Event ? $event->ID() : '0'); |
|
| 832 | - } |
|
| 833 | - |
|
| 834 | - |
|
| 835 | - /** |
|
| 836 | - * Gets the event name |
|
| 837 | - * |
|
| 838 | - * @param EE_Event $event |
|
| 839 | - * @return string |
|
| 840 | - * @throws EE_Error |
|
| 841 | - */ |
|
| 842 | - public static function get_event_name($event) |
|
| 843 | - { |
|
| 844 | - return $event instanceof EE_Event |
|
| 845 | - ? mb_substr($event->name(), 0, 245) |
|
| 846 | - : esc_html__('Event', 'event_espresso'); |
|
| 847 | - } |
|
| 848 | - |
|
| 849 | - |
|
| 850 | - /** |
|
| 851 | - * Gets the event excerpt |
|
| 852 | - * |
|
| 853 | - * @param EE_Event $event |
|
| 854 | - * @return string |
|
| 855 | - * @throws EE_Error |
|
| 856 | - */ |
|
| 857 | - public static function get_event_desc($event) |
|
| 858 | - { |
|
| 859 | - return $event instanceof EE_Event ? $event->short_description() : ''; |
|
| 860 | - } |
|
| 861 | - |
|
| 862 | - |
|
| 863 | - /** |
|
| 864 | - * Given the grand total line item and a ticket, finds the event sub-total |
|
| 865 | - * line item the ticket's purchase should be added onto |
|
| 866 | - * |
|
| 867 | - * @access public |
|
| 868 | - * @param EE_Line_Item $grand_total the grand total line item |
|
| 869 | - * @param EE_Ticket $ticket |
|
| 870 | - * @return EE_Line_Item |
|
| 871 | - * @throws EE_Error |
|
| 872 | - * @throws InvalidArgumentException |
|
| 873 | - * @throws InvalidDataTypeException |
|
| 874 | - * @throws InvalidInterfaceException |
|
| 875 | - * @throws ReflectionException |
|
| 876 | - */ |
|
| 877 | - public static function get_event_line_item_for_ticket(EE_Line_Item $grand_total, EE_Ticket $ticket) |
|
| 878 | - { |
|
| 879 | - $first_datetime = $ticket->first_datetime(); |
|
| 880 | - if (! $first_datetime instanceof EE_Datetime) { |
|
| 881 | - throw new EE_Error( |
|
| 882 | - sprintf( |
|
| 883 | - esc_html__('The supplied ticket (ID %d) has no datetimes', 'event_espresso'), |
|
| 884 | - $ticket->ID() |
|
| 885 | - ) |
|
| 886 | - ); |
|
| 887 | - } |
|
| 888 | - $event = $first_datetime->event(); |
|
| 889 | - if (! $event instanceof EE_Event) { |
|
| 890 | - throw new EE_Error( |
|
| 891 | - sprintf( |
|
| 892 | - esc_html__( |
|
| 893 | - 'The supplied ticket (ID %d) has no event data associated with it.', |
|
| 894 | - 'event_espresso' |
|
| 895 | - ), |
|
| 896 | - $ticket->ID() |
|
| 897 | - ) |
|
| 898 | - ); |
|
| 899 | - } |
|
| 900 | - $events_sub_total = EEH_Line_Item::get_event_line_item($grand_total, $event); |
|
| 901 | - if (! $events_sub_total instanceof EE_Line_Item) { |
|
| 902 | - throw new EE_Error( |
|
| 903 | - sprintf( |
|
| 904 | - esc_html__( |
|
| 905 | - 'There is no events sub-total for ticket %s on total line item %d', |
|
| 906 | - 'event_espresso' |
|
| 907 | - ), |
|
| 908 | - $ticket->ID(), |
|
| 909 | - $grand_total->ID() |
|
| 910 | - ) |
|
| 911 | - ); |
|
| 912 | - } |
|
| 913 | - return $events_sub_total; |
|
| 914 | - } |
|
| 915 | - |
|
| 916 | - |
|
| 917 | - /** |
|
| 918 | - * Gets the event line item |
|
| 919 | - * |
|
| 920 | - * @param EE_Line_Item $grand_total |
|
| 921 | - * @param EE_Event $event |
|
| 922 | - * @return EE_Line_Item for the event subtotal which is a child of $grand_total |
|
| 923 | - * @throws EE_Error |
|
| 924 | - * @throws InvalidArgumentException |
|
| 925 | - * @throws InvalidDataTypeException |
|
| 926 | - * @throws InvalidInterfaceException |
|
| 927 | - * @throws ReflectionException |
|
| 928 | - */ |
|
| 929 | - public static function get_event_line_item(EE_Line_Item $grand_total, $event) |
|
| 930 | - { |
|
| 931 | - /** @type EE_Event $event */ |
|
| 932 | - $event = EEM_Event::instance()->ensure_is_obj($event, true); |
|
| 933 | - $event_line_item = null; |
|
| 934 | - $found = false; |
|
| 935 | - foreach (EEH_Line_Item::get_event_subtotals($grand_total) as $event_line_item) { |
|
| 936 | - // default event subtotal, we should only ever find this the first time this method is called |
|
| 937 | - if (! $event_line_item->OBJ_ID()) { |
|
| 938 | - // let's use this! but first... set the event details |
|
| 939 | - EEH_Line_Item::set_event_subtotal_details($event_line_item, $event); |
|
| 940 | - $found = true; |
|
| 941 | - break; |
|
| 942 | - } |
|
| 943 | - if ($event_line_item->OBJ_ID() === $event->ID()) { |
|
| 944 | - // found existing line item for this event in the cart, so break out of loop and use this one |
|
| 945 | - $found = true; |
|
| 946 | - break; |
|
| 947 | - } |
|
| 948 | - } |
|
| 949 | - if (! $found) { |
|
| 950 | - // there is no event sub-total yet, so add it |
|
| 951 | - $pre_tax_subtotal = EEH_Line_Item::get_pre_tax_subtotal($grand_total); |
|
| 952 | - // create a new "event" subtotal below that |
|
| 953 | - $event_line_item = EEH_Line_Item::create_event_subtotal($pre_tax_subtotal, null, $event); |
|
| 954 | - // and set the event details |
|
| 955 | - EEH_Line_Item::set_event_subtotal_details($event_line_item, $event); |
|
| 956 | - } |
|
| 957 | - return $event_line_item; |
|
| 958 | - } |
|
| 959 | - |
|
| 960 | - |
|
| 961 | - /** |
|
| 962 | - * Creates a default items subtotal line item |
|
| 963 | - * |
|
| 964 | - * @param EE_Line_Item $event_line_item |
|
| 965 | - * @param EE_Event $event |
|
| 966 | - * @param EE_Transaction $transaction |
|
| 967 | - * @return void |
|
| 968 | - * @throws EE_Error |
|
| 969 | - * @throws InvalidArgumentException |
|
| 970 | - * @throws InvalidDataTypeException |
|
| 971 | - * @throws InvalidInterfaceException |
|
| 972 | - * @throws ReflectionException |
|
| 973 | - */ |
|
| 974 | - public static function set_event_subtotal_details( |
|
| 975 | - EE_Line_Item $event_line_item, |
|
| 976 | - EE_Event $event, |
|
| 977 | - $transaction = null |
|
| 978 | - ) { |
|
| 979 | - if ($event instanceof EE_Event) { |
|
| 980 | - $event_line_item->set_code(self::get_event_code($event)); |
|
| 981 | - $event_line_item->set_name(self::get_event_name($event)); |
|
| 982 | - $event_line_item->set_desc(self::get_event_desc($event)); |
|
| 983 | - $event_line_item->set_OBJ_ID($event->ID()); |
|
| 984 | - } |
|
| 985 | - self::set_TXN_ID($event_line_item, $transaction); |
|
| 986 | - } |
|
| 987 | - |
|
| 988 | - |
|
| 989 | - /** |
|
| 990 | - * Finds what taxes should apply, adds them as tax line items under the taxes sub-total, |
|
| 991 | - * and recalculates the taxes sub-total and the grand total. Resets the taxes, so |
|
| 992 | - * any old taxes are removed |
|
| 993 | - * |
|
| 994 | - * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total |
|
| 995 | - * @param bool $update_txn_status |
|
| 996 | - * @return bool |
|
| 997 | - * @throws EE_Error |
|
| 998 | - * @throws InvalidArgumentException |
|
| 999 | - * @throws InvalidDataTypeException |
|
| 1000 | - * @throws InvalidInterfaceException |
|
| 1001 | - * @throws ReflectionException |
|
| 1002 | - * @throws RuntimeException |
|
| 1003 | - */ |
|
| 1004 | - public static function apply_taxes(EE_Line_Item $total_line_item, $update_txn_status = false) |
|
| 1005 | - { |
|
| 1006 | - /** @type EEM_Price $EEM_Price */ |
|
| 1007 | - $EEM_Price = EE_Registry::instance()->load_model('Price'); |
|
| 1008 | - // get array of taxes via Price Model |
|
| 1009 | - $ordered_taxes = $EEM_Price->get_all_prices_that_are_taxes(); |
|
| 1010 | - ksort($ordered_taxes); |
|
| 1011 | - $taxes_line_item = self::get_taxes_subtotal($total_line_item); |
|
| 1012 | - // just to be safe, remove its old tax line items |
|
| 1013 | - $deleted = $taxes_line_item->delete_children_line_items(); |
|
| 1014 | - $updates = false; |
|
| 1015 | - // loop thru taxes |
|
| 1016 | - foreach ($ordered_taxes as $order => $taxes) { |
|
| 1017 | - foreach ($taxes as $tax) { |
|
| 1018 | - if ($tax instanceof EE_Price) { |
|
| 1019 | - $tax_line_item = EE_Line_Item::new_instance( |
|
| 1020 | - [ |
|
| 1021 | - 'LIN_name' => $tax->name(), |
|
| 1022 | - 'LIN_desc' => $tax->desc(), |
|
| 1023 | - 'LIN_percent' => $tax->amount(), |
|
| 1024 | - 'LIN_is_taxable' => false, |
|
| 1025 | - 'LIN_order' => $order, |
|
| 1026 | - 'LIN_total' => 0, |
|
| 1027 | - 'LIN_type' => EEM_Line_Item::type_tax, |
|
| 1028 | - 'OBJ_type' => EEM_Line_Item::OBJ_TYPE_PRICE, |
|
| 1029 | - 'OBJ_ID' => $tax->ID(), |
|
| 1030 | - ] |
|
| 1031 | - ); |
|
| 1032 | - $tax_line_item = apply_filters( |
|
| 1033 | - 'FHEE__EEH_Line_Item__apply_taxes__tax_line_item', |
|
| 1034 | - $tax_line_item |
|
| 1035 | - ); |
|
| 1036 | - $updates = $taxes_line_item->add_child_line_item($tax_line_item) |
|
| 1037 | - ? |
|
| 1038 | - true |
|
| 1039 | - : |
|
| 1040 | - $updates; |
|
| 1041 | - } |
|
| 1042 | - } |
|
| 1043 | - } |
|
| 1044 | - // only recalculate totals if something changed |
|
| 1045 | - if ($deleted || $updates) { |
|
| 1046 | - $total_line_item->recalculate_total_including_taxes($update_txn_status); |
|
| 1047 | - return true; |
|
| 1048 | - } |
|
| 1049 | - return false; |
|
| 1050 | - } |
|
| 1051 | - |
|
| 1052 | - |
|
| 1053 | - /** |
|
| 1054 | - * Ensures that taxes have been applied to the order, if not applies them. |
|
| 1055 | - * Returns the total amount of tax |
|
| 1056 | - * |
|
| 1057 | - * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total |
|
| 1058 | - * @return float |
|
| 1059 | - * @throws EE_Error |
|
| 1060 | - * @throws InvalidArgumentException |
|
| 1061 | - * @throws InvalidDataTypeException |
|
| 1062 | - * @throws InvalidInterfaceException |
|
| 1063 | - * @throws ReflectionException |
|
| 1064 | - */ |
|
| 1065 | - public static function ensure_taxes_applied($total_line_item) |
|
| 1066 | - { |
|
| 1067 | - $taxes_subtotal = self::get_taxes_subtotal($total_line_item); |
|
| 1068 | - if (! $taxes_subtotal->children()) { |
|
| 1069 | - self::apply_taxes($total_line_item); |
|
| 1070 | - } |
|
| 1071 | - return $taxes_subtotal->total(); |
|
| 1072 | - } |
|
| 1073 | - |
|
| 1074 | - |
|
| 1075 | - /** |
|
| 1076 | - * Deletes ALL children of the passed line item |
|
| 1077 | - * |
|
| 1078 | - * @param EE_Line_Item $parent_line_item |
|
| 1079 | - * @return bool |
|
| 1080 | - * @throws EE_Error |
|
| 1081 | - * @throws InvalidArgumentException |
|
| 1082 | - * @throws InvalidDataTypeException |
|
| 1083 | - * @throws InvalidInterfaceException |
|
| 1084 | - * @throws ReflectionException |
|
| 1085 | - */ |
|
| 1086 | - public static function delete_all_child_items(EE_Line_Item $parent_line_item) |
|
| 1087 | - { |
|
| 1088 | - $deleted = 0; |
|
| 1089 | - foreach ($parent_line_item->children() as $child_line_item) { |
|
| 1090 | - if ($child_line_item instanceof EE_Line_Item) { |
|
| 1091 | - $deleted += EEH_Line_Item::delete_all_child_items($child_line_item); |
|
| 1092 | - if ($child_line_item->ID()) { |
|
| 1093 | - $child_line_item->delete(); |
|
| 1094 | - unset($child_line_item); |
|
| 1095 | - } else { |
|
| 1096 | - $parent_line_item->delete_child_line_item($child_line_item->code()); |
|
| 1097 | - } |
|
| 1098 | - $deleted++; |
|
| 1099 | - } |
|
| 1100 | - } |
|
| 1101 | - return $deleted; |
|
| 1102 | - } |
|
| 1103 | - |
|
| 1104 | - |
|
| 1105 | - /** |
|
| 1106 | - * Deletes the line items as indicated by the line item code(s) provided, |
|
| 1107 | - * regardless of where they're found in the line item tree. Automatically |
|
| 1108 | - * re-calculates the line item totals and updates the related transaction. But |
|
| 1109 | - * DOES NOT automatically upgrade the transaction's registrations' final prices (which |
|
| 1110 | - * should probably change because of this). |
|
| 1111 | - * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item() |
|
| 1112 | - * after using this, to keep the registration final prices in-sync with the transaction's total. |
|
| 1113 | - * |
|
| 1114 | - * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total |
|
| 1115 | - * @param array|bool|string $line_item_codes |
|
| 1116 | - * @return int number of items successfully removed |
|
| 1117 | - * @throws EE_Error|ReflectionException |
|
| 1118 | - */ |
|
| 1119 | - public static function delete_items(EE_Line_Item $total_line_item, $line_item_codes = false) |
|
| 1120 | - { |
|
| 1121 | - if ($total_line_item->type() !== EEM_Line_Item::type_total) { |
|
| 1122 | - EE_Error::doing_it_wrong( |
|
| 1123 | - 'EEH_Line_Item::delete_items', |
|
| 1124 | - esc_html__( |
|
| 1125 | - 'This static method should only be called with a TOTAL line item, otherwise we won\'t recalculate the totals correctly', |
|
| 1126 | - 'event_espresso' |
|
| 1127 | - ), |
|
| 1128 | - '4.6.18' |
|
| 1129 | - ); |
|
| 1130 | - } |
|
| 1131 | - do_action('AHEE_log', __FILE__, __FUNCTION__, ''); |
|
| 1132 | - |
|
| 1133 | - // check if only a single line_item_id was passed |
|
| 1134 | - if (! empty($line_item_codes) && ! is_array($line_item_codes)) { |
|
| 1135 | - // place single line_item_id in an array to appear as multiple line_item_ids |
|
| 1136 | - $line_item_codes = [$line_item_codes]; |
|
| 1137 | - } |
|
| 1138 | - $removals = 0; |
|
| 1139 | - // cycle thru line_item_ids |
|
| 1140 | - foreach ($line_item_codes as $line_item_id) { |
|
| 1141 | - $removals += $total_line_item->delete_child_line_item($line_item_id); |
|
| 1142 | - } |
|
| 1143 | - |
|
| 1144 | - if ($removals > 0) { |
|
| 1145 | - $total_line_item->recalculate_taxes_and_tax_total(); |
|
| 1146 | - return $removals; |
|
| 1147 | - } else { |
|
| 1148 | - return false; |
|
| 1149 | - } |
|
| 1150 | - } |
|
| 1151 | - |
|
| 1152 | - |
|
| 1153 | - /** |
|
| 1154 | - * Overwrites the previous tax by clearing out the old taxes, and creates a new |
|
| 1155 | - * tax and updates the total line item accordingly |
|
| 1156 | - * |
|
| 1157 | - * @param EE_Line_Item $total_line_item |
|
| 1158 | - * @param float $amount |
|
| 1159 | - * @param string $name |
|
| 1160 | - * @param string $description |
|
| 1161 | - * @param string $code |
|
| 1162 | - * @param boolean $add_to_existing_line_item |
|
| 1163 | - * if true, and a duplicate line item with the same code is found, |
|
| 1164 | - * $amount will be added onto it; otherwise will simply set the taxes to match $amount |
|
| 1165 | - * @return EE_Line_Item the new tax line item created |
|
| 1166 | - * @throws EE_Error |
|
| 1167 | - * @throws InvalidArgumentException |
|
| 1168 | - * @throws InvalidDataTypeException |
|
| 1169 | - * @throws InvalidInterfaceException |
|
| 1170 | - * @throws ReflectionException |
|
| 1171 | - */ |
|
| 1172 | - public static function set_total_tax_to( |
|
| 1173 | - EE_Line_Item $total_line_item, |
|
| 1174 | - $amount, |
|
| 1175 | - $name = null, |
|
| 1176 | - $description = null, |
|
| 1177 | - $code = null, |
|
| 1178 | - $add_to_existing_line_item = false |
|
| 1179 | - ) { |
|
| 1180 | - $tax_subtotal = self::get_taxes_subtotal($total_line_item); |
|
| 1181 | - $taxable_total = $total_line_item->taxable_total(); |
|
| 1182 | - |
|
| 1183 | - if ($add_to_existing_line_item) { |
|
| 1184 | - $new_tax = $tax_subtotal->get_child_line_item($code); |
|
| 1185 | - EEM_Line_Item::instance()->delete( |
|
| 1186 | - [['LIN_code' => ['!=', $code], 'LIN_parent' => $tax_subtotal->ID()]] |
|
| 1187 | - ); |
|
| 1188 | - } else { |
|
| 1189 | - $new_tax = null; |
|
| 1190 | - $tax_subtotal->delete_children_line_items(); |
|
| 1191 | - } |
|
| 1192 | - if ($new_tax) { |
|
| 1193 | - $new_tax->set_total($new_tax->total() + $amount); |
|
| 1194 | - $percent = $taxable_total ? $new_tax->total() / $taxable_total * 100 : 0; |
|
| 1195 | - $new_tax->set_percent(EEH_Line_Item::currencyFormatter()->roundForLocale($percent)); |
|
| 1196 | - } else { |
|
| 1197 | - $percent = $taxable_total ? ($amount / $taxable_total * 100) : 0; |
|
| 1198 | - // no existing tax item. Create it |
|
| 1199 | - $new_tax = EE_Line_Item::new_instance( |
|
| 1200 | - [ |
|
| 1201 | - 'TXN_ID' => $total_line_item->TXN_ID(), |
|
| 1202 | - 'LIN_name' => $name ? $name : esc_html__('Tax', 'event_espresso'), |
|
| 1203 | - 'LIN_desc' => $description ? $description : '', |
|
| 1204 | - 'LIN_percent' => EEH_Line_Item::currencyFormatter()->roundForLocale($percent), |
|
| 1205 | - 'LIN_total' => $amount, |
|
| 1206 | - 'LIN_parent' => $tax_subtotal->ID(), |
|
| 1207 | - 'LIN_type' => EEM_Line_Item::type_tax, |
|
| 1208 | - 'LIN_code' => $code, |
|
| 1209 | - ] |
|
| 1210 | - ); |
|
| 1211 | - } |
|
| 1212 | - |
|
| 1213 | - $new_tax = apply_filters( |
|
| 1214 | - 'FHEE__EEH_Line_Item__set_total_tax_to__new_tax_subtotal', |
|
| 1215 | - $new_tax, |
|
| 1216 | - $total_line_item |
|
| 1217 | - ); |
|
| 1218 | - $new_tax->save(); |
|
| 1219 | - $tax_subtotal->set_total($new_tax->total()); |
|
| 1220 | - $tax_subtotal->save(); |
|
| 1221 | - $total_line_item->recalculate_total_including_taxes(); |
|
| 1222 | - return $new_tax; |
|
| 1223 | - } |
|
| 1224 | - |
|
| 1225 | - |
|
| 1226 | - /** |
|
| 1227 | - * Makes all the line items which are children of $line_item taxable (or not). |
|
| 1228 | - * Does NOT save the line items |
|
| 1229 | - * |
|
| 1230 | - * @param EE_Line_Item $line_item |
|
| 1231 | - * @param boolean $taxable |
|
| 1232 | - * @param string $code_substring_for_whitelist if this string is part of the line item's code |
|
| 1233 | - * it will be whitelisted (ie, except from becoming taxable) |
|
| 1234 | - * @throws EE_Error|ReflectionException |
|
| 1235 | - */ |
|
| 1236 | - public static function set_line_items_taxable( |
|
| 1237 | - EE_Line_Item $line_item, |
|
| 1238 | - $taxable = true, |
|
| 1239 | - $code_substring_for_whitelist = null |
|
| 1240 | - ) { |
|
| 1241 | - $whitelisted = false; |
|
| 1242 | - if ($code_substring_for_whitelist !== null) { |
|
| 1243 | - $whitelisted = strpos($line_item->code(), $code_substring_for_whitelist) !== false; |
|
| 1244 | - } |
|
| 1245 | - if (! $whitelisted && $line_item->is_line_item()) { |
|
| 1246 | - $line_item->set_is_taxable($taxable); |
|
| 1247 | - } |
|
| 1248 | - foreach ($line_item->children() as $child_line_item) { |
|
| 1249 | - EEH_Line_Item::set_line_items_taxable( |
|
| 1250 | - $child_line_item, |
|
| 1251 | - $taxable, |
|
| 1252 | - $code_substring_for_whitelist |
|
| 1253 | - ); |
|
| 1254 | - } |
|
| 1255 | - } |
|
| 1256 | - |
|
| 1257 | - |
|
| 1258 | - /** |
|
| 1259 | - * Gets all descendants that are event subtotals |
|
| 1260 | - * |
|
| 1261 | - * @param EE_Line_Item $parent_line_item - the line item to find descendants of |
|
| 1262 | - * @return EE_Line_Item[] |
|
| 1263 | - * @throws EE_Error|ReflectionException |
|
| 1264 | - * @uses EEH_Line_Item::get_subtotals_of_object_type() |
|
| 1265 | - */ |
|
| 1266 | - public static function get_event_subtotals(EE_Line_Item $parent_line_item) |
|
| 1267 | - { |
|
| 1268 | - return self::get_subtotals_of_object_type($parent_line_item, EEM_Line_Item::OBJ_TYPE_EVENT); |
|
| 1269 | - } |
|
| 1270 | - |
|
| 1271 | - |
|
| 1272 | - /** |
|
| 1273 | - * Gets all descendants subtotals that match the supplied object type |
|
| 1274 | - * |
|
| 1275 | - * @param EE_Line_Item $parent_line_item - the line item to find descendants of |
|
| 1276 | - * @param string $obj_type |
|
| 1277 | - * @return EE_Line_Item[] |
|
| 1278 | - * @throws EE_Error|ReflectionException |
|
| 1279 | - * @uses EEH_Line_Item::_get_descendants_by_type_and_object_type() |
|
| 1280 | - */ |
|
| 1281 | - public static function get_subtotals_of_object_type(EE_Line_Item $parent_line_item, $obj_type = '') |
|
| 1282 | - { |
|
| 1283 | - return self::_get_descendants_by_type_and_object_type( |
|
| 1284 | - $parent_line_item, |
|
| 1285 | - EEM_Line_Item::type_sub_total, |
|
| 1286 | - $obj_type |
|
| 1287 | - ); |
|
| 1288 | - } |
|
| 1289 | - |
|
| 1290 | - |
|
| 1291 | - /** |
|
| 1292 | - * Gets all descendants that are tickets |
|
| 1293 | - * |
|
| 1294 | - * @param EE_Line_Item $parent_line_item - the line item to find descendants of |
|
| 1295 | - * @return EE_Line_Item[] |
|
| 1296 | - * @throws EE_Error|ReflectionException |
|
| 1297 | - * @uses EEH_Line_Item::get_line_items_of_object_type() |
|
| 1298 | - */ |
|
| 1299 | - public static function get_ticket_line_items(EE_Line_Item $parent_line_item) |
|
| 1300 | - { |
|
| 1301 | - return self::get_line_items_of_object_type( |
|
| 1302 | - $parent_line_item, |
|
| 1303 | - EEM_Line_Item::OBJ_TYPE_TICKET |
|
| 1304 | - ); |
|
| 1305 | - } |
|
| 1306 | - |
|
| 1307 | - |
|
| 1308 | - /** |
|
| 1309 | - * Gets all descendants subtotals that match the supplied object type |
|
| 1310 | - * |
|
| 1311 | - * @param EE_Line_Item $parent_line_item - the line item to find descendants of |
|
| 1312 | - * @param string $obj_type |
|
| 1313 | - * @return EE_Line_Item[] |
|
| 1314 | - * @throws EE_Error|ReflectionException |
|
| 1315 | - * @uses EEH_Line_Item::_get_descendants_by_type_and_object_type() |
|
| 1316 | - */ |
|
| 1317 | - public static function get_line_items_of_object_type(EE_Line_Item $parent_line_item, $obj_type = '') |
|
| 1318 | - { |
|
| 1319 | - return self::_get_descendants_by_type_and_object_type( |
|
| 1320 | - $parent_line_item, |
|
| 1321 | - EEM_Line_Item::type_line_item, |
|
| 1322 | - $obj_type |
|
| 1323 | - ); |
|
| 1324 | - } |
|
| 1325 | - |
|
| 1326 | - |
|
| 1327 | - /** |
|
| 1328 | - * Gets all the descendants (ie, children or children of children etc) that are of the type 'tax' |
|
| 1329 | - * |
|
| 1330 | - * @param EE_Line_Item $parent_line_item - the line item to find descendants of |
|
| 1331 | - * @return EE_Line_Item[] |
|
| 1332 | - * @throws EE_Error|ReflectionException |
|
| 1333 | - * @uses EEH_Line_Item::get_descendants_of_type() |
|
| 1334 | - */ |
|
| 1335 | - public static function get_tax_descendants(EE_Line_Item $parent_line_item) |
|
| 1336 | - { |
|
| 1337 | - return EEH_Line_Item::get_descendants_of_type( |
|
| 1338 | - $parent_line_item, |
|
| 1339 | - EEM_Line_Item::type_tax |
|
| 1340 | - ); |
|
| 1341 | - } |
|
| 1342 | - |
|
| 1343 | - |
|
| 1344 | - /** |
|
| 1345 | - * Gets all the real items purchased which are children of this item |
|
| 1346 | - * |
|
| 1347 | - * @param EE_Line_Item $parent_line_item - the line item to find descendants of |
|
| 1348 | - * @return EE_Line_Item[] |
|
| 1349 | - * @throws EE_Error|ReflectionException |
|
| 1350 | - * @uses EEH_Line_Item::get_descendants_of_type() |
|
| 1351 | - */ |
|
| 1352 | - public static function get_line_item_descendants(EE_Line_Item $parent_line_item) |
|
| 1353 | - { |
|
| 1354 | - return EEH_Line_Item::get_descendants_of_type( |
|
| 1355 | - $parent_line_item, |
|
| 1356 | - EEM_Line_Item::type_line_item |
|
| 1357 | - ); |
|
| 1358 | - } |
|
| 1359 | - |
|
| 1360 | - |
|
| 1361 | - /** |
|
| 1362 | - * Gets all descendants of supplied line item that match the supplied line item type |
|
| 1363 | - * |
|
| 1364 | - * @param EE_Line_Item $parent_line_item - the line item to find descendants of |
|
| 1365 | - * @param string $line_item_type one of the EEM_Line_Item constants |
|
| 1366 | - * @return EE_Line_Item[] |
|
| 1367 | - * @throws EE_Error|ReflectionException |
|
| 1368 | - * @uses EEH_Line_Item::_get_descendants_by_type_and_object_type() |
|
| 1369 | - */ |
|
| 1370 | - public static function get_descendants_of_type(EE_Line_Item $parent_line_item, $line_item_type) |
|
| 1371 | - { |
|
| 1372 | - return self::_get_descendants_by_type_and_object_type($parent_line_item, $line_item_type); |
|
| 1373 | - } |
|
| 1374 | - |
|
| 1375 | - |
|
| 1376 | - /** |
|
| 1377 | - * Gets all descendants of supplied line item that match the supplied line item type and possibly the object type |
|
| 1378 | - * as well |
|
| 1379 | - * |
|
| 1380 | - * @param EE_Line_Item $parent_line_item - the line item to find descendants of |
|
| 1381 | - * @param string $line_item_type one of the EEM_Line_Item constants |
|
| 1382 | - * @param string | NULL $obj_type object model class name (minus prefix) |
|
| 1383 | - * or NULL to ignore object type when searching |
|
| 1384 | - * @return EE_Line_Item[] |
|
| 1385 | - * @throws EE_Error|ReflectionException |
|
| 1386 | - */ |
|
| 1387 | - protected static function _get_descendants_by_type_and_object_type( |
|
| 1388 | - EE_Line_Item $parent_line_item, |
|
| 1389 | - $line_item_type, |
|
| 1390 | - $obj_type = null |
|
| 1391 | - ) { |
|
| 1392 | - $objects = []; |
|
| 1393 | - foreach ($parent_line_item->children() as $child_line_item) { |
|
| 1394 | - if ($child_line_item instanceof EE_Line_Item) { |
|
| 1395 | - if ( |
|
| 1396 | - $child_line_item->type() === $line_item_type |
|
| 1397 | - && ( |
|
| 1398 | - $child_line_item->OBJ_type() === $obj_type || $obj_type === null |
|
| 1399 | - ) |
|
| 1400 | - ) { |
|
| 1401 | - $objects[] = $child_line_item; |
|
| 1402 | - } else { |
|
| 1403 | - // go-through-all-its children looking for more matches |
|
| 1404 | - $objects = array_merge( |
|
| 1405 | - $objects, |
|
| 1406 | - self::_get_descendants_by_type_and_object_type( |
|
| 1407 | - $child_line_item, |
|
| 1408 | - $line_item_type, |
|
| 1409 | - $obj_type |
|
| 1410 | - ) |
|
| 1411 | - ); |
|
| 1412 | - } |
|
| 1413 | - } |
|
| 1414 | - } |
|
| 1415 | - return $objects; |
|
| 1416 | - } |
|
| 1417 | - |
|
| 1418 | - |
|
| 1419 | - /** |
|
| 1420 | - * Gets all descendants subtotals that match the supplied object type |
|
| 1421 | - * |
|
| 1422 | - * @param EE_Line_Item $parent_line_item - the line item to find descendants of |
|
| 1423 | - * @param string $OBJ_type object type (like Event) |
|
| 1424 | - * @param array $OBJ_IDs array of OBJ_IDs |
|
| 1425 | - * @return EE_Line_Item[] |
|
| 1426 | - * @throws EE_Error|ReflectionException |
|
| 1427 | - * @uses EEH_Line_Item::_get_descendants_by_type_and_object_type() |
|
| 1428 | - */ |
|
| 1429 | - public static function get_line_items_by_object_type_and_IDs( |
|
| 1430 | - EE_Line_Item $parent_line_item, |
|
| 1431 | - $OBJ_type = '', |
|
| 1432 | - $OBJ_IDs = [] |
|
| 1433 | - ) { |
|
| 1434 | - return self::_get_descendants_by_object_type_and_object_ID( |
|
| 1435 | - $parent_line_item, |
|
| 1436 | - $OBJ_type, |
|
| 1437 | - $OBJ_IDs |
|
| 1438 | - ); |
|
| 1439 | - } |
|
| 1440 | - |
|
| 1441 | - |
|
| 1442 | - /** |
|
| 1443 | - * Gets all descendants of supplied line item that match the supplied line item type and possibly the object type |
|
| 1444 | - * as well |
|
| 1445 | - * |
|
| 1446 | - * @param EE_Line_Item $parent_line_item - the line item to find descendants of |
|
| 1447 | - * @param string $OBJ_type object type (like Event) |
|
| 1448 | - * @param array $OBJ_IDs array of OBJ_IDs |
|
| 1449 | - * @return EE_Line_Item[] |
|
| 1450 | - * @throws EE_Error|ReflectionException |
|
| 1451 | - */ |
|
| 1452 | - protected static function _get_descendants_by_object_type_and_object_ID( |
|
| 1453 | - EE_Line_Item $parent_line_item, |
|
| 1454 | - $OBJ_type, |
|
| 1455 | - $OBJ_IDs |
|
| 1456 | - ) { |
|
| 1457 | - $objects = []; |
|
| 1458 | - foreach ($parent_line_item->children() as $child_line_item) { |
|
| 1459 | - if ($child_line_item instanceof EE_Line_Item) { |
|
| 1460 | - if ( |
|
| 1461 | - $child_line_item->OBJ_type() === $OBJ_type |
|
| 1462 | - && is_array($OBJ_IDs) |
|
| 1463 | - && in_array($child_line_item->OBJ_ID(), $OBJ_IDs) |
|
| 1464 | - ) { |
|
| 1465 | - $objects[] = $child_line_item; |
|
| 1466 | - } else { |
|
| 1467 | - // go-through-all-its children looking for more matches |
|
| 1468 | - $objects = array_merge( |
|
| 1469 | - $objects, |
|
| 1470 | - self::_get_descendants_by_object_type_and_object_ID( |
|
| 1471 | - $child_line_item, |
|
| 1472 | - $OBJ_type, |
|
| 1473 | - $OBJ_IDs |
|
| 1474 | - ) |
|
| 1475 | - ); |
|
| 1476 | - } |
|
| 1477 | - } |
|
| 1478 | - } |
|
| 1479 | - return $objects; |
|
| 1480 | - } |
|
| 1481 | - |
|
| 1482 | - |
|
| 1483 | - /** |
|
| 1484 | - * Uses a breadth-first-search in order to find the nearest descendant of |
|
| 1485 | - * the specified type and returns it, else NULL |
|
| 1486 | - * |
|
| 1487 | - * @param EE_Line_Item $parent_line_item - the line item to find descendants of |
|
| 1488 | - * @param string $type like one of the EEM_Line_Item::type_* |
|
| 1489 | - * @return EE_Line_Item |
|
| 1490 | - * @throws EE_Error |
|
| 1491 | - * @throws InvalidArgumentException |
|
| 1492 | - * @throws InvalidDataTypeException |
|
| 1493 | - * @throws InvalidInterfaceException |
|
| 1494 | - * @throws ReflectionException |
|
| 1495 | - * @uses EEH_Line_Item::_get_nearest_descendant() |
|
| 1496 | - */ |
|
| 1497 | - public static function get_nearest_descendant_of_type(EE_Line_Item $parent_line_item, $type) |
|
| 1498 | - { |
|
| 1499 | - return self::_get_nearest_descendant($parent_line_item, 'LIN_type', $type); |
|
| 1500 | - } |
|
| 1501 | - |
|
| 1502 | - |
|
| 1503 | - /** |
|
| 1504 | - * Uses a breadth-first-search in order to find the nearest descendant |
|
| 1505 | - * having the specified LIN_code and returns it, else NULL |
|
| 1506 | - * |
|
| 1507 | - * @param EE_Line_Item $parent_line_item - the line item to find descendants of |
|
| 1508 | - * @param string $code any value used for LIN_code |
|
| 1509 | - * @return EE_Line_Item |
|
| 1510 | - * @throws EE_Error |
|
| 1511 | - * @throws InvalidArgumentException |
|
| 1512 | - * @throws InvalidDataTypeException |
|
| 1513 | - * @throws InvalidInterfaceException |
|
| 1514 | - * @throws ReflectionException |
|
| 1515 | - * @uses EEH_Line_Item::_get_nearest_descendant() |
|
| 1516 | - */ |
|
| 1517 | - public static function get_nearest_descendant_having_code(EE_Line_Item $parent_line_item, $code) |
|
| 1518 | - { |
|
| 1519 | - return self::_get_nearest_descendant($parent_line_item, 'LIN_code', $code); |
|
| 1520 | - } |
|
| 1521 | - |
|
| 1522 | - |
|
| 1523 | - /** |
|
| 1524 | - * Uses a breadth-first-search in order to find the nearest descendant |
|
| 1525 | - * having the specified LIN_code and returns it, else NULL |
|
| 1526 | - * |
|
| 1527 | - * @param EE_Line_Item $parent_line_item - the line item to find descendants of |
|
| 1528 | - * @param string $search_field name of EE_Line_Item property |
|
| 1529 | - * @param string $value any value stored in $search_field |
|
| 1530 | - * @return EE_Line_Item |
|
| 1531 | - * @throws EE_Error |
|
| 1532 | - * @throws InvalidArgumentException |
|
| 1533 | - * @throws InvalidDataTypeException |
|
| 1534 | - * @throws InvalidInterfaceException |
|
| 1535 | - * @throws ReflectionException |
|
| 1536 | - */ |
|
| 1537 | - protected static function _get_nearest_descendant(EE_Line_Item $parent_line_item, $search_field, $value) |
|
| 1538 | - { |
|
| 1539 | - foreach ($parent_line_item->children() as $child) { |
|
| 1540 | - if ($child->get($search_field) == $value) { |
|
| 1541 | - return $child; |
|
| 1542 | - } |
|
| 1543 | - } |
|
| 1544 | - foreach ($parent_line_item->children() as $child) { |
|
| 1545 | - $descendant_found = self::_get_nearest_descendant( |
|
| 1546 | - $child, |
|
| 1547 | - $search_field, |
|
| 1548 | - $value |
|
| 1549 | - ); |
|
| 1550 | - if ($descendant_found) { |
|
| 1551 | - return $descendant_found; |
|
| 1552 | - } |
|
| 1553 | - } |
|
| 1554 | - return null; |
|
| 1555 | - } |
|
| 1556 | - |
|
| 1557 | - |
|
| 1558 | - /** |
|
| 1559 | - * if passed line item has a TXN ID, uses that to jump directly to the grand total line item for the transaction, |
|
| 1560 | - * else recursively walks up the line item tree until a parent of type total is found, |
|
| 1561 | - * |
|
| 1562 | - * @param EE_Line_Item $line_item |
|
| 1563 | - * @return EE_Line_Item |
|
| 1564 | - * @throws EE_Error|ReflectionException |
|
| 1565 | - */ |
|
| 1566 | - public static function find_transaction_grand_total_for_line_item(EE_Line_Item $line_item) |
|
| 1567 | - { |
|
| 1568 | - if ($line_item->TXN_ID()) { |
|
| 1569 | - $total_line_item = $line_item->transaction()->total_line_item(false); |
|
| 1570 | - if ($total_line_item instanceof EE_Line_Item) { |
|
| 1571 | - return $total_line_item; |
|
| 1572 | - } |
|
| 1573 | - } else { |
|
| 1574 | - $line_item_parent = $line_item->parent(); |
|
| 1575 | - if ($line_item_parent instanceof EE_Line_Item) { |
|
| 1576 | - if ($line_item_parent->is_total()) { |
|
| 1577 | - return $line_item_parent; |
|
| 1578 | - } |
|
| 1579 | - return EEH_Line_Item::find_transaction_grand_total_for_line_item($line_item_parent); |
|
| 1580 | - } |
|
| 1581 | - } |
|
| 1582 | - throw new EE_Error( |
|
| 1583 | - sprintf( |
|
| 1584 | - esc_html__( |
|
| 1585 | - 'A valid grand total for line item %1$d was not found.', |
|
| 1586 | - 'event_espresso' |
|
| 1587 | - ), |
|
| 1588 | - $line_item->ID() |
|
| 1589 | - ) |
|
| 1590 | - ); |
|
| 1591 | - } |
|
| 1592 | - |
|
| 1593 | - |
|
| 1594 | - /** |
|
| 1595 | - * Prints out a representation of the line item tree |
|
| 1596 | - * |
|
| 1597 | - * @param EE_Line_Item $line_item |
|
| 1598 | - * @param int $indentation |
|
| 1599 | - * @return void |
|
| 1600 | - * @throws EE_Error|ReflectionException |
|
| 1601 | - */ |
|
| 1602 | - public static function visualize(EE_Line_Item $line_item, $indentation = 0) |
|
| 1603 | - { |
|
| 1604 | - echo defined('EE_TESTS_DIR') ? "\n" : '<br />'; |
|
| 1605 | - if (! $indentation) { |
|
| 1606 | - echo defined('EE_TESTS_DIR') ? "\n" : '<br />'; |
|
| 1607 | - } |
|
| 1608 | - for ($i = 0; $i < $indentation; $i++) { |
|
| 1609 | - echo '. '; |
|
| 1610 | - } |
|
| 1611 | - $breakdown = ''; |
|
| 1612 | - if ($line_item->is_line_item()) { |
|
| 1613 | - if ($line_item->is_percent()) { |
|
| 1614 | - $breakdown = "{$line_item->percent()}%"; |
|
| 1615 | - } else { |
|
| 1616 | - $breakdown = '$' . "{$line_item->unit_price()} x {$line_item->quantity()}"; |
|
| 1617 | - } |
|
| 1618 | - } |
|
| 1619 | - echo wp_kses($line_item->name(), AllowedTags::getAllowedTags()); |
|
| 1620 | - echo " [ ID:{$line_item->ID()} | qty:{$line_item->quantity()} ] {$line_item->type()} : "; |
|
| 1621 | - echo '$' . (string) $line_item->total(); |
|
| 1622 | - if ($breakdown) { |
|
| 1623 | - echo " ( {$breakdown} )"; |
|
| 1624 | - } |
|
| 1625 | - if ($line_item->is_taxable()) { |
|
| 1626 | - echo ' * taxable'; |
|
| 1627 | - } |
|
| 1628 | - if ($line_item->children()) { |
|
| 1629 | - foreach ($line_item->children() as $child) { |
|
| 1630 | - self::visualize($child, $indentation + 1); |
|
| 1631 | - } |
|
| 1632 | - } |
|
| 1633 | - } |
|
| 1634 | - |
|
| 1635 | - |
|
| 1636 | - /** |
|
| 1637 | - * Calculates the registration's final price, taking into account that they |
|
| 1638 | - * need to not only help pay for their OWN ticket, but also any transaction-wide surcharges and taxes, |
|
| 1639 | - * and receive a portion of any transaction-wide discounts. |
|
| 1640 | - * eg1, if I buy a $1 ticket and brent buys a $9 ticket, and we receive a $5 discount |
|
| 1641 | - * then I'll get 1/10 of that $5 discount, which is $0.50, and brent will get |
|
| 1642 | - * 9/10ths of that $5 discount, which is $4.50. So my final price should be $0.50 |
|
| 1643 | - * and brent's final price should be $5.50. |
|
| 1644 | - * In order to do this, we basically need to traverse the line item tree calculating |
|
| 1645 | - * the running totals (just as if we were recalculating the total), but when we identify |
|
| 1646 | - * regular line items, we need to keep track of their share of the grand total. |
|
| 1647 | - * Also, we need to keep track of the TAXABLE total for each ticket purchase, so |
|
| 1648 | - * we can know how to apply taxes to it. (Note: "taxable total" does not equal the "pretax total" |
|
| 1649 | - * when there are non-taxable items; otherwise they would be the same) |
|
| 1650 | - * |
|
| 1651 | - * @param EE_Line_Item $line_item |
|
| 1652 | - * @param array $billable_ticket_quantities array of EE_Ticket IDs and their corresponding quantity |
|
| 1653 | - * that can be included in price calculations at this |
|
| 1654 | - * moment |
|
| 1655 | - * @return array keys are line items for tickets IDs and values are their share of the running total, |
|
| 1656 | - * plus the key 'total', and 'taxable' which also has keys |
|
| 1657 | - * of all the ticket IDs. Eg array( |
|
| 1658 | - * 12 => 4.3 |
|
| 1659 | - * 23 => 8.0 |
|
| 1660 | - * 'total' => 16.6, |
|
| 1661 | - * 'taxable' => array( |
|
| 1662 | - * 12 => 10, |
|
| 1663 | - * 23 => 4 |
|
| 1664 | - * ). |
|
| 1665 | - * So to find which registrations have which final price, |
|
| 1666 | - * we need to find which line item is theirs, which can be |
|
| 1667 | - * done with |
|
| 1668 | - * `EEM_Line_Item::instance()->get_line_item_for_registration( |
|
| 1669 | - * $registration );` |
|
| 1670 | - * @throws EE_Error |
|
| 1671 | - * @throws InvalidArgumentException |
|
| 1672 | - * @throws InvalidDataTypeException |
|
| 1673 | - * @throws InvalidInterfaceException |
|
| 1674 | - * @throws ReflectionException |
|
| 1675 | - */ |
|
| 1676 | - public static function calculate_reg_final_prices_per_line_item( |
|
| 1677 | - EE_Line_Item $line_item, |
|
| 1678 | - $billable_ticket_quantities = [] |
|
| 1679 | - ) { |
|
| 1680 | - $running_totals = [ |
|
| 1681 | - 'total' => 0, |
|
| 1682 | - 'taxable' => ['total' => 0], |
|
| 1683 | - ]; |
|
| 1684 | - foreach ($line_item->children() as $child_line_item) { |
|
| 1685 | - switch ($child_line_item->type()) { |
|
| 1686 | - case EEM_Line_Item::type_sub_total: |
|
| 1687 | - $running_totals_from_subtotal = EEH_Line_Item::calculate_reg_final_prices_per_line_item( |
|
| 1688 | - $child_line_item, |
|
| 1689 | - $billable_ticket_quantities |
|
| 1690 | - ); |
|
| 1691 | - // combine arrays but preserve numeric keys |
|
| 1692 | - $running_totals = |
|
| 1693 | - array_replace_recursive($running_totals_from_subtotal, $running_totals); |
|
| 1694 | - $running_totals['total'] += $running_totals_from_subtotal['total']; |
|
| 1695 | - $running_totals['taxable']['total'] += $running_totals_from_subtotal['taxable']['total']; |
|
| 1696 | - break; |
|
| 1697 | - |
|
| 1698 | - case EEM_Line_Item::type_tax_sub_total: |
|
| 1699 | - // find how much the taxes percentage is |
|
| 1700 | - if ($child_line_item->percent() !== 0) { |
|
| 1701 | - $tax_percent_decimal = $child_line_item->percent(true); |
|
| 1702 | - } else { |
|
| 1703 | - $tax_percent_decimal = |
|
| 1704 | - EEH_Line_Item::currencyFormatter()->roundForLocale( |
|
| 1705 | - EE_Taxes::get_total_taxes_percentage() / 100 |
|
| 1706 | - ); |
|
| 1707 | - } |
|
| 1708 | - // and apply to all the taxable totals, and add to the pretax totals |
|
| 1709 | - foreach ($running_totals as $line_item_id => $this_running_total) { |
|
| 1710 | - // "total" and "taxable" array key is an exception |
|
| 1711 | - if ($line_item_id === 'taxable') { |
|
| 1712 | - continue; |
|
| 1713 | - } |
|
| 1714 | - $taxable_total = $running_totals['taxable'][ $line_item_id ]; |
|
| 1715 | - $running_totals[ $line_item_id ] += EEH_Line_Item::currencyFormatter()->roundForLocale( |
|
| 1716 | - $taxable_total * $tax_percent_decimal |
|
| 1717 | - ); |
|
| 1718 | - } |
|
| 1719 | - break; |
|
| 1720 | - |
|
| 1721 | - case EEM_Line_Item::type_line_item: |
|
| 1722 | - // ticket line items or ???? |
|
| 1723 | - if ($child_line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET) { |
|
| 1724 | - // kk it's a ticket |
|
| 1725 | - if (isset($running_totals[ $child_line_item->ID() ])) { |
|
| 1726 | - // huh? that shouldn't happen. |
|
| 1727 | - $running_totals['total'] += $child_line_item->total(); |
|
| 1728 | - } else { |
|
| 1729 | - // its not in our running totals yet. great. |
|
| 1730 | - if ($child_line_item->is_taxable()) { |
|
| 1731 | - $taxable_amount = $child_line_item->unit_price(); |
|
| 1732 | - } else { |
|
| 1733 | - $taxable_amount = 0; |
|
| 1734 | - } |
|
| 1735 | - // are we only calculating totals for some tickets? |
|
| 1736 | - if (isset($billable_ticket_quantities[ $child_line_item->OBJ_ID() ])) { |
|
| 1737 | - $quantity = |
|
| 1738 | - $billable_ticket_quantities[ $child_line_item->OBJ_ID() ]; |
|
| 1739 | - $running_totals[ $child_line_item->ID() ] = $quantity |
|
| 1740 | - ? $child_line_item->unit_price() |
|
| 1741 | - : 0; |
|
| 1742 | - $running_totals['taxable'][ $child_line_item->ID() ] = $quantity |
|
| 1743 | - ? $taxable_amount |
|
| 1744 | - : 0; |
|
| 1745 | - } else { |
|
| 1746 | - $quantity = $child_line_item->quantity(); |
|
| 1747 | - $running_totals[ $child_line_item->ID() ] = $child_line_item->unit_price(); |
|
| 1748 | - $running_totals['taxable'][ $child_line_item->ID() ] = $taxable_amount; |
|
| 1749 | - } |
|
| 1750 | - $running_totals['taxable']['total'] += $taxable_amount * $quantity; |
|
| 1751 | - $running_totals['total'] += $child_line_item->unit_price() * $quantity; |
|
| 1752 | - } |
|
| 1753 | - } else { |
|
| 1754 | - // it's some other type of item added to the cart |
|
| 1755 | - // it should affect the running totals |
|
| 1756 | - // basically we want to convert it into a PERCENT modifier. Because |
|
| 1757 | - // more clearly affect all registration's final price equally |
|
| 1758 | - $line_items_percent_of_running_total = $running_totals['total'] > 0 |
|
| 1759 | - ? EEH_Line_Item::currencyFormatter()->roundForLocale( |
|
| 1760 | - $child_line_item->total() / $running_totals['total'] |
|
| 1761 | - ) + 1 |
|
| 1762 | - : 1; |
|
| 1763 | - foreach ($running_totals as $line_item_id => $this_running_total) { |
|
| 1764 | - // the "taxable" array key is an exception |
|
| 1765 | - if ($line_item_id === 'taxable') { |
|
| 1766 | - continue; |
|
| 1767 | - } |
|
| 1768 | - // update the running totals |
|
| 1769 | - // yes this actually even works for the running grand total! |
|
| 1770 | - $running_totals[ $line_item_id ] = EEH_Line_Item::currencyFormatter()->roundForLocale( |
|
| 1771 | - $line_items_percent_of_running_total * $this_running_total |
|
| 1772 | - ); |
|
| 1773 | - |
|
| 1774 | - if ($child_line_item->is_taxable()) { |
|
| 1775 | - $running_totals['taxable'][ $line_item_id ] = |
|
| 1776 | - EEH_Line_Item::currencyFormatter()->roundForLocale( |
|
| 1777 | - $line_items_percent_of_running_total * $running_totals['taxable'][ $line_item_id ] |
|
| 1778 | - ); |
|
| 1779 | - } |
|
| 1780 | - } |
|
| 1781 | - } |
|
| 1782 | - break; |
|
| 1783 | - } |
|
| 1784 | - } |
|
| 1785 | - return $running_totals; |
|
| 1786 | - } |
|
| 1787 | - |
|
| 1788 | - |
|
| 1789 | - /** |
|
| 1790 | - * @param EE_Line_Item $total_line_item |
|
| 1791 | - * @param EE_Line_Item $ticket_line_item |
|
| 1792 | - * @return float | null |
|
| 1793 | - * @throws EE_Error |
|
| 1794 | - * @throws InvalidArgumentException |
|
| 1795 | - * @throws InvalidDataTypeException |
|
| 1796 | - * @throws InvalidInterfaceException |
|
| 1797 | - * @throws OutOfRangeException |
|
| 1798 | - * @throws ReflectionException |
|
| 1799 | - */ |
|
| 1800 | - public static function calculate_final_price_for_ticket_line_item( |
|
| 1801 | - EE_Line_Item $total_line_item, |
|
| 1802 | - EE_Line_Item $ticket_line_item |
|
| 1803 | - ) { |
|
| 1804 | - static $final_prices_per_ticket_line_item = []; |
|
| 1805 | - if (empty($final_prices_per_ticket_line_item[ $total_line_item->ID() ])) { |
|
| 1806 | - $final_prices_per_ticket_line_item[ $total_line_item->ID() ] = |
|
| 1807 | - EEH_Line_Item::calculate_reg_final_prices_per_line_item( |
|
| 1808 | - $total_line_item |
|
| 1809 | - ); |
|
| 1810 | - } |
|
| 1811 | - // ok now find this new registration's final price |
|
| 1812 | - if (isset($final_prices_per_ticket_line_item[ $total_line_item->ID() ][ $ticket_line_item->ID() ])) { |
|
| 1813 | - return $final_prices_per_ticket_line_item[ $total_line_item->ID() ][ $ticket_line_item->ID() ]; |
|
| 1814 | - } |
|
| 1815 | - $message = sprintf( |
|
| 1816 | - esc_html__( |
|
| 1817 | - 'The final price for the ticket line item (ID:%1$d) could not be calculated.', |
|
| 1818 | - 'event_espresso' |
|
| 1819 | - ), |
|
| 1820 | - $ticket_line_item->ID() |
|
| 1821 | - ); |
|
| 1822 | - if (WP_DEBUG) { |
|
| 1823 | - $message .= '<br>' . print_r($final_prices_per_ticket_line_item, true); |
|
| 1824 | - throw new OutOfRangeException($message); |
|
| 1825 | - } |
|
| 1826 | - EE_Log::instance()->log(__CLASS__, __FUNCTION__, $message); |
|
| 1827 | - return null; |
|
| 1828 | - } |
|
| 1829 | - |
|
| 1830 | - |
|
| 1831 | - /** |
|
| 1832 | - * Creates a duplicate of the line item tree, except only includes billable items |
|
| 1833 | - * and the portion of line items attributed to billable things |
|
| 1834 | - * |
|
| 1835 | - * @param EE_Line_Item $line_item |
|
| 1836 | - * @param EE_Registration[] $registrations |
|
| 1837 | - * @return EE_Line_Item |
|
| 1838 | - * @throws EE_Error |
|
| 1839 | - * @throws InvalidArgumentException |
|
| 1840 | - * @throws InvalidDataTypeException |
|
| 1841 | - * @throws InvalidInterfaceException |
|
| 1842 | - * @throws ReflectionException |
|
| 1843 | - */ |
|
| 1844 | - public static function billable_line_item_tree(EE_Line_Item $line_item, $registrations) |
|
| 1845 | - { |
|
| 1846 | - $copy_li = EEH_Line_Item::billable_line_item($line_item, $registrations); |
|
| 1847 | - foreach ($line_item->children() as $child_li) { |
|
| 1848 | - $copy_li->add_child_line_item( |
|
| 1849 | - EEH_Line_Item::billable_line_item_tree($child_li, $registrations) |
|
| 1850 | - ); |
|
| 1851 | - } |
|
| 1852 | - // if this is the grand total line item, make sure the totals all add up |
|
| 1853 | - // (we could have duplicated this logic AS we copied the line items, but |
|
| 1854 | - // it seems DRYer this way) |
|
| 1855 | - if ($copy_li->type() === EEM_Line_Item::type_total) { |
|
| 1856 | - $copy_li->recalculate_total_including_taxes(); |
|
| 1857 | - } |
|
| 1858 | - return $copy_li; |
|
| 1859 | - } |
|
| 1860 | - |
|
| 1861 | - |
|
| 1862 | - /** |
|
| 1863 | - * Creates a new, unsaved line item from $line_item that factors in the |
|
| 1864 | - * number of billable registrations on $registrations. |
|
| 1865 | - * |
|
| 1866 | - * @param EE_Line_Item $line_item |
|
| 1867 | - * @param EE_Registration[] $registrations |
|
| 1868 | - * @return EE_Line_Item |
|
| 1869 | - * @throws EE_Error |
|
| 1870 | - * @throws InvalidArgumentException |
|
| 1871 | - * @throws InvalidDataTypeException |
|
| 1872 | - * @throws InvalidInterfaceException |
|
| 1873 | - * @throws ReflectionException |
|
| 1874 | - */ |
|
| 1875 | - public static function billable_line_item(EE_Line_Item $line_item, $registrations) |
|
| 1876 | - { |
|
| 1877 | - $new_li_fields = $line_item->model_field_array(); |
|
| 1878 | - if ( |
|
| 1879 | - $line_item->type() === EEM_Line_Item::type_line_item |
|
| 1880 | - && $line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET |
|
| 1881 | - ) { |
|
| 1882 | - $count = 0; |
|
| 1883 | - foreach ($registrations as $registration) { |
|
| 1884 | - if ( |
|
| 1885 | - $line_item->OBJ_ID() === $registration->ticket_ID() && |
|
| 1886 | - in_array( |
|
| 1887 | - $registration->status_ID(), |
|
| 1888 | - EEM_Registration::reg_statuses_that_allow_payment(), |
|
| 1889 | - true |
|
| 1890 | - ) |
|
| 1891 | - ) { |
|
| 1892 | - $count++; |
|
| 1893 | - } |
|
| 1894 | - } |
|
| 1895 | - $new_li_fields['LIN_quantity'] = $count; |
|
| 1896 | - } |
|
| 1897 | - // don't set the total. We'll leave that up to the code that calculates it |
|
| 1898 | - unset($new_li_fields['LIN_ID'], $new_li_fields['LIN_parent'], $new_li_fields['LIN_total']); |
|
| 1899 | - return EE_Line_Item::new_instance($new_li_fields); |
|
| 1900 | - } |
|
| 1901 | - |
|
| 1902 | - |
|
| 1903 | - /** |
|
| 1904 | - * Returns a modified line item tree where all the subtotals which have a total of 0 |
|
| 1905 | - * are removed, and line items with a quantity of 0 |
|
| 1906 | - * |
|
| 1907 | - * @param EE_Line_Item $line_item |null |
|
| 1908 | - * @return EE_Line_Item|null |
|
| 1909 | - * @throws EE_Error |
|
| 1910 | - * @throws InvalidArgumentException |
|
| 1911 | - * @throws InvalidDataTypeException |
|
| 1912 | - * @throws InvalidInterfaceException |
|
| 1913 | - * @throws ReflectionException |
|
| 1914 | - */ |
|
| 1915 | - public static function non_empty_line_items(EE_Line_Item $line_item) |
|
| 1916 | - { |
|
| 1917 | - $copied_li = EEH_Line_Item::non_empty_line_item($line_item); |
|
| 1918 | - if ($copied_li === null) { |
|
| 1919 | - return null; |
|
| 1920 | - } |
|
| 1921 | - // if this is an event subtotal, we want to only include it if it |
|
| 1922 | - // has a non-zero total and at least one ticket line item child |
|
| 1923 | - $ticket_children = 0; |
|
| 1924 | - foreach ($line_item->children() as $child_li) { |
|
| 1925 | - $child_li_copy = EEH_Line_Item::non_empty_line_items($child_li); |
|
| 1926 | - if ($child_li_copy !== null) { |
|
| 1927 | - $copied_li->add_child_line_item($child_li_copy); |
|
| 1928 | - if ( |
|
| 1929 | - $child_li_copy->type() === EEM_Line_Item::type_line_item |
|
| 1930 | - && $child_li_copy->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET |
|
| 1931 | - ) { |
|
| 1932 | - $ticket_children++; |
|
| 1933 | - } |
|
| 1934 | - } |
|
| 1935 | - } |
|
| 1936 | - // if this is an event subtotal with NO ticket children |
|
| 1937 | - // we basically want to ignore it |
|
| 1938 | - if ( |
|
| 1939 | - $ticket_children === 0 |
|
| 1940 | - && $line_item->type() === EEM_Line_Item::type_sub_total |
|
| 1941 | - && $line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_EVENT |
|
| 1942 | - && $line_item->total() === 0 |
|
| 1943 | - ) { |
|
| 1944 | - return null; |
|
| 1945 | - } |
|
| 1946 | - return $copied_li; |
|
| 1947 | - } |
|
| 1948 | - |
|
| 1949 | - |
|
| 1950 | - /** |
|
| 1951 | - * Creates a new, unsaved line item, but if it's a ticket line item |
|
| 1952 | - * with a total of 0, or a subtotal of 0, returns null instead |
|
| 1953 | - * |
|
| 1954 | - * @param EE_Line_Item $line_item |
|
| 1955 | - * @return EE_Line_Item |
|
| 1956 | - * @throws EE_Error |
|
| 1957 | - * @throws InvalidArgumentException |
|
| 1958 | - * @throws InvalidDataTypeException |
|
| 1959 | - * @throws InvalidInterfaceException |
|
| 1960 | - * @throws ReflectionException |
|
| 1961 | - */ |
|
| 1962 | - public static function non_empty_line_item(EE_Line_Item $line_item) |
|
| 1963 | - { |
|
| 1964 | - if ( |
|
| 1965 | - $line_item->type() === EEM_Line_Item::type_line_item |
|
| 1966 | - && $line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET |
|
| 1967 | - && $line_item->quantity() === 0 |
|
| 1968 | - ) { |
|
| 1969 | - return null; |
|
| 1970 | - } |
|
| 1971 | - $new_li_fields = $line_item->model_field_array(); |
|
| 1972 | - // don't set the total. We'll leave that up to the code that calculates it |
|
| 1973 | - unset($new_li_fields['LIN_ID'], $new_li_fields['LIN_parent']); |
|
| 1974 | - return EE_Line_Item::new_instance($new_li_fields); |
|
| 1975 | - } |
|
| 1976 | - |
|
| 1977 | - |
|
| 1978 | - /** |
|
| 1979 | - * Cycles through all of the ticket line items for the supplied total line item |
|
| 1980 | - * and ensures that the line item's "is_taxable" field matches that of its corresponding ticket |
|
| 1981 | - * |
|
| 1982 | - * @param EE_Line_Item $total_line_item |
|
| 1983 | - * @throws EE_Error |
|
| 1984 | - * @throws InvalidArgumentException |
|
| 1985 | - * @throws InvalidDataTypeException |
|
| 1986 | - * @throws InvalidInterfaceException |
|
| 1987 | - * @throws ReflectionException |
|
| 1988 | - * @since 4.9.79.p |
|
| 1989 | - */ |
|
| 1990 | - public static function resetIsTaxableForTickets(EE_Line_Item $total_line_item) |
|
| 1991 | - { |
|
| 1992 | - $ticket_line_items = self::get_ticket_line_items($total_line_item); |
|
| 1993 | - foreach ($ticket_line_items as $ticket_line_item) { |
|
| 1994 | - if ( |
|
| 1995 | - $ticket_line_item instanceof EE_Line_Item |
|
| 1996 | - && $ticket_line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET |
|
| 1997 | - ) { |
|
| 1998 | - $ticket = $ticket_line_item->ticket(); |
|
| 1999 | - if ($ticket instanceof EE_Ticket && $ticket->taxable() !== $ticket_line_item->is_taxable()) { |
|
| 2000 | - $ticket_line_item->set_is_taxable($ticket->taxable()); |
|
| 2001 | - $ticket_line_item->save(); |
|
| 2002 | - } |
|
| 2003 | - } |
|
| 2004 | - } |
|
| 2005 | - } |
|
| 2006 | - |
|
| 2007 | - |
|
| 2008 | - |
|
| 2009 | - /**************************************** @DEPRECATED METHODS *************************************** */ |
|
| 2010 | - /** |
|
| 2011 | - * @param EE_Line_Item $total_line_item |
|
| 2012 | - * @return EE_Line_Item |
|
| 2013 | - * @throws EE_Error |
|
| 2014 | - * @throws InvalidArgumentException |
|
| 2015 | - * @throws InvalidDataTypeException |
|
| 2016 | - * @throws InvalidInterfaceException |
|
| 2017 | - * @throws ReflectionException |
|
| 2018 | - * @deprecated |
|
| 2019 | - */ |
|
| 2020 | - public static function get_items_subtotal(EE_Line_Item $total_line_item) |
|
| 2021 | - { |
|
| 2022 | - EE_Error::doing_it_wrong( |
|
| 2023 | - 'EEH_Line_Item::get_items_subtotal()', |
|
| 2024 | - sprintf( |
|
| 2025 | - esc_html__('Method replaced with %1$s', 'event_espresso'), |
|
| 2026 | - 'EEH_Line_Item::get_pre_tax_subtotal()' |
|
| 2027 | - ), |
|
| 2028 | - '4.6.0' |
|
| 2029 | - ); |
|
| 2030 | - return self::get_pre_tax_subtotal($total_line_item); |
|
| 2031 | - } |
|
| 2032 | - |
|
| 2033 | - |
|
| 2034 | - /** |
|
| 2035 | - * @param EE_Transaction $transaction |
|
| 2036 | - * @return EE_Line_Item |
|
| 2037 | - * @throws EE_Error |
|
| 2038 | - * @throws InvalidArgumentException |
|
| 2039 | - * @throws InvalidDataTypeException |
|
| 2040 | - * @throws InvalidInterfaceException |
|
| 2041 | - * @throws ReflectionException |
|
| 2042 | - * @deprecated |
|
| 2043 | - */ |
|
| 2044 | - public static function create_default_total_line_item($transaction = null) |
|
| 2045 | - { |
|
| 2046 | - EE_Error::doing_it_wrong( |
|
| 2047 | - 'EEH_Line_Item::create_default_total_line_item()', |
|
| 2048 | - sprintf( |
|
| 2049 | - esc_html__('Method replaced with %1$s', 'event_espresso'), |
|
| 2050 | - 'EEH_Line_Item::create_total_line_item()' |
|
| 2051 | - ), |
|
| 2052 | - '4.6.0' |
|
| 2053 | - ); |
|
| 2054 | - return self::create_total_line_item($transaction); |
|
| 2055 | - } |
|
| 2056 | - |
|
| 2057 | - |
|
| 2058 | - /** |
|
| 2059 | - * @param EE_Line_Item $total_line_item |
|
| 2060 | - * @param EE_Transaction $transaction |
|
| 2061 | - * @return EE_Line_Item |
|
| 2062 | - * @throws EE_Error |
|
| 2063 | - * @throws InvalidArgumentException |
|
| 2064 | - * @throws InvalidDataTypeException |
|
| 2065 | - * @throws InvalidInterfaceException |
|
| 2066 | - * @throws ReflectionException |
|
| 2067 | - * @deprecated |
|
| 2068 | - */ |
|
| 2069 | - public static function create_default_tickets_subtotal(EE_Line_Item $total_line_item, $transaction = null) |
|
| 2070 | - { |
|
| 2071 | - EE_Error::doing_it_wrong( |
|
| 2072 | - 'EEH_Line_Item::create_default_tickets_subtotal()', |
|
| 2073 | - sprintf( |
|
| 2074 | - esc_html__('Method replaced with %1$s', 'event_espresso'), |
|
| 2075 | - 'EEH_Line_Item::create_pre_tax_subtotal()' |
|
| 2076 | - ), |
|
| 2077 | - '4.6.0' |
|
| 2078 | - ); |
|
| 2079 | - return self::create_pre_tax_subtotal($total_line_item, $transaction); |
|
| 2080 | - } |
|
| 2081 | - |
|
| 2082 | - |
|
| 2083 | - /** |
|
| 2084 | - * @param EE_Line_Item $total_line_item |
|
| 2085 | - * @param EE_Transaction $transaction |
|
| 2086 | - * @return EE_Line_Item |
|
| 2087 | - * @throws EE_Error |
|
| 2088 | - * @throws InvalidArgumentException |
|
| 2089 | - * @throws InvalidDataTypeException |
|
| 2090 | - * @throws InvalidInterfaceException |
|
| 2091 | - * @throws ReflectionException |
|
| 2092 | - * @deprecated |
|
| 2093 | - */ |
|
| 2094 | - public static function create_default_taxes_subtotal(EE_Line_Item $total_line_item, $transaction = null) |
|
| 2095 | - { |
|
| 2096 | - EE_Error::doing_it_wrong( |
|
| 2097 | - 'EEH_Line_Item::create_default_taxes_subtotal()', |
|
| 2098 | - sprintf( |
|
| 2099 | - esc_html__('Method replaced with %1$s', 'event_espresso'), |
|
| 2100 | - 'EEH_Line_Item::create_taxes_subtotal()' |
|
| 2101 | - ), |
|
| 2102 | - '4.6.0' |
|
| 2103 | - ); |
|
| 2104 | - return self::create_taxes_subtotal($total_line_item, $transaction); |
|
| 2105 | - } |
|
| 2106 | - |
|
| 2107 | - |
|
| 2108 | - /** |
|
| 2109 | - * @param EE_Line_Item $total_line_item |
|
| 2110 | - * @param EE_Transaction $transaction |
|
| 2111 | - * @return EE_Line_Item |
|
| 2112 | - * @throws EE_Error |
|
| 2113 | - * @throws InvalidArgumentException |
|
| 2114 | - * @throws InvalidDataTypeException |
|
| 2115 | - * @throws InvalidInterfaceException |
|
| 2116 | - * @throws ReflectionException |
|
| 2117 | - * @deprecated |
|
| 2118 | - */ |
|
| 2119 | - public static function create_default_event_subtotal(EE_Line_Item $total_line_item, $transaction = null) |
|
| 2120 | - { |
|
| 2121 | - EE_Error::doing_it_wrong( |
|
| 2122 | - 'EEH_Line_Item::create_default_event_subtotal()', |
|
| 2123 | - sprintf( |
|
| 2124 | - esc_html__('Method replaced with %1$s', 'event_espresso'), |
|
| 2125 | - 'EEH_Line_Item::create_event_subtotal()' |
|
| 2126 | - ), |
|
| 2127 | - '4.6.0' |
|
| 2128 | - ); |
|
| 2129 | - return self::create_event_subtotal($total_line_item, $transaction); |
|
| 2130 | - } |
|
| 26 | + /** |
|
| 27 | + * @return CurrencyFormatter |
|
| 28 | + * @since $VID:$ |
|
| 29 | + */ |
|
| 30 | + private static function currencyFormatter() |
|
| 31 | + { |
|
| 32 | + static $currency_formatter; |
|
| 33 | + if (! $currency_formatter instanceof CurrencyFormatter) { |
|
| 34 | + $currency_formatter = LoaderFactory::getLoader()->getShared(CurrencyFormatter::class); |
|
| 35 | + } |
|
| 36 | + return $currency_formatter; |
|
| 37 | + } |
|
| 38 | + |
|
| 39 | + /** |
|
| 40 | + * Adds a simple item (unrelated to any other model object) to the provided PARENT line item. |
|
| 41 | + * Does NOT automatically re-calculate the line item totals or update the related transaction. |
|
| 42 | + * You should call recalculate_total_including_taxes() on the grant total line item after this |
|
| 43 | + * to update the subtotals, and EE_Registration_Processor::calculate_reg_final_prices_per_line_item() |
|
| 44 | + * to keep the registration final prices in-sync with the transaction's total. |
|
| 45 | + * |
|
| 46 | + * @param EE_Line_Item $parent_line_item |
|
| 47 | + * @param string $name |
|
| 48 | + * @param float $unit_price |
|
| 49 | + * @param string $description |
|
| 50 | + * @param int $quantity |
|
| 51 | + * @param boolean $taxable |
|
| 52 | + * @param boolean $code if set to a value, ensures there is only one line item with that code |
|
| 53 | + * @return boolean success |
|
| 54 | + * @throws EE_Error |
|
| 55 | + * @throws InvalidArgumentException |
|
| 56 | + * @throws InvalidDataTypeException |
|
| 57 | + * @throws InvalidInterfaceException |
|
| 58 | + * @throws ReflectionException |
|
| 59 | + */ |
|
| 60 | + public static function add_unrelated_item( |
|
| 61 | + EE_Line_Item $parent_line_item, |
|
| 62 | + $name, |
|
| 63 | + $unit_price, |
|
| 64 | + $description = '', |
|
| 65 | + $quantity = 1, |
|
| 66 | + $taxable = false, |
|
| 67 | + $code = null |
|
| 68 | + ) { |
|
| 69 | + $items_subtotal = self::get_pre_tax_subtotal($parent_line_item); |
|
| 70 | + $line_item = EE_Line_Item::new_instance( |
|
| 71 | + [ |
|
| 72 | + 'LIN_name' => $name, |
|
| 73 | + 'LIN_desc' => $description, |
|
| 74 | + 'LIN_unit_price' => $unit_price, |
|
| 75 | + 'LIN_quantity' => $quantity, |
|
| 76 | + 'LIN_percent' => null, |
|
| 77 | + 'LIN_is_taxable' => $taxable, |
|
| 78 | + 'LIN_order' => $items_subtotal instanceof EE_Line_Item ? count($items_subtotal->children()) : 0, |
|
| 79 | + 'LIN_total' => EEH_Line_Item::currencyFormatter()->roundForLocale( |
|
| 80 | + (float) $unit_price * (int) $quantity |
|
| 81 | + ), |
|
| 82 | + 'LIN_type' => EEM_Line_Item::type_line_item, |
|
| 83 | + 'LIN_code' => $code, |
|
| 84 | + ] |
|
| 85 | + ); |
|
| 86 | + $line_item = apply_filters( |
|
| 87 | + 'FHEE__EEH_Line_Item__add_unrelated_item__line_item', |
|
| 88 | + $line_item, |
|
| 89 | + $parent_line_item |
|
| 90 | + ); |
|
| 91 | + return self::add_item($parent_line_item, $line_item); |
|
| 92 | + } |
|
| 93 | + |
|
| 94 | + |
|
| 95 | + /** |
|
| 96 | + * Adds a simple item ( unrelated to any other model object) to the total line item, |
|
| 97 | + * in the correct spot in the line item tree. Does not automatically |
|
| 98 | + * re-calculate the line item totals, nor update the related transaction, nor upgrade the transaction's |
|
| 99 | + * registrations' final prices (which should probably change because of this). |
|
| 100 | + * You should call recalculate_total_including_taxes() on the grand total line item, then |
|
| 101 | + * update the transaction's total, and EE_Registration_Processor::update_registration_final_prices() |
|
| 102 | + * after using this, to keep the registration final prices in-sync with the transaction's total. |
|
| 103 | + * |
|
| 104 | + * @param EE_Line_Item $parent_line_item |
|
| 105 | + * @param string $name |
|
| 106 | + * @param float $percentage_amount |
|
| 107 | + * @param string $description |
|
| 108 | + * @param boolean $taxable |
|
| 109 | + * @return boolean success |
|
| 110 | + * @throws EE_Error|ReflectionException |
|
| 111 | + */ |
|
| 112 | + public static function add_percentage_based_item( |
|
| 113 | + EE_Line_Item $parent_line_item, |
|
| 114 | + $name, |
|
| 115 | + $percentage_amount, |
|
| 116 | + $description = '', |
|
| 117 | + $taxable = false |
|
| 118 | + ) { |
|
| 119 | + $line_item = EE_Line_Item::new_instance( |
|
| 120 | + [ |
|
| 121 | + 'LIN_name' => $name, |
|
| 122 | + 'LIN_desc' => $description, |
|
| 123 | + 'LIN_unit_price' => 0, |
|
| 124 | + 'LIN_percent' => $percentage_amount, |
|
| 125 | + 'LIN_quantity' => 1, |
|
| 126 | + 'LIN_is_taxable' => $taxable, |
|
| 127 | + 'LIN_total' => EEH_Line_Item::currencyFormatter()->roundForLocale( |
|
| 128 | + $percentage_amount * $parent_line_item->total() / 100 |
|
| 129 | + ), |
|
| 130 | + 'LIN_type' => EEM_Line_Item::type_line_item, |
|
| 131 | + 'LIN_parent' => $parent_line_item->ID(), |
|
| 132 | + ] |
|
| 133 | + ); |
|
| 134 | + $line_item = apply_filters( |
|
| 135 | + 'FHEE__EEH_Line_Item__add_percentage_based_item__line_item', |
|
| 136 | + $line_item |
|
| 137 | + ); |
|
| 138 | + return $parent_line_item->add_child_line_item($line_item, false); |
|
| 139 | + } |
|
| 140 | + |
|
| 141 | + |
|
| 142 | + /** |
|
| 143 | + * Returns the new line item created by adding a purchase of the ticket |
|
| 144 | + * ensures that ticket line item is saved, and that cart total has been recalculated. |
|
| 145 | + * If this ticket has already been purchased, just increments its count. |
|
| 146 | + * Automatically re-calculates the line item totals and updates the related transaction. But |
|
| 147 | + * DOES NOT automatically upgrade the transaction's registrations' final prices (which |
|
| 148 | + * should probably change because of this). |
|
| 149 | + * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item() |
|
| 150 | + * after using this, to keep the registration final prices in-sync with the transaction's total. |
|
| 151 | + * |
|
| 152 | + * @param EE_Line_Item $total_line_item grand total line item of type EEM_Line_Item::type_total |
|
| 153 | + * @param EE_Ticket $ticket |
|
| 154 | + * @param int $qty |
|
| 155 | + * @return EE_Line_Item |
|
| 156 | + * @throws EE_Error |
|
| 157 | + * @throws InvalidArgumentException |
|
| 158 | + * @throws InvalidDataTypeException |
|
| 159 | + * @throws InvalidInterfaceException |
|
| 160 | + * @throws ReflectionException |
|
| 161 | + */ |
|
| 162 | + public static function add_ticket_purchase(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1) |
|
| 163 | + { |
|
| 164 | + if (! $total_line_item instanceof EE_Line_Item || ! $total_line_item->is_total()) { |
|
| 165 | + throw new EE_Error( |
|
| 166 | + sprintf( |
|
| 167 | + esc_html__( |
|
| 168 | + 'A valid line item total is required in order to add tickets. A line item of type "%s" was passed.', |
|
| 169 | + 'event_espresso' |
|
| 170 | + ), |
|
| 171 | + $ticket->ID(), |
|
| 172 | + $total_line_item->ID() |
|
| 173 | + ) |
|
| 174 | + ); |
|
| 175 | + } |
|
| 176 | + // either increment the qty for an existing ticket |
|
| 177 | + $line_item = self::increment_ticket_qty_if_already_in_cart($total_line_item, $ticket, $qty); |
|
| 178 | + // or add a new one |
|
| 179 | + if (! $line_item instanceof EE_Line_Item) { |
|
| 180 | + $line_item = self::create_ticket_line_item($total_line_item, $ticket, $qty); |
|
| 181 | + } |
|
| 182 | + $total_line_item->recalculate_total_including_taxes(); |
|
| 183 | + return $line_item; |
|
| 184 | + } |
|
| 185 | + |
|
| 186 | + |
|
| 187 | + /** |
|
| 188 | + * Returns the new line item created by adding a purchase of the ticket |
|
| 189 | + * |
|
| 190 | + * @param EE_Line_Item $total_line_item |
|
| 191 | + * @param EE_Ticket $ticket |
|
| 192 | + * @param int $qty |
|
| 193 | + * @return EE_Line_Item |
|
| 194 | + * @throws EE_Error |
|
| 195 | + * @throws InvalidArgumentException |
|
| 196 | + * @throws InvalidDataTypeException |
|
| 197 | + * @throws InvalidInterfaceException |
|
| 198 | + * @throws ReflectionException |
|
| 199 | + */ |
|
| 200 | + public static function increment_ticket_qty_if_already_in_cart( |
|
| 201 | + EE_Line_Item $total_line_item, |
|
| 202 | + EE_Ticket $ticket, |
|
| 203 | + $qty = 1 |
|
| 204 | + ) { |
|
| 205 | + $line_item = null; |
|
| 206 | + if ($total_line_item instanceof EE_Line_Item && $total_line_item->is_total()) { |
|
| 207 | + $ticket_line_items = EEH_Line_Item::get_ticket_line_items($total_line_item); |
|
| 208 | + foreach ((array) $ticket_line_items as $ticket_line_item) { |
|
| 209 | + if ( |
|
| 210 | + $ticket_line_item instanceof EE_Line_Item |
|
| 211 | + && (int) $ticket_line_item->OBJ_ID() === (int) $ticket->ID() |
|
| 212 | + ) { |
|
| 213 | + $line_item = $ticket_line_item; |
|
| 214 | + break; |
|
| 215 | + } |
|
| 216 | + } |
|
| 217 | + } |
|
| 218 | + if ($line_item instanceof EE_Line_Item) { |
|
| 219 | + EEH_Line_Item::increment_quantity($line_item, $qty); |
|
| 220 | + return $line_item; |
|
| 221 | + } |
|
| 222 | + return null; |
|
| 223 | + } |
|
| 224 | + |
|
| 225 | + |
|
| 226 | + /** |
|
| 227 | + * Increments the line item and all its children's quantity by $qty (but percent line items are unaffected). |
|
| 228 | + * Does NOT save or recalculate other line items totals |
|
| 229 | + * |
|
| 230 | + * @param EE_Line_Item $line_item |
|
| 231 | + * @param int $qty |
|
| 232 | + * @return void |
|
| 233 | + * @throws EE_Error |
|
| 234 | + * @throws InvalidArgumentException |
|
| 235 | + * @throws InvalidDataTypeException |
|
| 236 | + * @throws InvalidInterfaceException |
|
| 237 | + * @throws ReflectionException |
|
| 238 | + */ |
|
| 239 | + public static function increment_quantity(EE_Line_Item $line_item, $qty = 1) |
|
| 240 | + { |
|
| 241 | + if (! $line_item->is_percent()) { |
|
| 242 | + $qty += $line_item->quantity(); |
|
| 243 | + $line_item->set_quantity($qty); |
|
| 244 | + $line_item->set_total($line_item->unit_price() * $qty); |
|
| 245 | + $line_item->save(); |
|
| 246 | + } |
|
| 247 | + foreach ($line_item->children() as $child) { |
|
| 248 | + if ($child->is_sub_line_item()) { |
|
| 249 | + EEH_Line_Item::update_quantity($child, $qty); |
|
| 250 | + } |
|
| 251 | + } |
|
| 252 | + } |
|
| 253 | + |
|
| 254 | + |
|
| 255 | + /** |
|
| 256 | + * Decrements the line item and all its children's quantity by $qty (but percent line items are unaffected). |
|
| 257 | + * Does NOT save or recalculate other line items totals |
|
| 258 | + * |
|
| 259 | + * @param EE_Line_Item $line_item |
|
| 260 | + * @param int $qty |
|
| 261 | + * @return void |
|
| 262 | + * @throws EE_Error |
|
| 263 | + * @throws InvalidArgumentException |
|
| 264 | + * @throws InvalidDataTypeException |
|
| 265 | + * @throws InvalidInterfaceException |
|
| 266 | + * @throws ReflectionException |
|
| 267 | + */ |
|
| 268 | + public static function decrement_quantity(EE_Line_Item $line_item, $qty = 1) |
|
| 269 | + { |
|
| 270 | + if (! $line_item->is_percent()) { |
|
| 271 | + $qty = $line_item->quantity() - $qty; |
|
| 272 | + $qty = max($qty, 0); |
|
| 273 | + $line_item->set_quantity($qty); |
|
| 274 | + $line_item->set_total($line_item->unit_price() * $qty); |
|
| 275 | + $line_item->save(); |
|
| 276 | + } |
|
| 277 | + foreach ($line_item->children() as $child) { |
|
| 278 | + if ($child->is_sub_line_item()) { |
|
| 279 | + EEH_Line_Item::update_quantity($child, $qty); |
|
| 280 | + } |
|
| 281 | + } |
|
| 282 | + } |
|
| 283 | + |
|
| 284 | + |
|
| 285 | + /** |
|
| 286 | + * Updates the line item and its children's quantities to the specified number. |
|
| 287 | + * Does NOT save them or recalculate totals. |
|
| 288 | + * |
|
| 289 | + * @param EE_Line_Item $line_item |
|
| 290 | + * @param int $new_quantity |
|
| 291 | + * @throws EE_Error |
|
| 292 | + * @throws InvalidArgumentException |
|
| 293 | + * @throws InvalidDataTypeException |
|
| 294 | + * @throws InvalidInterfaceException |
|
| 295 | + * @throws ReflectionException |
|
| 296 | + */ |
|
| 297 | + public static function update_quantity(EE_Line_Item $line_item, $new_quantity) |
|
| 298 | + { |
|
| 299 | + if (! $line_item->is_percent()) { |
|
| 300 | + $line_item->set_quantity($new_quantity); |
|
| 301 | + $line_item->set_total($line_item->unit_price() * $new_quantity); |
|
| 302 | + $line_item->save(); |
|
| 303 | + } |
|
| 304 | + foreach ($line_item->children() as $child) { |
|
| 305 | + if ($child->is_sub_line_item()) { |
|
| 306 | + EEH_Line_Item::update_quantity($child, $new_quantity); |
|
| 307 | + } |
|
| 308 | + } |
|
| 309 | + } |
|
| 310 | + |
|
| 311 | + |
|
| 312 | + /** |
|
| 313 | + * Returns the new line item created by adding a purchase of the ticket |
|
| 314 | + * |
|
| 315 | + * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total |
|
| 316 | + * @param EE_Ticket $ticket |
|
| 317 | + * @param int $qty |
|
| 318 | + * @return EE_Line_Item |
|
| 319 | + * @throws EE_Error |
|
| 320 | + * @throws InvalidArgumentException |
|
| 321 | + * @throws InvalidDataTypeException |
|
| 322 | + * @throws InvalidInterfaceException |
|
| 323 | + * @throws ReflectionException |
|
| 324 | + */ |
|
| 325 | + public static function create_ticket_line_item(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1) |
|
| 326 | + { |
|
| 327 | + $datetimes = $ticket->datetimes(); |
|
| 328 | + $first_datetime = reset($datetimes); |
|
| 329 | + $first_datetime_name = esc_html__('Event', 'event_espresso'); |
|
| 330 | + if ($first_datetime instanceof EE_Datetime && $first_datetime->event() instanceof EE_Event) { |
|
| 331 | + $first_datetime_name = $first_datetime->event()->name(); |
|
| 332 | + } |
|
| 333 | + $event = sprintf(_x('(For %1$s)', '(For Event Name)', 'event_espresso'), $first_datetime_name); |
|
| 334 | + // get event subtotal line |
|
| 335 | + $events_sub_total = self::get_event_line_item_for_ticket($total_line_item, $ticket); |
|
| 336 | + // add $ticket to cart |
|
| 337 | + $line_item = EE_Line_Item::new_instance( |
|
| 338 | + [ |
|
| 339 | + 'LIN_name' => $ticket->name(), |
|
| 340 | + 'LIN_desc' => $ticket->description() !== '' ? $ticket->description() . ' ' . $event : $event, |
|
| 341 | + 'LIN_unit_price' => $ticket->price(), |
|
| 342 | + 'LIN_quantity' => $qty, |
|
| 343 | + 'LIN_is_taxable' => $ticket->taxable(), |
|
| 344 | + 'LIN_order' => count($events_sub_total->children()), |
|
| 345 | + 'LIN_total' => $ticket->price() * $qty, |
|
| 346 | + 'LIN_type' => EEM_Line_Item::type_line_item, |
|
| 347 | + 'OBJ_ID' => $ticket->ID(), |
|
| 348 | + 'OBJ_type' => EEM_Line_Item::OBJ_TYPE_TICKET, |
|
| 349 | + ] |
|
| 350 | + ); |
|
| 351 | + $line_item = apply_filters( |
|
| 352 | + 'FHEE__EEH_Line_Item__create_ticket_line_item__line_item', |
|
| 353 | + $line_item |
|
| 354 | + ); |
|
| 355 | + $events_sub_total->add_child_line_item($line_item); |
|
| 356 | + // now add the sub-line items |
|
| 357 | + $running_total_for_ticket = 0; |
|
| 358 | + foreach ($ticket->prices(['order_by' => ['PRC_order' => 'ASC']]) as $price) { |
|
| 359 | + $sign = $price->is_discount() ? -1 : 1; |
|
| 360 | + $price_total = $price->is_percent() |
|
| 361 | + ? $sign * $running_total_for_ticket * $price->amount() / 100 |
|
| 362 | + : $sign * $price->amount() * $qty; |
|
| 363 | + $price_total = EEH_Line_Item::currencyFormatter()->roundForLocale($price_total); |
|
| 364 | + $sub_line_item = EE_Line_Item::new_instance( |
|
| 365 | + [ |
|
| 366 | + 'LIN_name' => $price->name(), |
|
| 367 | + 'LIN_desc' => $price->desc(), |
|
| 368 | + 'LIN_quantity' => $price->is_percent() ? null : $qty, |
|
| 369 | + 'LIN_is_taxable' => false, |
|
| 370 | + 'LIN_order' => $price->order(), |
|
| 371 | + 'LIN_total' => $price_total, |
|
| 372 | + 'LIN_type' => EEM_Line_Item::type_sub_line_item, |
|
| 373 | + 'OBJ_ID' => $price->ID(), |
|
| 374 | + 'OBJ_type' => EEM_Line_Item::OBJ_TYPE_PRICE, |
|
| 375 | + ] |
|
| 376 | + ); |
|
| 377 | + /** @var EE_Line_Item $sub_line_item */ |
|
| 378 | + $sub_line_item = apply_filters( |
|
| 379 | + 'FHEE__EEH_Line_Item__create_ticket_line_item__sub_line_item', |
|
| 380 | + $sub_line_item |
|
| 381 | + ); |
|
| 382 | + if ($price->is_percent()) { |
|
| 383 | + $sub_line_item->set_percent($sign * $price->amount()); |
|
| 384 | + } else { |
|
| 385 | + $sub_line_item->set_unit_price($sign * $price->amount()); |
|
| 386 | + } |
|
| 387 | + $running_total_for_ticket += $price_total; |
|
| 388 | + $line_item->add_child_line_item($sub_line_item); |
|
| 389 | + } |
|
| 390 | + return $line_item; |
|
| 391 | + } |
|
| 392 | + |
|
| 393 | + |
|
| 394 | + /** |
|
| 395 | + * Adds the specified item under the pre-tax-sub-total line item. Automatically |
|
| 396 | + * re-calculates the line item totals and updates the related transaction. But |
|
| 397 | + * DOES NOT automatically upgrade the transaction's registrations' final prices (which |
|
| 398 | + * should probably change because of this). |
|
| 399 | + * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item() |
|
| 400 | + * after using this, to keep the registration final prices in-sync with the transaction's total. |
|
| 401 | + * |
|
| 402 | + * @param EE_Line_Item $total_line_item |
|
| 403 | + * @param EE_Line_Item $item to be added |
|
| 404 | + * @return boolean |
|
| 405 | + * @throws EE_Error |
|
| 406 | + * @throws InvalidArgumentException |
|
| 407 | + * @throws InvalidDataTypeException |
|
| 408 | + * @throws InvalidInterfaceException |
|
| 409 | + * @throws ReflectionException |
|
| 410 | + */ |
|
| 411 | + public static function add_item(EE_Line_Item $total_line_item, EE_Line_Item $item) |
|
| 412 | + { |
|
| 413 | + $pre_tax_subtotal = self::get_pre_tax_subtotal($total_line_item); |
|
| 414 | + if ($pre_tax_subtotal instanceof EE_Line_Item) { |
|
| 415 | + $success = $pre_tax_subtotal->add_child_line_item($item); |
|
| 416 | + } else { |
|
| 417 | + return false; |
|
| 418 | + } |
|
| 419 | + $total_line_item->recalculate_total_including_taxes(); |
|
| 420 | + return $success; |
|
| 421 | + } |
|
| 422 | + |
|
| 423 | + |
|
| 424 | + /** |
|
| 425 | + * cancels an existing ticket line item, |
|
| 426 | + * by decrementing it's quantity by 1 and adding a new "type_cancellation" sub-line-item. |
|
| 427 | + * ALL totals and subtotals will NEED TO BE UPDATED after performing this action |
|
| 428 | + * |
|
| 429 | + * @param EE_Line_Item $ticket_line_item |
|
| 430 | + * @param int $qty |
|
| 431 | + * @return bool success |
|
| 432 | + * @throws EE_Error |
|
| 433 | + * @throws InvalidArgumentException |
|
| 434 | + * @throws InvalidDataTypeException |
|
| 435 | + * @throws InvalidInterfaceException |
|
| 436 | + * @throws ReflectionException |
|
| 437 | + */ |
|
| 438 | + public static function cancel_ticket_line_item(EE_Line_Item $ticket_line_item, $qty = 1) |
|
| 439 | + { |
|
| 440 | + // validate incoming line_item |
|
| 441 | + if ($ticket_line_item->OBJ_type() !== EEM_Line_Item::OBJ_TYPE_TICKET) { |
|
| 442 | + throw new EE_Error( |
|
| 443 | + sprintf( |
|
| 444 | + esc_html__( |
|
| 445 | + 'The supplied line item must have an Object Type of "Ticket", not %1$s.', |
|
| 446 | + 'event_espresso' |
|
| 447 | + ), |
|
| 448 | + $ticket_line_item->type() |
|
| 449 | + ) |
|
| 450 | + ); |
|
| 451 | + } |
|
| 452 | + if ($ticket_line_item->quantity() < $qty) { |
|
| 453 | + throw new EE_Error( |
|
| 454 | + sprintf( |
|
| 455 | + esc_html__( |
|
| 456 | + 'Can not cancel %1$d ticket(s) because the supplied line item has a quantity of %2$d.', |
|
| 457 | + 'event_espresso' |
|
| 458 | + ), |
|
| 459 | + $qty, |
|
| 460 | + $ticket_line_item->quantity() |
|
| 461 | + ) |
|
| 462 | + ); |
|
| 463 | + } |
|
| 464 | + // decrement ticket quantity; don't rely on auto-fixing when recalculating totals to do this |
|
| 465 | + $ticket_line_item->set_quantity($ticket_line_item->quantity() - $qty); |
|
| 466 | + foreach ($ticket_line_item->children() as $child_line_item) { |
|
| 467 | + if ( |
|
| 468 | + $child_line_item->is_sub_line_item() |
|
| 469 | + && ! $child_line_item->is_percent() |
|
| 470 | + && ! $child_line_item->is_cancellation() |
|
| 471 | + ) { |
|
| 472 | + $child_line_item->set_quantity($child_line_item->quantity() - $qty); |
|
| 473 | + } |
|
| 474 | + } |
|
| 475 | + // get cancellation sub line item |
|
| 476 | + $cancellation_line_item = EEH_Line_Item::get_descendants_of_type( |
|
| 477 | + $ticket_line_item, |
|
| 478 | + EEM_Line_Item::type_cancellation |
|
| 479 | + ); |
|
| 480 | + $cancellation_line_item = reset($cancellation_line_item); |
|
| 481 | + // verify that this ticket was indeed previously cancelled |
|
| 482 | + if ($cancellation_line_item instanceof EE_Line_Item) { |
|
| 483 | + // increment cancelled quantity |
|
| 484 | + $cancellation_line_item->set_quantity($cancellation_line_item->quantity() + $qty); |
|
| 485 | + } else { |
|
| 486 | + // create cancellation sub line item |
|
| 487 | + $cancellation_line_item = EE_Line_Item::new_instance( |
|
| 488 | + [ |
|
| 489 | + 'LIN_name' => esc_html__('Cancellation', 'event_espresso'), |
|
| 490 | + 'LIN_desc' => sprintf( |
|
| 491 | + esc_html_x( |
|
| 492 | + 'Cancelled %1$s : %2$s', |
|
| 493 | + 'Cancelled Ticket Name : 2015-01-01 11:11', |
|
| 494 | + 'event_espresso' |
|
| 495 | + ), |
|
| 496 | + $ticket_line_item->name(), |
|
| 497 | + current_time(get_option('date_format') . ' ' . get_option('time_format')) |
|
| 498 | + ), |
|
| 499 | + 'LIN_unit_price' => 0, // $ticket_line_item->unit_price() |
|
| 500 | + 'LIN_quantity' => $qty, |
|
| 501 | + 'LIN_is_taxable' => $ticket_line_item->is_taxable(), |
|
| 502 | + 'LIN_order' => count($ticket_line_item->children()), |
|
| 503 | + 'LIN_total' => 0, // $ticket_line_item->unit_price() |
|
| 504 | + 'LIN_type' => EEM_Line_Item::type_cancellation, |
|
| 505 | + ] |
|
| 506 | + ); |
|
| 507 | + $ticket_line_item->add_child_line_item($cancellation_line_item); |
|
| 508 | + } |
|
| 509 | + if ($ticket_line_item->save_this_and_descendants() > 0) { |
|
| 510 | + // decrement parent line item quantity |
|
| 511 | + $event_line_item = $ticket_line_item->parent(); |
|
| 512 | + if ( |
|
| 513 | + $event_line_item instanceof EE_Line_Item |
|
| 514 | + && $event_line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_EVENT |
|
| 515 | + ) { |
|
| 516 | + $event_line_item->set_quantity($event_line_item->quantity() - $qty); |
|
| 517 | + $event_line_item->save(); |
|
| 518 | + } |
|
| 519 | + EEH_Line_Item::get_grand_total_and_recalculate_everything($ticket_line_item); |
|
| 520 | + return true; |
|
| 521 | + } |
|
| 522 | + return false; |
|
| 523 | + } |
|
| 524 | + |
|
| 525 | + |
|
| 526 | + /** |
|
| 527 | + * reinstates (un-cancels?) a previously canceled ticket line item, |
|
| 528 | + * by incrementing it's quantity by 1, and decrementing it's "type_cancellation" sub-line-item. |
|
| 529 | + * ALL totals and subtotals will NEED TO BE UPDATED after performing this action |
|
| 530 | + * |
|
| 531 | + * @param EE_Line_Item $ticket_line_item |
|
| 532 | + * @param int $qty |
|
| 533 | + * @return bool success |
|
| 534 | + * @throws EE_Error |
|
| 535 | + * @throws InvalidArgumentException |
|
| 536 | + * @throws InvalidDataTypeException |
|
| 537 | + * @throws InvalidInterfaceException |
|
| 538 | + * @throws ReflectionException |
|
| 539 | + */ |
|
| 540 | + public static function reinstate_canceled_ticket_line_item(EE_Line_Item $ticket_line_item, $qty = 1) |
|
| 541 | + { |
|
| 542 | + // validate incoming line_item |
|
| 543 | + if ($ticket_line_item->OBJ_type() !== EEM_Line_Item::OBJ_TYPE_TICKET) { |
|
| 544 | + throw new EE_Error( |
|
| 545 | + sprintf( |
|
| 546 | + esc_html__( |
|
| 547 | + 'The supplied line item must have an Object Type of "Ticket", not %1$s.', |
|
| 548 | + 'event_espresso' |
|
| 549 | + ), |
|
| 550 | + $ticket_line_item->type() |
|
| 551 | + ) |
|
| 552 | + ); |
|
| 553 | + } |
|
| 554 | + // get cancellation sub line item |
|
| 555 | + $cancellation_line_item = EEH_Line_Item::get_descendants_of_type( |
|
| 556 | + $ticket_line_item, |
|
| 557 | + EEM_Line_Item::type_cancellation |
|
| 558 | + ); |
|
| 559 | + $cancellation_line_item = reset($cancellation_line_item); |
|
| 560 | + // verify that this ticket was indeed previously cancelled |
|
| 561 | + if (! $cancellation_line_item instanceof EE_Line_Item) { |
|
| 562 | + return false; |
|
| 563 | + } |
|
| 564 | + if ($cancellation_line_item->quantity() > $qty) { |
|
| 565 | + // decrement cancelled quantity |
|
| 566 | + $cancellation_line_item->set_quantity($cancellation_line_item->quantity() - $qty); |
|
| 567 | + } elseif ($cancellation_line_item->quantity() === $qty) { |
|
| 568 | + // decrement cancelled quantity in case anyone still has the object kicking around |
|
| 569 | + $cancellation_line_item->set_quantity($cancellation_line_item->quantity() - $qty); |
|
| 570 | + // delete because quantity will end up as 0 |
|
| 571 | + $cancellation_line_item->delete(); |
|
| 572 | + // and attempt to destroy the object, |
|
| 573 | + // even though PHP won't actually destroy it until it needs the memory |
|
| 574 | + unset($cancellation_line_item); |
|
| 575 | + } else { |
|
| 576 | + // what ?!?! negative quantity ?!?! |
|
| 577 | + throw new EE_Error( |
|
| 578 | + sprintf( |
|
| 579 | + esc_html__( |
|
| 580 | + 'Can not reinstate %1$d cancelled ticket(s) because the cancelled ticket quantity is only %2$d.', |
|
| 581 | + 'event_espresso' |
|
| 582 | + ), |
|
| 583 | + $qty, |
|
| 584 | + $cancellation_line_item->quantity() |
|
| 585 | + ) |
|
| 586 | + ); |
|
| 587 | + } |
|
| 588 | + // increment ticket quantity |
|
| 589 | + $ticket_line_item->set_quantity($ticket_line_item->quantity() + $qty); |
|
| 590 | + if ($ticket_line_item->save_this_and_descendants() > 0) { |
|
| 591 | + // increment parent line item quantity |
|
| 592 | + $event_line_item = $ticket_line_item->parent(); |
|
| 593 | + if ( |
|
| 594 | + $event_line_item instanceof EE_Line_Item |
|
| 595 | + && $event_line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_EVENT |
|
| 596 | + ) { |
|
| 597 | + $event_line_item->set_quantity($event_line_item->quantity() + $qty); |
|
| 598 | + } |
|
| 599 | + EEH_Line_Item::get_grand_total_and_recalculate_everything($ticket_line_item); |
|
| 600 | + return true; |
|
| 601 | + } |
|
| 602 | + return false; |
|
| 603 | + } |
|
| 604 | + |
|
| 605 | + |
|
| 606 | + /** |
|
| 607 | + * calls EEH_Line_Item::find_transaction_grand_total_for_line_item() |
|
| 608 | + * then EE_Line_Item::recalculate_total_including_taxes() on the result |
|
| 609 | + * |
|
| 610 | + * @param EE_Line_Item $line_item |
|
| 611 | + * @return float |
|
| 612 | + * @throws EE_Error |
|
| 613 | + * @throws InvalidArgumentException |
|
| 614 | + * @throws InvalidDataTypeException |
|
| 615 | + * @throws InvalidInterfaceException |
|
| 616 | + * @throws ReflectionException |
|
| 617 | + */ |
|
| 618 | + public static function get_grand_total_and_recalculate_everything(EE_Line_Item $line_item) |
|
| 619 | + { |
|
| 620 | + $grand_total_line_item = EEH_Line_Item::find_transaction_grand_total_for_line_item($line_item); |
|
| 621 | + return $grand_total_line_item->recalculate_total_including_taxes(); |
|
| 622 | + } |
|
| 623 | + |
|
| 624 | + |
|
| 625 | + /** |
|
| 626 | + * Gets the line item which contains the subtotal of all the items |
|
| 627 | + * |
|
| 628 | + * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total |
|
| 629 | + * @return EE_Line_Item |
|
| 630 | + * @throws EE_Error |
|
| 631 | + * @throws InvalidArgumentException |
|
| 632 | + * @throws InvalidDataTypeException |
|
| 633 | + * @throws InvalidInterfaceException |
|
| 634 | + * @throws ReflectionException |
|
| 635 | + */ |
|
| 636 | + public static function get_pre_tax_subtotal(EE_Line_Item $total_line_item) |
|
| 637 | + { |
|
| 638 | + $pre_tax_subtotal = $total_line_item->get_child_line_item('pre-tax-subtotal'); |
|
| 639 | + return $pre_tax_subtotal instanceof EE_Line_Item |
|
| 640 | + ? $pre_tax_subtotal |
|
| 641 | + : self::create_pre_tax_subtotal($total_line_item); |
|
| 642 | + } |
|
| 643 | + |
|
| 644 | + |
|
| 645 | + /** |
|
| 646 | + * Gets the line item for the taxes subtotal |
|
| 647 | + * |
|
| 648 | + * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total |
|
| 649 | + * @return EE_Line_Item |
|
| 650 | + * @throws EE_Error |
|
| 651 | + * @throws InvalidArgumentException |
|
| 652 | + * @throws InvalidDataTypeException |
|
| 653 | + * @throws InvalidInterfaceException |
|
| 654 | + * @throws ReflectionException |
|
| 655 | + */ |
|
| 656 | + public static function get_taxes_subtotal(EE_Line_Item $total_line_item) |
|
| 657 | + { |
|
| 658 | + $taxes = $total_line_item->get_child_line_item('taxes'); |
|
| 659 | + return $taxes ? $taxes : self::create_taxes_subtotal($total_line_item); |
|
| 660 | + } |
|
| 661 | + |
|
| 662 | + |
|
| 663 | + /** |
|
| 664 | + * sets the TXN ID on an EE_Line_Item if passed a valid EE_Transaction object |
|
| 665 | + * |
|
| 666 | + * @param EE_Line_Item $line_item |
|
| 667 | + * @param EE_Transaction $transaction |
|
| 668 | + * @return void |
|
| 669 | + * @throws EE_Error |
|
| 670 | + * @throws InvalidArgumentException |
|
| 671 | + * @throws InvalidDataTypeException |
|
| 672 | + * @throws InvalidInterfaceException |
|
| 673 | + * @throws ReflectionException |
|
| 674 | + */ |
|
| 675 | + public static function set_TXN_ID(EE_Line_Item $line_item, $transaction = null) |
|
| 676 | + { |
|
| 677 | + if ($transaction) { |
|
| 678 | + /** @type EEM_Transaction $EEM_Transaction */ |
|
| 679 | + $EEM_Transaction = EE_Registry::instance()->load_model('Transaction'); |
|
| 680 | + $TXN_ID = $EEM_Transaction->ensure_is_ID($transaction); |
|
| 681 | + $line_item->set_TXN_ID($TXN_ID); |
|
| 682 | + } |
|
| 683 | + } |
|
| 684 | + |
|
| 685 | + |
|
| 686 | + /** |
|
| 687 | + * Creates a new default total line item for the transaction, |
|
| 688 | + * and its tickets subtotal and taxes subtotal line items (and adds the |
|
| 689 | + * existing taxes as children of the taxes subtotal line item) |
|
| 690 | + * |
|
| 691 | + * @param EE_Transaction $transaction |
|
| 692 | + * @return EE_Line_Item of type total |
|
| 693 | + * @throws EE_Error |
|
| 694 | + * @throws InvalidArgumentException |
|
| 695 | + * @throws InvalidDataTypeException |
|
| 696 | + * @throws InvalidInterfaceException |
|
| 697 | + * @throws ReflectionException |
|
| 698 | + */ |
|
| 699 | + public static function create_total_line_item($transaction = null) |
|
| 700 | + { |
|
| 701 | + $total_line_item = EE_Line_Item::new_instance( |
|
| 702 | + [ |
|
| 703 | + 'LIN_code' => 'total', |
|
| 704 | + 'LIN_name' => esc_html__('Grand Total', 'event_espresso'), |
|
| 705 | + 'LIN_type' => EEM_Line_Item::type_total, |
|
| 706 | + 'OBJ_type' => EEM_Line_Item::OBJ_TYPE_TRANSACTION, |
|
| 707 | + ] |
|
| 708 | + ); |
|
| 709 | + $total_line_item = apply_filters( |
|
| 710 | + 'FHEE__EEH_Line_Item__create_total_line_item__total_line_item', |
|
| 711 | + $total_line_item |
|
| 712 | + ); |
|
| 713 | + self::set_TXN_ID($total_line_item, $transaction); |
|
| 714 | + self::create_pre_tax_subtotal($total_line_item, $transaction); |
|
| 715 | + self::create_taxes_subtotal($total_line_item, $transaction); |
|
| 716 | + return $total_line_item; |
|
| 717 | + } |
|
| 718 | + |
|
| 719 | + |
|
| 720 | + /** |
|
| 721 | + * Creates a default items subtotal line item |
|
| 722 | + * |
|
| 723 | + * @param EE_Line_Item $total_line_item |
|
| 724 | + * @param EE_Transaction $transaction |
|
| 725 | + * @return EE_Line_Item |
|
| 726 | + * @throws EE_Error |
|
| 727 | + * @throws InvalidArgumentException |
|
| 728 | + * @throws InvalidDataTypeException |
|
| 729 | + * @throws InvalidInterfaceException |
|
| 730 | + * @throws ReflectionException |
|
| 731 | + */ |
|
| 732 | + protected static function create_pre_tax_subtotal(EE_Line_Item $total_line_item, $transaction = null) |
|
| 733 | + { |
|
| 734 | + $pre_tax_line_item = EE_Line_Item::new_instance( |
|
| 735 | + [ |
|
| 736 | + 'LIN_code' => 'pre-tax-subtotal', |
|
| 737 | + 'LIN_name' => esc_html__('Pre-Tax Subtotal', 'event_espresso'), |
|
| 738 | + 'LIN_type' => EEM_Line_Item::type_sub_total, |
|
| 739 | + ] |
|
| 740 | + ); |
|
| 741 | + $pre_tax_line_item = apply_filters( |
|
| 742 | + 'FHEE__EEH_Line_Item__create_pre_tax_subtotal__pre_tax_line_item', |
|
| 743 | + $pre_tax_line_item |
|
| 744 | + ); |
|
| 745 | + self::set_TXN_ID($pre_tax_line_item, $transaction); |
|
| 746 | + $total_line_item->add_child_line_item($pre_tax_line_item); |
|
| 747 | + self::create_event_subtotal($pre_tax_line_item, $transaction); |
|
| 748 | + return $pre_tax_line_item; |
|
| 749 | + } |
|
| 750 | + |
|
| 751 | + |
|
| 752 | + /** |
|
| 753 | + * Creates a line item for the taxes subtotal and finds all the tax prices |
|
| 754 | + * and applies taxes to it |
|
| 755 | + * |
|
| 756 | + * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total |
|
| 757 | + * @param EE_Transaction $transaction |
|
| 758 | + * @return EE_Line_Item |
|
| 759 | + * @throws EE_Error |
|
| 760 | + * @throws InvalidArgumentException |
|
| 761 | + * @throws InvalidDataTypeException |
|
| 762 | + * @throws InvalidInterfaceException |
|
| 763 | + * @throws ReflectionException |
|
| 764 | + */ |
|
| 765 | + protected static function create_taxes_subtotal(EE_Line_Item $total_line_item, $transaction = null) |
|
| 766 | + { |
|
| 767 | + $tax_line_item = EE_Line_Item::new_instance( |
|
| 768 | + [ |
|
| 769 | + 'LIN_code' => 'taxes', |
|
| 770 | + 'LIN_name' => esc_html__('Taxes', 'event_espresso'), |
|
| 771 | + 'LIN_type' => EEM_Line_Item::type_tax_sub_total, |
|
| 772 | + 'LIN_order' => 1000,// this should always come last |
|
| 773 | + ] |
|
| 774 | + ); |
|
| 775 | + $tax_line_item = apply_filters( |
|
| 776 | + 'FHEE__EEH_Line_Item__create_taxes_subtotal__tax_line_item', |
|
| 777 | + $tax_line_item |
|
| 778 | + ); |
|
| 779 | + self::set_TXN_ID($tax_line_item, $transaction); |
|
| 780 | + $total_line_item->add_child_line_item($tax_line_item); |
|
| 781 | + // and lastly, add the actual taxes |
|
| 782 | + self::apply_taxes($total_line_item); |
|
| 783 | + return $tax_line_item; |
|
| 784 | + } |
|
| 785 | + |
|
| 786 | + |
|
| 787 | + /** |
|
| 788 | + * Creates a default items subtotal line item |
|
| 789 | + * |
|
| 790 | + * @param EE_Line_Item $pre_tax_line_item |
|
| 791 | + * @param EE_Transaction $transaction |
|
| 792 | + * @param EE_Event $event |
|
| 793 | + * @return EE_Line_Item |
|
| 794 | + * @throws EE_Error |
|
| 795 | + * @throws InvalidArgumentException |
|
| 796 | + * @throws InvalidDataTypeException |
|
| 797 | + * @throws InvalidInterfaceException |
|
| 798 | + * @throws ReflectionException |
|
| 799 | + */ |
|
| 800 | + public static function create_event_subtotal(EE_Line_Item $pre_tax_line_item, $transaction = null, $event = null) |
|
| 801 | + { |
|
| 802 | + $event_line_item = EE_Line_Item::new_instance( |
|
| 803 | + [ |
|
| 804 | + 'LIN_code' => self::get_event_code($event), |
|
| 805 | + 'LIN_name' => self::get_event_name($event), |
|
| 806 | + 'LIN_desc' => self::get_event_desc($event), |
|
| 807 | + 'LIN_type' => EEM_Line_Item::type_sub_total, |
|
| 808 | + 'OBJ_type' => EEM_Line_Item::OBJ_TYPE_EVENT, |
|
| 809 | + 'OBJ_ID' => $event instanceof EE_Event ? $event->ID() : 0, |
|
| 810 | + ] |
|
| 811 | + ); |
|
| 812 | + $event_line_item = apply_filters( |
|
| 813 | + 'FHEE__EEH_Line_Item__create_event_subtotal__event_line_item', |
|
| 814 | + $event_line_item |
|
| 815 | + ); |
|
| 816 | + self::set_TXN_ID($event_line_item, $transaction); |
|
| 817 | + $pre_tax_line_item->add_child_line_item($event_line_item); |
|
| 818 | + return $event_line_item; |
|
| 819 | + } |
|
| 820 | + |
|
| 821 | + |
|
| 822 | + /** |
|
| 823 | + * Gets what the event ticket's code SHOULD be |
|
| 824 | + * |
|
| 825 | + * @param EE_Event $event |
|
| 826 | + * @return string |
|
| 827 | + * @throws EE_Error|ReflectionException |
|
| 828 | + */ |
|
| 829 | + public static function get_event_code($event) |
|
| 830 | + { |
|
| 831 | + return 'event-' . ($event instanceof EE_Event ? $event->ID() : '0'); |
|
| 832 | + } |
|
| 833 | + |
|
| 834 | + |
|
| 835 | + /** |
|
| 836 | + * Gets the event name |
|
| 837 | + * |
|
| 838 | + * @param EE_Event $event |
|
| 839 | + * @return string |
|
| 840 | + * @throws EE_Error |
|
| 841 | + */ |
|
| 842 | + public static function get_event_name($event) |
|
| 843 | + { |
|
| 844 | + return $event instanceof EE_Event |
|
| 845 | + ? mb_substr($event->name(), 0, 245) |
|
| 846 | + : esc_html__('Event', 'event_espresso'); |
|
| 847 | + } |
|
| 848 | + |
|
| 849 | + |
|
| 850 | + /** |
|
| 851 | + * Gets the event excerpt |
|
| 852 | + * |
|
| 853 | + * @param EE_Event $event |
|
| 854 | + * @return string |
|
| 855 | + * @throws EE_Error |
|
| 856 | + */ |
|
| 857 | + public static function get_event_desc($event) |
|
| 858 | + { |
|
| 859 | + return $event instanceof EE_Event ? $event->short_description() : ''; |
|
| 860 | + } |
|
| 861 | + |
|
| 862 | + |
|
| 863 | + /** |
|
| 864 | + * Given the grand total line item and a ticket, finds the event sub-total |
|
| 865 | + * line item the ticket's purchase should be added onto |
|
| 866 | + * |
|
| 867 | + * @access public |
|
| 868 | + * @param EE_Line_Item $grand_total the grand total line item |
|
| 869 | + * @param EE_Ticket $ticket |
|
| 870 | + * @return EE_Line_Item |
|
| 871 | + * @throws EE_Error |
|
| 872 | + * @throws InvalidArgumentException |
|
| 873 | + * @throws InvalidDataTypeException |
|
| 874 | + * @throws InvalidInterfaceException |
|
| 875 | + * @throws ReflectionException |
|
| 876 | + */ |
|
| 877 | + public static function get_event_line_item_for_ticket(EE_Line_Item $grand_total, EE_Ticket $ticket) |
|
| 878 | + { |
|
| 879 | + $first_datetime = $ticket->first_datetime(); |
|
| 880 | + if (! $first_datetime instanceof EE_Datetime) { |
|
| 881 | + throw new EE_Error( |
|
| 882 | + sprintf( |
|
| 883 | + esc_html__('The supplied ticket (ID %d) has no datetimes', 'event_espresso'), |
|
| 884 | + $ticket->ID() |
|
| 885 | + ) |
|
| 886 | + ); |
|
| 887 | + } |
|
| 888 | + $event = $first_datetime->event(); |
|
| 889 | + if (! $event instanceof EE_Event) { |
|
| 890 | + throw new EE_Error( |
|
| 891 | + sprintf( |
|
| 892 | + esc_html__( |
|
| 893 | + 'The supplied ticket (ID %d) has no event data associated with it.', |
|
| 894 | + 'event_espresso' |
|
| 895 | + ), |
|
| 896 | + $ticket->ID() |
|
| 897 | + ) |
|
| 898 | + ); |
|
| 899 | + } |
|
| 900 | + $events_sub_total = EEH_Line_Item::get_event_line_item($grand_total, $event); |
|
| 901 | + if (! $events_sub_total instanceof EE_Line_Item) { |
|
| 902 | + throw new EE_Error( |
|
| 903 | + sprintf( |
|
| 904 | + esc_html__( |
|
| 905 | + 'There is no events sub-total for ticket %s on total line item %d', |
|
| 906 | + 'event_espresso' |
|
| 907 | + ), |
|
| 908 | + $ticket->ID(), |
|
| 909 | + $grand_total->ID() |
|
| 910 | + ) |
|
| 911 | + ); |
|
| 912 | + } |
|
| 913 | + return $events_sub_total; |
|
| 914 | + } |
|
| 915 | + |
|
| 916 | + |
|
| 917 | + /** |
|
| 918 | + * Gets the event line item |
|
| 919 | + * |
|
| 920 | + * @param EE_Line_Item $grand_total |
|
| 921 | + * @param EE_Event $event |
|
| 922 | + * @return EE_Line_Item for the event subtotal which is a child of $grand_total |
|
| 923 | + * @throws EE_Error |
|
| 924 | + * @throws InvalidArgumentException |
|
| 925 | + * @throws InvalidDataTypeException |
|
| 926 | + * @throws InvalidInterfaceException |
|
| 927 | + * @throws ReflectionException |
|
| 928 | + */ |
|
| 929 | + public static function get_event_line_item(EE_Line_Item $grand_total, $event) |
|
| 930 | + { |
|
| 931 | + /** @type EE_Event $event */ |
|
| 932 | + $event = EEM_Event::instance()->ensure_is_obj($event, true); |
|
| 933 | + $event_line_item = null; |
|
| 934 | + $found = false; |
|
| 935 | + foreach (EEH_Line_Item::get_event_subtotals($grand_total) as $event_line_item) { |
|
| 936 | + // default event subtotal, we should only ever find this the first time this method is called |
|
| 937 | + if (! $event_line_item->OBJ_ID()) { |
|
| 938 | + // let's use this! but first... set the event details |
|
| 939 | + EEH_Line_Item::set_event_subtotal_details($event_line_item, $event); |
|
| 940 | + $found = true; |
|
| 941 | + break; |
|
| 942 | + } |
|
| 943 | + if ($event_line_item->OBJ_ID() === $event->ID()) { |
|
| 944 | + // found existing line item for this event in the cart, so break out of loop and use this one |
|
| 945 | + $found = true; |
|
| 946 | + break; |
|
| 947 | + } |
|
| 948 | + } |
|
| 949 | + if (! $found) { |
|
| 950 | + // there is no event sub-total yet, so add it |
|
| 951 | + $pre_tax_subtotal = EEH_Line_Item::get_pre_tax_subtotal($grand_total); |
|
| 952 | + // create a new "event" subtotal below that |
|
| 953 | + $event_line_item = EEH_Line_Item::create_event_subtotal($pre_tax_subtotal, null, $event); |
|
| 954 | + // and set the event details |
|
| 955 | + EEH_Line_Item::set_event_subtotal_details($event_line_item, $event); |
|
| 956 | + } |
|
| 957 | + return $event_line_item; |
|
| 958 | + } |
|
| 959 | + |
|
| 960 | + |
|
| 961 | + /** |
|
| 962 | + * Creates a default items subtotal line item |
|
| 963 | + * |
|
| 964 | + * @param EE_Line_Item $event_line_item |
|
| 965 | + * @param EE_Event $event |
|
| 966 | + * @param EE_Transaction $transaction |
|
| 967 | + * @return void |
|
| 968 | + * @throws EE_Error |
|
| 969 | + * @throws InvalidArgumentException |
|
| 970 | + * @throws InvalidDataTypeException |
|
| 971 | + * @throws InvalidInterfaceException |
|
| 972 | + * @throws ReflectionException |
|
| 973 | + */ |
|
| 974 | + public static function set_event_subtotal_details( |
|
| 975 | + EE_Line_Item $event_line_item, |
|
| 976 | + EE_Event $event, |
|
| 977 | + $transaction = null |
|
| 978 | + ) { |
|
| 979 | + if ($event instanceof EE_Event) { |
|
| 980 | + $event_line_item->set_code(self::get_event_code($event)); |
|
| 981 | + $event_line_item->set_name(self::get_event_name($event)); |
|
| 982 | + $event_line_item->set_desc(self::get_event_desc($event)); |
|
| 983 | + $event_line_item->set_OBJ_ID($event->ID()); |
|
| 984 | + } |
|
| 985 | + self::set_TXN_ID($event_line_item, $transaction); |
|
| 986 | + } |
|
| 987 | + |
|
| 988 | + |
|
| 989 | + /** |
|
| 990 | + * Finds what taxes should apply, adds them as tax line items under the taxes sub-total, |
|
| 991 | + * and recalculates the taxes sub-total and the grand total. Resets the taxes, so |
|
| 992 | + * any old taxes are removed |
|
| 993 | + * |
|
| 994 | + * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total |
|
| 995 | + * @param bool $update_txn_status |
|
| 996 | + * @return bool |
|
| 997 | + * @throws EE_Error |
|
| 998 | + * @throws InvalidArgumentException |
|
| 999 | + * @throws InvalidDataTypeException |
|
| 1000 | + * @throws InvalidInterfaceException |
|
| 1001 | + * @throws ReflectionException |
|
| 1002 | + * @throws RuntimeException |
|
| 1003 | + */ |
|
| 1004 | + public static function apply_taxes(EE_Line_Item $total_line_item, $update_txn_status = false) |
|
| 1005 | + { |
|
| 1006 | + /** @type EEM_Price $EEM_Price */ |
|
| 1007 | + $EEM_Price = EE_Registry::instance()->load_model('Price'); |
|
| 1008 | + // get array of taxes via Price Model |
|
| 1009 | + $ordered_taxes = $EEM_Price->get_all_prices_that_are_taxes(); |
|
| 1010 | + ksort($ordered_taxes); |
|
| 1011 | + $taxes_line_item = self::get_taxes_subtotal($total_line_item); |
|
| 1012 | + // just to be safe, remove its old tax line items |
|
| 1013 | + $deleted = $taxes_line_item->delete_children_line_items(); |
|
| 1014 | + $updates = false; |
|
| 1015 | + // loop thru taxes |
|
| 1016 | + foreach ($ordered_taxes as $order => $taxes) { |
|
| 1017 | + foreach ($taxes as $tax) { |
|
| 1018 | + if ($tax instanceof EE_Price) { |
|
| 1019 | + $tax_line_item = EE_Line_Item::new_instance( |
|
| 1020 | + [ |
|
| 1021 | + 'LIN_name' => $tax->name(), |
|
| 1022 | + 'LIN_desc' => $tax->desc(), |
|
| 1023 | + 'LIN_percent' => $tax->amount(), |
|
| 1024 | + 'LIN_is_taxable' => false, |
|
| 1025 | + 'LIN_order' => $order, |
|
| 1026 | + 'LIN_total' => 0, |
|
| 1027 | + 'LIN_type' => EEM_Line_Item::type_tax, |
|
| 1028 | + 'OBJ_type' => EEM_Line_Item::OBJ_TYPE_PRICE, |
|
| 1029 | + 'OBJ_ID' => $tax->ID(), |
|
| 1030 | + ] |
|
| 1031 | + ); |
|
| 1032 | + $tax_line_item = apply_filters( |
|
| 1033 | + 'FHEE__EEH_Line_Item__apply_taxes__tax_line_item', |
|
| 1034 | + $tax_line_item |
|
| 1035 | + ); |
|
| 1036 | + $updates = $taxes_line_item->add_child_line_item($tax_line_item) |
|
| 1037 | + ? |
|
| 1038 | + true |
|
| 1039 | + : |
|
| 1040 | + $updates; |
|
| 1041 | + } |
|
| 1042 | + } |
|
| 1043 | + } |
|
| 1044 | + // only recalculate totals if something changed |
|
| 1045 | + if ($deleted || $updates) { |
|
| 1046 | + $total_line_item->recalculate_total_including_taxes($update_txn_status); |
|
| 1047 | + return true; |
|
| 1048 | + } |
|
| 1049 | + return false; |
|
| 1050 | + } |
|
| 1051 | + |
|
| 1052 | + |
|
| 1053 | + /** |
|
| 1054 | + * Ensures that taxes have been applied to the order, if not applies them. |
|
| 1055 | + * Returns the total amount of tax |
|
| 1056 | + * |
|
| 1057 | + * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total |
|
| 1058 | + * @return float |
|
| 1059 | + * @throws EE_Error |
|
| 1060 | + * @throws InvalidArgumentException |
|
| 1061 | + * @throws InvalidDataTypeException |
|
| 1062 | + * @throws InvalidInterfaceException |
|
| 1063 | + * @throws ReflectionException |
|
| 1064 | + */ |
|
| 1065 | + public static function ensure_taxes_applied($total_line_item) |
|
| 1066 | + { |
|
| 1067 | + $taxes_subtotal = self::get_taxes_subtotal($total_line_item); |
|
| 1068 | + if (! $taxes_subtotal->children()) { |
|
| 1069 | + self::apply_taxes($total_line_item); |
|
| 1070 | + } |
|
| 1071 | + return $taxes_subtotal->total(); |
|
| 1072 | + } |
|
| 1073 | + |
|
| 1074 | + |
|
| 1075 | + /** |
|
| 1076 | + * Deletes ALL children of the passed line item |
|
| 1077 | + * |
|
| 1078 | + * @param EE_Line_Item $parent_line_item |
|
| 1079 | + * @return bool |
|
| 1080 | + * @throws EE_Error |
|
| 1081 | + * @throws InvalidArgumentException |
|
| 1082 | + * @throws InvalidDataTypeException |
|
| 1083 | + * @throws InvalidInterfaceException |
|
| 1084 | + * @throws ReflectionException |
|
| 1085 | + */ |
|
| 1086 | + public static function delete_all_child_items(EE_Line_Item $parent_line_item) |
|
| 1087 | + { |
|
| 1088 | + $deleted = 0; |
|
| 1089 | + foreach ($parent_line_item->children() as $child_line_item) { |
|
| 1090 | + if ($child_line_item instanceof EE_Line_Item) { |
|
| 1091 | + $deleted += EEH_Line_Item::delete_all_child_items($child_line_item); |
|
| 1092 | + if ($child_line_item->ID()) { |
|
| 1093 | + $child_line_item->delete(); |
|
| 1094 | + unset($child_line_item); |
|
| 1095 | + } else { |
|
| 1096 | + $parent_line_item->delete_child_line_item($child_line_item->code()); |
|
| 1097 | + } |
|
| 1098 | + $deleted++; |
|
| 1099 | + } |
|
| 1100 | + } |
|
| 1101 | + return $deleted; |
|
| 1102 | + } |
|
| 1103 | + |
|
| 1104 | + |
|
| 1105 | + /** |
|
| 1106 | + * Deletes the line items as indicated by the line item code(s) provided, |
|
| 1107 | + * regardless of where they're found in the line item tree. Automatically |
|
| 1108 | + * re-calculates the line item totals and updates the related transaction. But |
|
| 1109 | + * DOES NOT automatically upgrade the transaction's registrations' final prices (which |
|
| 1110 | + * should probably change because of this). |
|
| 1111 | + * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item() |
|
| 1112 | + * after using this, to keep the registration final prices in-sync with the transaction's total. |
|
| 1113 | + * |
|
| 1114 | + * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total |
|
| 1115 | + * @param array|bool|string $line_item_codes |
|
| 1116 | + * @return int number of items successfully removed |
|
| 1117 | + * @throws EE_Error|ReflectionException |
|
| 1118 | + */ |
|
| 1119 | + public static function delete_items(EE_Line_Item $total_line_item, $line_item_codes = false) |
|
| 1120 | + { |
|
| 1121 | + if ($total_line_item->type() !== EEM_Line_Item::type_total) { |
|
| 1122 | + EE_Error::doing_it_wrong( |
|
| 1123 | + 'EEH_Line_Item::delete_items', |
|
| 1124 | + esc_html__( |
|
| 1125 | + 'This static method should only be called with a TOTAL line item, otherwise we won\'t recalculate the totals correctly', |
|
| 1126 | + 'event_espresso' |
|
| 1127 | + ), |
|
| 1128 | + '4.6.18' |
|
| 1129 | + ); |
|
| 1130 | + } |
|
| 1131 | + do_action('AHEE_log', __FILE__, __FUNCTION__, ''); |
|
| 1132 | + |
|
| 1133 | + // check if only a single line_item_id was passed |
|
| 1134 | + if (! empty($line_item_codes) && ! is_array($line_item_codes)) { |
|
| 1135 | + // place single line_item_id in an array to appear as multiple line_item_ids |
|
| 1136 | + $line_item_codes = [$line_item_codes]; |
|
| 1137 | + } |
|
| 1138 | + $removals = 0; |
|
| 1139 | + // cycle thru line_item_ids |
|
| 1140 | + foreach ($line_item_codes as $line_item_id) { |
|
| 1141 | + $removals += $total_line_item->delete_child_line_item($line_item_id); |
|
| 1142 | + } |
|
| 1143 | + |
|
| 1144 | + if ($removals > 0) { |
|
| 1145 | + $total_line_item->recalculate_taxes_and_tax_total(); |
|
| 1146 | + return $removals; |
|
| 1147 | + } else { |
|
| 1148 | + return false; |
|
| 1149 | + } |
|
| 1150 | + } |
|
| 1151 | + |
|
| 1152 | + |
|
| 1153 | + /** |
|
| 1154 | + * Overwrites the previous tax by clearing out the old taxes, and creates a new |
|
| 1155 | + * tax and updates the total line item accordingly |
|
| 1156 | + * |
|
| 1157 | + * @param EE_Line_Item $total_line_item |
|
| 1158 | + * @param float $amount |
|
| 1159 | + * @param string $name |
|
| 1160 | + * @param string $description |
|
| 1161 | + * @param string $code |
|
| 1162 | + * @param boolean $add_to_existing_line_item |
|
| 1163 | + * if true, and a duplicate line item with the same code is found, |
|
| 1164 | + * $amount will be added onto it; otherwise will simply set the taxes to match $amount |
|
| 1165 | + * @return EE_Line_Item the new tax line item created |
|
| 1166 | + * @throws EE_Error |
|
| 1167 | + * @throws InvalidArgumentException |
|
| 1168 | + * @throws InvalidDataTypeException |
|
| 1169 | + * @throws InvalidInterfaceException |
|
| 1170 | + * @throws ReflectionException |
|
| 1171 | + */ |
|
| 1172 | + public static function set_total_tax_to( |
|
| 1173 | + EE_Line_Item $total_line_item, |
|
| 1174 | + $amount, |
|
| 1175 | + $name = null, |
|
| 1176 | + $description = null, |
|
| 1177 | + $code = null, |
|
| 1178 | + $add_to_existing_line_item = false |
|
| 1179 | + ) { |
|
| 1180 | + $tax_subtotal = self::get_taxes_subtotal($total_line_item); |
|
| 1181 | + $taxable_total = $total_line_item->taxable_total(); |
|
| 1182 | + |
|
| 1183 | + if ($add_to_existing_line_item) { |
|
| 1184 | + $new_tax = $tax_subtotal->get_child_line_item($code); |
|
| 1185 | + EEM_Line_Item::instance()->delete( |
|
| 1186 | + [['LIN_code' => ['!=', $code], 'LIN_parent' => $tax_subtotal->ID()]] |
|
| 1187 | + ); |
|
| 1188 | + } else { |
|
| 1189 | + $new_tax = null; |
|
| 1190 | + $tax_subtotal->delete_children_line_items(); |
|
| 1191 | + } |
|
| 1192 | + if ($new_tax) { |
|
| 1193 | + $new_tax->set_total($new_tax->total() + $amount); |
|
| 1194 | + $percent = $taxable_total ? $new_tax->total() / $taxable_total * 100 : 0; |
|
| 1195 | + $new_tax->set_percent(EEH_Line_Item::currencyFormatter()->roundForLocale($percent)); |
|
| 1196 | + } else { |
|
| 1197 | + $percent = $taxable_total ? ($amount / $taxable_total * 100) : 0; |
|
| 1198 | + // no existing tax item. Create it |
|
| 1199 | + $new_tax = EE_Line_Item::new_instance( |
|
| 1200 | + [ |
|
| 1201 | + 'TXN_ID' => $total_line_item->TXN_ID(), |
|
| 1202 | + 'LIN_name' => $name ? $name : esc_html__('Tax', 'event_espresso'), |
|
| 1203 | + 'LIN_desc' => $description ? $description : '', |
|
| 1204 | + 'LIN_percent' => EEH_Line_Item::currencyFormatter()->roundForLocale($percent), |
|
| 1205 | + 'LIN_total' => $amount, |
|
| 1206 | + 'LIN_parent' => $tax_subtotal->ID(), |
|
| 1207 | + 'LIN_type' => EEM_Line_Item::type_tax, |
|
| 1208 | + 'LIN_code' => $code, |
|
| 1209 | + ] |
|
| 1210 | + ); |
|
| 1211 | + } |
|
| 1212 | + |
|
| 1213 | + $new_tax = apply_filters( |
|
| 1214 | + 'FHEE__EEH_Line_Item__set_total_tax_to__new_tax_subtotal', |
|
| 1215 | + $new_tax, |
|
| 1216 | + $total_line_item |
|
| 1217 | + ); |
|
| 1218 | + $new_tax->save(); |
|
| 1219 | + $tax_subtotal->set_total($new_tax->total()); |
|
| 1220 | + $tax_subtotal->save(); |
|
| 1221 | + $total_line_item->recalculate_total_including_taxes(); |
|
| 1222 | + return $new_tax; |
|
| 1223 | + } |
|
| 1224 | + |
|
| 1225 | + |
|
| 1226 | + /** |
|
| 1227 | + * Makes all the line items which are children of $line_item taxable (or not). |
|
| 1228 | + * Does NOT save the line items |
|
| 1229 | + * |
|
| 1230 | + * @param EE_Line_Item $line_item |
|
| 1231 | + * @param boolean $taxable |
|
| 1232 | + * @param string $code_substring_for_whitelist if this string is part of the line item's code |
|
| 1233 | + * it will be whitelisted (ie, except from becoming taxable) |
|
| 1234 | + * @throws EE_Error|ReflectionException |
|
| 1235 | + */ |
|
| 1236 | + public static function set_line_items_taxable( |
|
| 1237 | + EE_Line_Item $line_item, |
|
| 1238 | + $taxable = true, |
|
| 1239 | + $code_substring_for_whitelist = null |
|
| 1240 | + ) { |
|
| 1241 | + $whitelisted = false; |
|
| 1242 | + if ($code_substring_for_whitelist !== null) { |
|
| 1243 | + $whitelisted = strpos($line_item->code(), $code_substring_for_whitelist) !== false; |
|
| 1244 | + } |
|
| 1245 | + if (! $whitelisted && $line_item->is_line_item()) { |
|
| 1246 | + $line_item->set_is_taxable($taxable); |
|
| 1247 | + } |
|
| 1248 | + foreach ($line_item->children() as $child_line_item) { |
|
| 1249 | + EEH_Line_Item::set_line_items_taxable( |
|
| 1250 | + $child_line_item, |
|
| 1251 | + $taxable, |
|
| 1252 | + $code_substring_for_whitelist |
|
| 1253 | + ); |
|
| 1254 | + } |
|
| 1255 | + } |
|
| 1256 | + |
|
| 1257 | + |
|
| 1258 | + /** |
|
| 1259 | + * Gets all descendants that are event subtotals |
|
| 1260 | + * |
|
| 1261 | + * @param EE_Line_Item $parent_line_item - the line item to find descendants of |
|
| 1262 | + * @return EE_Line_Item[] |
|
| 1263 | + * @throws EE_Error|ReflectionException |
|
| 1264 | + * @uses EEH_Line_Item::get_subtotals_of_object_type() |
|
| 1265 | + */ |
|
| 1266 | + public static function get_event_subtotals(EE_Line_Item $parent_line_item) |
|
| 1267 | + { |
|
| 1268 | + return self::get_subtotals_of_object_type($parent_line_item, EEM_Line_Item::OBJ_TYPE_EVENT); |
|
| 1269 | + } |
|
| 1270 | + |
|
| 1271 | + |
|
| 1272 | + /** |
|
| 1273 | + * Gets all descendants subtotals that match the supplied object type |
|
| 1274 | + * |
|
| 1275 | + * @param EE_Line_Item $parent_line_item - the line item to find descendants of |
|
| 1276 | + * @param string $obj_type |
|
| 1277 | + * @return EE_Line_Item[] |
|
| 1278 | + * @throws EE_Error|ReflectionException |
|
| 1279 | + * @uses EEH_Line_Item::_get_descendants_by_type_and_object_type() |
|
| 1280 | + */ |
|
| 1281 | + public static function get_subtotals_of_object_type(EE_Line_Item $parent_line_item, $obj_type = '') |
|
| 1282 | + { |
|
| 1283 | + return self::_get_descendants_by_type_and_object_type( |
|
| 1284 | + $parent_line_item, |
|
| 1285 | + EEM_Line_Item::type_sub_total, |
|
| 1286 | + $obj_type |
|
| 1287 | + ); |
|
| 1288 | + } |
|
| 1289 | + |
|
| 1290 | + |
|
| 1291 | + /** |
|
| 1292 | + * Gets all descendants that are tickets |
|
| 1293 | + * |
|
| 1294 | + * @param EE_Line_Item $parent_line_item - the line item to find descendants of |
|
| 1295 | + * @return EE_Line_Item[] |
|
| 1296 | + * @throws EE_Error|ReflectionException |
|
| 1297 | + * @uses EEH_Line_Item::get_line_items_of_object_type() |
|
| 1298 | + */ |
|
| 1299 | + public static function get_ticket_line_items(EE_Line_Item $parent_line_item) |
|
| 1300 | + { |
|
| 1301 | + return self::get_line_items_of_object_type( |
|
| 1302 | + $parent_line_item, |
|
| 1303 | + EEM_Line_Item::OBJ_TYPE_TICKET |
|
| 1304 | + ); |
|
| 1305 | + } |
|
| 1306 | + |
|
| 1307 | + |
|
| 1308 | + /** |
|
| 1309 | + * Gets all descendants subtotals that match the supplied object type |
|
| 1310 | + * |
|
| 1311 | + * @param EE_Line_Item $parent_line_item - the line item to find descendants of |
|
| 1312 | + * @param string $obj_type |
|
| 1313 | + * @return EE_Line_Item[] |
|
| 1314 | + * @throws EE_Error|ReflectionException |
|
| 1315 | + * @uses EEH_Line_Item::_get_descendants_by_type_and_object_type() |
|
| 1316 | + */ |
|
| 1317 | + public static function get_line_items_of_object_type(EE_Line_Item $parent_line_item, $obj_type = '') |
|
| 1318 | + { |
|
| 1319 | + return self::_get_descendants_by_type_and_object_type( |
|
| 1320 | + $parent_line_item, |
|
| 1321 | + EEM_Line_Item::type_line_item, |
|
| 1322 | + $obj_type |
|
| 1323 | + ); |
|
| 1324 | + } |
|
| 1325 | + |
|
| 1326 | + |
|
| 1327 | + /** |
|
| 1328 | + * Gets all the descendants (ie, children or children of children etc) that are of the type 'tax' |
|
| 1329 | + * |
|
| 1330 | + * @param EE_Line_Item $parent_line_item - the line item to find descendants of |
|
| 1331 | + * @return EE_Line_Item[] |
|
| 1332 | + * @throws EE_Error|ReflectionException |
|
| 1333 | + * @uses EEH_Line_Item::get_descendants_of_type() |
|
| 1334 | + */ |
|
| 1335 | + public static function get_tax_descendants(EE_Line_Item $parent_line_item) |
|
| 1336 | + { |
|
| 1337 | + return EEH_Line_Item::get_descendants_of_type( |
|
| 1338 | + $parent_line_item, |
|
| 1339 | + EEM_Line_Item::type_tax |
|
| 1340 | + ); |
|
| 1341 | + } |
|
| 1342 | + |
|
| 1343 | + |
|
| 1344 | + /** |
|
| 1345 | + * Gets all the real items purchased which are children of this item |
|
| 1346 | + * |
|
| 1347 | + * @param EE_Line_Item $parent_line_item - the line item to find descendants of |
|
| 1348 | + * @return EE_Line_Item[] |
|
| 1349 | + * @throws EE_Error|ReflectionException |
|
| 1350 | + * @uses EEH_Line_Item::get_descendants_of_type() |
|
| 1351 | + */ |
|
| 1352 | + public static function get_line_item_descendants(EE_Line_Item $parent_line_item) |
|
| 1353 | + { |
|
| 1354 | + return EEH_Line_Item::get_descendants_of_type( |
|
| 1355 | + $parent_line_item, |
|
| 1356 | + EEM_Line_Item::type_line_item |
|
| 1357 | + ); |
|
| 1358 | + } |
|
| 1359 | + |
|
| 1360 | + |
|
| 1361 | + /** |
|
| 1362 | + * Gets all descendants of supplied line item that match the supplied line item type |
|
| 1363 | + * |
|
| 1364 | + * @param EE_Line_Item $parent_line_item - the line item to find descendants of |
|
| 1365 | + * @param string $line_item_type one of the EEM_Line_Item constants |
|
| 1366 | + * @return EE_Line_Item[] |
|
| 1367 | + * @throws EE_Error|ReflectionException |
|
| 1368 | + * @uses EEH_Line_Item::_get_descendants_by_type_and_object_type() |
|
| 1369 | + */ |
|
| 1370 | + public static function get_descendants_of_type(EE_Line_Item $parent_line_item, $line_item_type) |
|
| 1371 | + { |
|
| 1372 | + return self::_get_descendants_by_type_and_object_type($parent_line_item, $line_item_type); |
|
| 1373 | + } |
|
| 1374 | + |
|
| 1375 | + |
|
| 1376 | + /** |
|
| 1377 | + * Gets all descendants of supplied line item that match the supplied line item type and possibly the object type |
|
| 1378 | + * as well |
|
| 1379 | + * |
|
| 1380 | + * @param EE_Line_Item $parent_line_item - the line item to find descendants of |
|
| 1381 | + * @param string $line_item_type one of the EEM_Line_Item constants |
|
| 1382 | + * @param string | NULL $obj_type object model class name (minus prefix) |
|
| 1383 | + * or NULL to ignore object type when searching |
|
| 1384 | + * @return EE_Line_Item[] |
|
| 1385 | + * @throws EE_Error|ReflectionException |
|
| 1386 | + */ |
|
| 1387 | + protected static function _get_descendants_by_type_and_object_type( |
|
| 1388 | + EE_Line_Item $parent_line_item, |
|
| 1389 | + $line_item_type, |
|
| 1390 | + $obj_type = null |
|
| 1391 | + ) { |
|
| 1392 | + $objects = []; |
|
| 1393 | + foreach ($parent_line_item->children() as $child_line_item) { |
|
| 1394 | + if ($child_line_item instanceof EE_Line_Item) { |
|
| 1395 | + if ( |
|
| 1396 | + $child_line_item->type() === $line_item_type |
|
| 1397 | + && ( |
|
| 1398 | + $child_line_item->OBJ_type() === $obj_type || $obj_type === null |
|
| 1399 | + ) |
|
| 1400 | + ) { |
|
| 1401 | + $objects[] = $child_line_item; |
|
| 1402 | + } else { |
|
| 1403 | + // go-through-all-its children looking for more matches |
|
| 1404 | + $objects = array_merge( |
|
| 1405 | + $objects, |
|
| 1406 | + self::_get_descendants_by_type_and_object_type( |
|
| 1407 | + $child_line_item, |
|
| 1408 | + $line_item_type, |
|
| 1409 | + $obj_type |
|
| 1410 | + ) |
|
| 1411 | + ); |
|
| 1412 | + } |
|
| 1413 | + } |
|
| 1414 | + } |
|
| 1415 | + return $objects; |
|
| 1416 | + } |
|
| 1417 | + |
|
| 1418 | + |
|
| 1419 | + /** |
|
| 1420 | + * Gets all descendants subtotals that match the supplied object type |
|
| 1421 | + * |
|
| 1422 | + * @param EE_Line_Item $parent_line_item - the line item to find descendants of |
|
| 1423 | + * @param string $OBJ_type object type (like Event) |
|
| 1424 | + * @param array $OBJ_IDs array of OBJ_IDs |
|
| 1425 | + * @return EE_Line_Item[] |
|
| 1426 | + * @throws EE_Error|ReflectionException |
|
| 1427 | + * @uses EEH_Line_Item::_get_descendants_by_type_and_object_type() |
|
| 1428 | + */ |
|
| 1429 | + public static function get_line_items_by_object_type_and_IDs( |
|
| 1430 | + EE_Line_Item $parent_line_item, |
|
| 1431 | + $OBJ_type = '', |
|
| 1432 | + $OBJ_IDs = [] |
|
| 1433 | + ) { |
|
| 1434 | + return self::_get_descendants_by_object_type_and_object_ID( |
|
| 1435 | + $parent_line_item, |
|
| 1436 | + $OBJ_type, |
|
| 1437 | + $OBJ_IDs |
|
| 1438 | + ); |
|
| 1439 | + } |
|
| 1440 | + |
|
| 1441 | + |
|
| 1442 | + /** |
|
| 1443 | + * Gets all descendants of supplied line item that match the supplied line item type and possibly the object type |
|
| 1444 | + * as well |
|
| 1445 | + * |
|
| 1446 | + * @param EE_Line_Item $parent_line_item - the line item to find descendants of |
|
| 1447 | + * @param string $OBJ_type object type (like Event) |
|
| 1448 | + * @param array $OBJ_IDs array of OBJ_IDs |
|
| 1449 | + * @return EE_Line_Item[] |
|
| 1450 | + * @throws EE_Error|ReflectionException |
|
| 1451 | + */ |
|
| 1452 | + protected static function _get_descendants_by_object_type_and_object_ID( |
|
| 1453 | + EE_Line_Item $parent_line_item, |
|
| 1454 | + $OBJ_type, |
|
| 1455 | + $OBJ_IDs |
|
| 1456 | + ) { |
|
| 1457 | + $objects = []; |
|
| 1458 | + foreach ($parent_line_item->children() as $child_line_item) { |
|
| 1459 | + if ($child_line_item instanceof EE_Line_Item) { |
|
| 1460 | + if ( |
|
| 1461 | + $child_line_item->OBJ_type() === $OBJ_type |
|
| 1462 | + && is_array($OBJ_IDs) |
|
| 1463 | + && in_array($child_line_item->OBJ_ID(), $OBJ_IDs) |
|
| 1464 | + ) { |
|
| 1465 | + $objects[] = $child_line_item; |
|
| 1466 | + } else { |
|
| 1467 | + // go-through-all-its children looking for more matches |
|
| 1468 | + $objects = array_merge( |
|
| 1469 | + $objects, |
|
| 1470 | + self::_get_descendants_by_object_type_and_object_ID( |
|
| 1471 | + $child_line_item, |
|
| 1472 | + $OBJ_type, |
|
| 1473 | + $OBJ_IDs |
|
| 1474 | + ) |
|
| 1475 | + ); |
|
| 1476 | + } |
|
| 1477 | + } |
|
| 1478 | + } |
|
| 1479 | + return $objects; |
|
| 1480 | + } |
|
| 1481 | + |
|
| 1482 | + |
|
| 1483 | + /** |
|
| 1484 | + * Uses a breadth-first-search in order to find the nearest descendant of |
|
| 1485 | + * the specified type and returns it, else NULL |
|
| 1486 | + * |
|
| 1487 | + * @param EE_Line_Item $parent_line_item - the line item to find descendants of |
|
| 1488 | + * @param string $type like one of the EEM_Line_Item::type_* |
|
| 1489 | + * @return EE_Line_Item |
|
| 1490 | + * @throws EE_Error |
|
| 1491 | + * @throws InvalidArgumentException |
|
| 1492 | + * @throws InvalidDataTypeException |
|
| 1493 | + * @throws InvalidInterfaceException |
|
| 1494 | + * @throws ReflectionException |
|
| 1495 | + * @uses EEH_Line_Item::_get_nearest_descendant() |
|
| 1496 | + */ |
|
| 1497 | + public static function get_nearest_descendant_of_type(EE_Line_Item $parent_line_item, $type) |
|
| 1498 | + { |
|
| 1499 | + return self::_get_nearest_descendant($parent_line_item, 'LIN_type', $type); |
|
| 1500 | + } |
|
| 1501 | + |
|
| 1502 | + |
|
| 1503 | + /** |
|
| 1504 | + * Uses a breadth-first-search in order to find the nearest descendant |
|
| 1505 | + * having the specified LIN_code and returns it, else NULL |
|
| 1506 | + * |
|
| 1507 | + * @param EE_Line_Item $parent_line_item - the line item to find descendants of |
|
| 1508 | + * @param string $code any value used for LIN_code |
|
| 1509 | + * @return EE_Line_Item |
|
| 1510 | + * @throws EE_Error |
|
| 1511 | + * @throws InvalidArgumentException |
|
| 1512 | + * @throws InvalidDataTypeException |
|
| 1513 | + * @throws InvalidInterfaceException |
|
| 1514 | + * @throws ReflectionException |
|
| 1515 | + * @uses EEH_Line_Item::_get_nearest_descendant() |
|
| 1516 | + */ |
|
| 1517 | + public static function get_nearest_descendant_having_code(EE_Line_Item $parent_line_item, $code) |
|
| 1518 | + { |
|
| 1519 | + return self::_get_nearest_descendant($parent_line_item, 'LIN_code', $code); |
|
| 1520 | + } |
|
| 1521 | + |
|
| 1522 | + |
|
| 1523 | + /** |
|
| 1524 | + * Uses a breadth-first-search in order to find the nearest descendant |
|
| 1525 | + * having the specified LIN_code and returns it, else NULL |
|
| 1526 | + * |
|
| 1527 | + * @param EE_Line_Item $parent_line_item - the line item to find descendants of |
|
| 1528 | + * @param string $search_field name of EE_Line_Item property |
|
| 1529 | + * @param string $value any value stored in $search_field |
|
| 1530 | + * @return EE_Line_Item |
|
| 1531 | + * @throws EE_Error |
|
| 1532 | + * @throws InvalidArgumentException |
|
| 1533 | + * @throws InvalidDataTypeException |
|
| 1534 | + * @throws InvalidInterfaceException |
|
| 1535 | + * @throws ReflectionException |
|
| 1536 | + */ |
|
| 1537 | + protected static function _get_nearest_descendant(EE_Line_Item $parent_line_item, $search_field, $value) |
|
| 1538 | + { |
|
| 1539 | + foreach ($parent_line_item->children() as $child) { |
|
| 1540 | + if ($child->get($search_field) == $value) { |
|
| 1541 | + return $child; |
|
| 1542 | + } |
|
| 1543 | + } |
|
| 1544 | + foreach ($parent_line_item->children() as $child) { |
|
| 1545 | + $descendant_found = self::_get_nearest_descendant( |
|
| 1546 | + $child, |
|
| 1547 | + $search_field, |
|
| 1548 | + $value |
|
| 1549 | + ); |
|
| 1550 | + if ($descendant_found) { |
|
| 1551 | + return $descendant_found; |
|
| 1552 | + } |
|
| 1553 | + } |
|
| 1554 | + return null; |
|
| 1555 | + } |
|
| 1556 | + |
|
| 1557 | + |
|
| 1558 | + /** |
|
| 1559 | + * if passed line item has a TXN ID, uses that to jump directly to the grand total line item for the transaction, |
|
| 1560 | + * else recursively walks up the line item tree until a parent of type total is found, |
|
| 1561 | + * |
|
| 1562 | + * @param EE_Line_Item $line_item |
|
| 1563 | + * @return EE_Line_Item |
|
| 1564 | + * @throws EE_Error|ReflectionException |
|
| 1565 | + */ |
|
| 1566 | + public static function find_transaction_grand_total_for_line_item(EE_Line_Item $line_item) |
|
| 1567 | + { |
|
| 1568 | + if ($line_item->TXN_ID()) { |
|
| 1569 | + $total_line_item = $line_item->transaction()->total_line_item(false); |
|
| 1570 | + if ($total_line_item instanceof EE_Line_Item) { |
|
| 1571 | + return $total_line_item; |
|
| 1572 | + } |
|
| 1573 | + } else { |
|
| 1574 | + $line_item_parent = $line_item->parent(); |
|
| 1575 | + if ($line_item_parent instanceof EE_Line_Item) { |
|
| 1576 | + if ($line_item_parent->is_total()) { |
|
| 1577 | + return $line_item_parent; |
|
| 1578 | + } |
|
| 1579 | + return EEH_Line_Item::find_transaction_grand_total_for_line_item($line_item_parent); |
|
| 1580 | + } |
|
| 1581 | + } |
|
| 1582 | + throw new EE_Error( |
|
| 1583 | + sprintf( |
|
| 1584 | + esc_html__( |
|
| 1585 | + 'A valid grand total for line item %1$d was not found.', |
|
| 1586 | + 'event_espresso' |
|
| 1587 | + ), |
|
| 1588 | + $line_item->ID() |
|
| 1589 | + ) |
|
| 1590 | + ); |
|
| 1591 | + } |
|
| 1592 | + |
|
| 1593 | + |
|
| 1594 | + /** |
|
| 1595 | + * Prints out a representation of the line item tree |
|
| 1596 | + * |
|
| 1597 | + * @param EE_Line_Item $line_item |
|
| 1598 | + * @param int $indentation |
|
| 1599 | + * @return void |
|
| 1600 | + * @throws EE_Error|ReflectionException |
|
| 1601 | + */ |
|
| 1602 | + public static function visualize(EE_Line_Item $line_item, $indentation = 0) |
|
| 1603 | + { |
|
| 1604 | + echo defined('EE_TESTS_DIR') ? "\n" : '<br />'; |
|
| 1605 | + if (! $indentation) { |
|
| 1606 | + echo defined('EE_TESTS_DIR') ? "\n" : '<br />'; |
|
| 1607 | + } |
|
| 1608 | + for ($i = 0; $i < $indentation; $i++) { |
|
| 1609 | + echo '. '; |
|
| 1610 | + } |
|
| 1611 | + $breakdown = ''; |
|
| 1612 | + if ($line_item->is_line_item()) { |
|
| 1613 | + if ($line_item->is_percent()) { |
|
| 1614 | + $breakdown = "{$line_item->percent()}%"; |
|
| 1615 | + } else { |
|
| 1616 | + $breakdown = '$' . "{$line_item->unit_price()} x {$line_item->quantity()}"; |
|
| 1617 | + } |
|
| 1618 | + } |
|
| 1619 | + echo wp_kses($line_item->name(), AllowedTags::getAllowedTags()); |
|
| 1620 | + echo " [ ID:{$line_item->ID()} | qty:{$line_item->quantity()} ] {$line_item->type()} : "; |
|
| 1621 | + echo '$' . (string) $line_item->total(); |
|
| 1622 | + if ($breakdown) { |
|
| 1623 | + echo " ( {$breakdown} )"; |
|
| 1624 | + } |
|
| 1625 | + if ($line_item->is_taxable()) { |
|
| 1626 | + echo ' * taxable'; |
|
| 1627 | + } |
|
| 1628 | + if ($line_item->children()) { |
|
| 1629 | + foreach ($line_item->children() as $child) { |
|
| 1630 | + self::visualize($child, $indentation + 1); |
|
| 1631 | + } |
|
| 1632 | + } |
|
| 1633 | + } |
|
| 1634 | + |
|
| 1635 | + |
|
| 1636 | + /** |
|
| 1637 | + * Calculates the registration's final price, taking into account that they |
|
| 1638 | + * need to not only help pay for their OWN ticket, but also any transaction-wide surcharges and taxes, |
|
| 1639 | + * and receive a portion of any transaction-wide discounts. |
|
| 1640 | + * eg1, if I buy a $1 ticket and brent buys a $9 ticket, and we receive a $5 discount |
|
| 1641 | + * then I'll get 1/10 of that $5 discount, which is $0.50, and brent will get |
|
| 1642 | + * 9/10ths of that $5 discount, which is $4.50. So my final price should be $0.50 |
|
| 1643 | + * and brent's final price should be $5.50. |
|
| 1644 | + * In order to do this, we basically need to traverse the line item tree calculating |
|
| 1645 | + * the running totals (just as if we were recalculating the total), but when we identify |
|
| 1646 | + * regular line items, we need to keep track of their share of the grand total. |
|
| 1647 | + * Also, we need to keep track of the TAXABLE total for each ticket purchase, so |
|
| 1648 | + * we can know how to apply taxes to it. (Note: "taxable total" does not equal the "pretax total" |
|
| 1649 | + * when there are non-taxable items; otherwise they would be the same) |
|
| 1650 | + * |
|
| 1651 | + * @param EE_Line_Item $line_item |
|
| 1652 | + * @param array $billable_ticket_quantities array of EE_Ticket IDs and their corresponding quantity |
|
| 1653 | + * that can be included in price calculations at this |
|
| 1654 | + * moment |
|
| 1655 | + * @return array keys are line items for tickets IDs and values are their share of the running total, |
|
| 1656 | + * plus the key 'total', and 'taxable' which also has keys |
|
| 1657 | + * of all the ticket IDs. Eg array( |
|
| 1658 | + * 12 => 4.3 |
|
| 1659 | + * 23 => 8.0 |
|
| 1660 | + * 'total' => 16.6, |
|
| 1661 | + * 'taxable' => array( |
|
| 1662 | + * 12 => 10, |
|
| 1663 | + * 23 => 4 |
|
| 1664 | + * ). |
|
| 1665 | + * So to find which registrations have which final price, |
|
| 1666 | + * we need to find which line item is theirs, which can be |
|
| 1667 | + * done with |
|
| 1668 | + * `EEM_Line_Item::instance()->get_line_item_for_registration( |
|
| 1669 | + * $registration );` |
|
| 1670 | + * @throws EE_Error |
|
| 1671 | + * @throws InvalidArgumentException |
|
| 1672 | + * @throws InvalidDataTypeException |
|
| 1673 | + * @throws InvalidInterfaceException |
|
| 1674 | + * @throws ReflectionException |
|
| 1675 | + */ |
|
| 1676 | + public static function calculate_reg_final_prices_per_line_item( |
|
| 1677 | + EE_Line_Item $line_item, |
|
| 1678 | + $billable_ticket_quantities = [] |
|
| 1679 | + ) { |
|
| 1680 | + $running_totals = [ |
|
| 1681 | + 'total' => 0, |
|
| 1682 | + 'taxable' => ['total' => 0], |
|
| 1683 | + ]; |
|
| 1684 | + foreach ($line_item->children() as $child_line_item) { |
|
| 1685 | + switch ($child_line_item->type()) { |
|
| 1686 | + case EEM_Line_Item::type_sub_total: |
|
| 1687 | + $running_totals_from_subtotal = EEH_Line_Item::calculate_reg_final_prices_per_line_item( |
|
| 1688 | + $child_line_item, |
|
| 1689 | + $billable_ticket_quantities |
|
| 1690 | + ); |
|
| 1691 | + // combine arrays but preserve numeric keys |
|
| 1692 | + $running_totals = |
|
| 1693 | + array_replace_recursive($running_totals_from_subtotal, $running_totals); |
|
| 1694 | + $running_totals['total'] += $running_totals_from_subtotal['total']; |
|
| 1695 | + $running_totals['taxable']['total'] += $running_totals_from_subtotal['taxable']['total']; |
|
| 1696 | + break; |
|
| 1697 | + |
|
| 1698 | + case EEM_Line_Item::type_tax_sub_total: |
|
| 1699 | + // find how much the taxes percentage is |
|
| 1700 | + if ($child_line_item->percent() !== 0) { |
|
| 1701 | + $tax_percent_decimal = $child_line_item->percent(true); |
|
| 1702 | + } else { |
|
| 1703 | + $tax_percent_decimal = |
|
| 1704 | + EEH_Line_Item::currencyFormatter()->roundForLocale( |
|
| 1705 | + EE_Taxes::get_total_taxes_percentage() / 100 |
|
| 1706 | + ); |
|
| 1707 | + } |
|
| 1708 | + // and apply to all the taxable totals, and add to the pretax totals |
|
| 1709 | + foreach ($running_totals as $line_item_id => $this_running_total) { |
|
| 1710 | + // "total" and "taxable" array key is an exception |
|
| 1711 | + if ($line_item_id === 'taxable') { |
|
| 1712 | + continue; |
|
| 1713 | + } |
|
| 1714 | + $taxable_total = $running_totals['taxable'][ $line_item_id ]; |
|
| 1715 | + $running_totals[ $line_item_id ] += EEH_Line_Item::currencyFormatter()->roundForLocale( |
|
| 1716 | + $taxable_total * $tax_percent_decimal |
|
| 1717 | + ); |
|
| 1718 | + } |
|
| 1719 | + break; |
|
| 1720 | + |
|
| 1721 | + case EEM_Line_Item::type_line_item: |
|
| 1722 | + // ticket line items or ???? |
|
| 1723 | + if ($child_line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET) { |
|
| 1724 | + // kk it's a ticket |
|
| 1725 | + if (isset($running_totals[ $child_line_item->ID() ])) { |
|
| 1726 | + // huh? that shouldn't happen. |
|
| 1727 | + $running_totals['total'] += $child_line_item->total(); |
|
| 1728 | + } else { |
|
| 1729 | + // its not in our running totals yet. great. |
|
| 1730 | + if ($child_line_item->is_taxable()) { |
|
| 1731 | + $taxable_amount = $child_line_item->unit_price(); |
|
| 1732 | + } else { |
|
| 1733 | + $taxable_amount = 0; |
|
| 1734 | + } |
|
| 1735 | + // are we only calculating totals for some tickets? |
|
| 1736 | + if (isset($billable_ticket_quantities[ $child_line_item->OBJ_ID() ])) { |
|
| 1737 | + $quantity = |
|
| 1738 | + $billable_ticket_quantities[ $child_line_item->OBJ_ID() ]; |
|
| 1739 | + $running_totals[ $child_line_item->ID() ] = $quantity |
|
| 1740 | + ? $child_line_item->unit_price() |
|
| 1741 | + : 0; |
|
| 1742 | + $running_totals['taxable'][ $child_line_item->ID() ] = $quantity |
|
| 1743 | + ? $taxable_amount |
|
| 1744 | + : 0; |
|
| 1745 | + } else { |
|
| 1746 | + $quantity = $child_line_item->quantity(); |
|
| 1747 | + $running_totals[ $child_line_item->ID() ] = $child_line_item->unit_price(); |
|
| 1748 | + $running_totals['taxable'][ $child_line_item->ID() ] = $taxable_amount; |
|
| 1749 | + } |
|
| 1750 | + $running_totals['taxable']['total'] += $taxable_amount * $quantity; |
|
| 1751 | + $running_totals['total'] += $child_line_item->unit_price() * $quantity; |
|
| 1752 | + } |
|
| 1753 | + } else { |
|
| 1754 | + // it's some other type of item added to the cart |
|
| 1755 | + // it should affect the running totals |
|
| 1756 | + // basically we want to convert it into a PERCENT modifier. Because |
|
| 1757 | + // more clearly affect all registration's final price equally |
|
| 1758 | + $line_items_percent_of_running_total = $running_totals['total'] > 0 |
|
| 1759 | + ? EEH_Line_Item::currencyFormatter()->roundForLocale( |
|
| 1760 | + $child_line_item->total() / $running_totals['total'] |
|
| 1761 | + ) + 1 |
|
| 1762 | + : 1; |
|
| 1763 | + foreach ($running_totals as $line_item_id => $this_running_total) { |
|
| 1764 | + // the "taxable" array key is an exception |
|
| 1765 | + if ($line_item_id === 'taxable') { |
|
| 1766 | + continue; |
|
| 1767 | + } |
|
| 1768 | + // update the running totals |
|
| 1769 | + // yes this actually even works for the running grand total! |
|
| 1770 | + $running_totals[ $line_item_id ] = EEH_Line_Item::currencyFormatter()->roundForLocale( |
|
| 1771 | + $line_items_percent_of_running_total * $this_running_total |
|
| 1772 | + ); |
|
| 1773 | + |
|
| 1774 | + if ($child_line_item->is_taxable()) { |
|
| 1775 | + $running_totals['taxable'][ $line_item_id ] = |
|
| 1776 | + EEH_Line_Item::currencyFormatter()->roundForLocale( |
|
| 1777 | + $line_items_percent_of_running_total * $running_totals['taxable'][ $line_item_id ] |
|
| 1778 | + ); |
|
| 1779 | + } |
|
| 1780 | + } |
|
| 1781 | + } |
|
| 1782 | + break; |
|
| 1783 | + } |
|
| 1784 | + } |
|
| 1785 | + return $running_totals; |
|
| 1786 | + } |
|
| 1787 | + |
|
| 1788 | + |
|
| 1789 | + /** |
|
| 1790 | + * @param EE_Line_Item $total_line_item |
|
| 1791 | + * @param EE_Line_Item $ticket_line_item |
|
| 1792 | + * @return float | null |
|
| 1793 | + * @throws EE_Error |
|
| 1794 | + * @throws InvalidArgumentException |
|
| 1795 | + * @throws InvalidDataTypeException |
|
| 1796 | + * @throws InvalidInterfaceException |
|
| 1797 | + * @throws OutOfRangeException |
|
| 1798 | + * @throws ReflectionException |
|
| 1799 | + */ |
|
| 1800 | + public static function calculate_final_price_for_ticket_line_item( |
|
| 1801 | + EE_Line_Item $total_line_item, |
|
| 1802 | + EE_Line_Item $ticket_line_item |
|
| 1803 | + ) { |
|
| 1804 | + static $final_prices_per_ticket_line_item = []; |
|
| 1805 | + if (empty($final_prices_per_ticket_line_item[ $total_line_item->ID() ])) { |
|
| 1806 | + $final_prices_per_ticket_line_item[ $total_line_item->ID() ] = |
|
| 1807 | + EEH_Line_Item::calculate_reg_final_prices_per_line_item( |
|
| 1808 | + $total_line_item |
|
| 1809 | + ); |
|
| 1810 | + } |
|
| 1811 | + // ok now find this new registration's final price |
|
| 1812 | + if (isset($final_prices_per_ticket_line_item[ $total_line_item->ID() ][ $ticket_line_item->ID() ])) { |
|
| 1813 | + return $final_prices_per_ticket_line_item[ $total_line_item->ID() ][ $ticket_line_item->ID() ]; |
|
| 1814 | + } |
|
| 1815 | + $message = sprintf( |
|
| 1816 | + esc_html__( |
|
| 1817 | + 'The final price for the ticket line item (ID:%1$d) could not be calculated.', |
|
| 1818 | + 'event_espresso' |
|
| 1819 | + ), |
|
| 1820 | + $ticket_line_item->ID() |
|
| 1821 | + ); |
|
| 1822 | + if (WP_DEBUG) { |
|
| 1823 | + $message .= '<br>' . print_r($final_prices_per_ticket_line_item, true); |
|
| 1824 | + throw new OutOfRangeException($message); |
|
| 1825 | + } |
|
| 1826 | + EE_Log::instance()->log(__CLASS__, __FUNCTION__, $message); |
|
| 1827 | + return null; |
|
| 1828 | + } |
|
| 1829 | + |
|
| 1830 | + |
|
| 1831 | + /** |
|
| 1832 | + * Creates a duplicate of the line item tree, except only includes billable items |
|
| 1833 | + * and the portion of line items attributed to billable things |
|
| 1834 | + * |
|
| 1835 | + * @param EE_Line_Item $line_item |
|
| 1836 | + * @param EE_Registration[] $registrations |
|
| 1837 | + * @return EE_Line_Item |
|
| 1838 | + * @throws EE_Error |
|
| 1839 | + * @throws InvalidArgumentException |
|
| 1840 | + * @throws InvalidDataTypeException |
|
| 1841 | + * @throws InvalidInterfaceException |
|
| 1842 | + * @throws ReflectionException |
|
| 1843 | + */ |
|
| 1844 | + public static function billable_line_item_tree(EE_Line_Item $line_item, $registrations) |
|
| 1845 | + { |
|
| 1846 | + $copy_li = EEH_Line_Item::billable_line_item($line_item, $registrations); |
|
| 1847 | + foreach ($line_item->children() as $child_li) { |
|
| 1848 | + $copy_li->add_child_line_item( |
|
| 1849 | + EEH_Line_Item::billable_line_item_tree($child_li, $registrations) |
|
| 1850 | + ); |
|
| 1851 | + } |
|
| 1852 | + // if this is the grand total line item, make sure the totals all add up |
|
| 1853 | + // (we could have duplicated this logic AS we copied the line items, but |
|
| 1854 | + // it seems DRYer this way) |
|
| 1855 | + if ($copy_li->type() === EEM_Line_Item::type_total) { |
|
| 1856 | + $copy_li->recalculate_total_including_taxes(); |
|
| 1857 | + } |
|
| 1858 | + return $copy_li; |
|
| 1859 | + } |
|
| 1860 | + |
|
| 1861 | + |
|
| 1862 | + /** |
|
| 1863 | + * Creates a new, unsaved line item from $line_item that factors in the |
|
| 1864 | + * number of billable registrations on $registrations. |
|
| 1865 | + * |
|
| 1866 | + * @param EE_Line_Item $line_item |
|
| 1867 | + * @param EE_Registration[] $registrations |
|
| 1868 | + * @return EE_Line_Item |
|
| 1869 | + * @throws EE_Error |
|
| 1870 | + * @throws InvalidArgumentException |
|
| 1871 | + * @throws InvalidDataTypeException |
|
| 1872 | + * @throws InvalidInterfaceException |
|
| 1873 | + * @throws ReflectionException |
|
| 1874 | + */ |
|
| 1875 | + public static function billable_line_item(EE_Line_Item $line_item, $registrations) |
|
| 1876 | + { |
|
| 1877 | + $new_li_fields = $line_item->model_field_array(); |
|
| 1878 | + if ( |
|
| 1879 | + $line_item->type() === EEM_Line_Item::type_line_item |
|
| 1880 | + && $line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET |
|
| 1881 | + ) { |
|
| 1882 | + $count = 0; |
|
| 1883 | + foreach ($registrations as $registration) { |
|
| 1884 | + if ( |
|
| 1885 | + $line_item->OBJ_ID() === $registration->ticket_ID() && |
|
| 1886 | + in_array( |
|
| 1887 | + $registration->status_ID(), |
|
| 1888 | + EEM_Registration::reg_statuses_that_allow_payment(), |
|
| 1889 | + true |
|
| 1890 | + ) |
|
| 1891 | + ) { |
|
| 1892 | + $count++; |
|
| 1893 | + } |
|
| 1894 | + } |
|
| 1895 | + $new_li_fields['LIN_quantity'] = $count; |
|
| 1896 | + } |
|
| 1897 | + // don't set the total. We'll leave that up to the code that calculates it |
|
| 1898 | + unset($new_li_fields['LIN_ID'], $new_li_fields['LIN_parent'], $new_li_fields['LIN_total']); |
|
| 1899 | + return EE_Line_Item::new_instance($new_li_fields); |
|
| 1900 | + } |
|
| 1901 | + |
|
| 1902 | + |
|
| 1903 | + /** |
|
| 1904 | + * Returns a modified line item tree where all the subtotals which have a total of 0 |
|
| 1905 | + * are removed, and line items with a quantity of 0 |
|
| 1906 | + * |
|
| 1907 | + * @param EE_Line_Item $line_item |null |
|
| 1908 | + * @return EE_Line_Item|null |
|
| 1909 | + * @throws EE_Error |
|
| 1910 | + * @throws InvalidArgumentException |
|
| 1911 | + * @throws InvalidDataTypeException |
|
| 1912 | + * @throws InvalidInterfaceException |
|
| 1913 | + * @throws ReflectionException |
|
| 1914 | + */ |
|
| 1915 | + public static function non_empty_line_items(EE_Line_Item $line_item) |
|
| 1916 | + { |
|
| 1917 | + $copied_li = EEH_Line_Item::non_empty_line_item($line_item); |
|
| 1918 | + if ($copied_li === null) { |
|
| 1919 | + return null; |
|
| 1920 | + } |
|
| 1921 | + // if this is an event subtotal, we want to only include it if it |
|
| 1922 | + // has a non-zero total and at least one ticket line item child |
|
| 1923 | + $ticket_children = 0; |
|
| 1924 | + foreach ($line_item->children() as $child_li) { |
|
| 1925 | + $child_li_copy = EEH_Line_Item::non_empty_line_items($child_li); |
|
| 1926 | + if ($child_li_copy !== null) { |
|
| 1927 | + $copied_li->add_child_line_item($child_li_copy); |
|
| 1928 | + if ( |
|
| 1929 | + $child_li_copy->type() === EEM_Line_Item::type_line_item |
|
| 1930 | + && $child_li_copy->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET |
|
| 1931 | + ) { |
|
| 1932 | + $ticket_children++; |
|
| 1933 | + } |
|
| 1934 | + } |
|
| 1935 | + } |
|
| 1936 | + // if this is an event subtotal with NO ticket children |
|
| 1937 | + // we basically want to ignore it |
|
| 1938 | + if ( |
|
| 1939 | + $ticket_children === 0 |
|
| 1940 | + && $line_item->type() === EEM_Line_Item::type_sub_total |
|
| 1941 | + && $line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_EVENT |
|
| 1942 | + && $line_item->total() === 0 |
|
| 1943 | + ) { |
|
| 1944 | + return null; |
|
| 1945 | + } |
|
| 1946 | + return $copied_li; |
|
| 1947 | + } |
|
| 1948 | + |
|
| 1949 | + |
|
| 1950 | + /** |
|
| 1951 | + * Creates a new, unsaved line item, but if it's a ticket line item |
|
| 1952 | + * with a total of 0, or a subtotal of 0, returns null instead |
|
| 1953 | + * |
|
| 1954 | + * @param EE_Line_Item $line_item |
|
| 1955 | + * @return EE_Line_Item |
|
| 1956 | + * @throws EE_Error |
|
| 1957 | + * @throws InvalidArgumentException |
|
| 1958 | + * @throws InvalidDataTypeException |
|
| 1959 | + * @throws InvalidInterfaceException |
|
| 1960 | + * @throws ReflectionException |
|
| 1961 | + */ |
|
| 1962 | + public static function non_empty_line_item(EE_Line_Item $line_item) |
|
| 1963 | + { |
|
| 1964 | + if ( |
|
| 1965 | + $line_item->type() === EEM_Line_Item::type_line_item |
|
| 1966 | + && $line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET |
|
| 1967 | + && $line_item->quantity() === 0 |
|
| 1968 | + ) { |
|
| 1969 | + return null; |
|
| 1970 | + } |
|
| 1971 | + $new_li_fields = $line_item->model_field_array(); |
|
| 1972 | + // don't set the total. We'll leave that up to the code that calculates it |
|
| 1973 | + unset($new_li_fields['LIN_ID'], $new_li_fields['LIN_parent']); |
|
| 1974 | + return EE_Line_Item::new_instance($new_li_fields); |
|
| 1975 | + } |
|
| 1976 | + |
|
| 1977 | + |
|
| 1978 | + /** |
|
| 1979 | + * Cycles through all of the ticket line items for the supplied total line item |
|
| 1980 | + * and ensures that the line item's "is_taxable" field matches that of its corresponding ticket |
|
| 1981 | + * |
|
| 1982 | + * @param EE_Line_Item $total_line_item |
|
| 1983 | + * @throws EE_Error |
|
| 1984 | + * @throws InvalidArgumentException |
|
| 1985 | + * @throws InvalidDataTypeException |
|
| 1986 | + * @throws InvalidInterfaceException |
|
| 1987 | + * @throws ReflectionException |
|
| 1988 | + * @since 4.9.79.p |
|
| 1989 | + */ |
|
| 1990 | + public static function resetIsTaxableForTickets(EE_Line_Item $total_line_item) |
|
| 1991 | + { |
|
| 1992 | + $ticket_line_items = self::get_ticket_line_items($total_line_item); |
|
| 1993 | + foreach ($ticket_line_items as $ticket_line_item) { |
|
| 1994 | + if ( |
|
| 1995 | + $ticket_line_item instanceof EE_Line_Item |
|
| 1996 | + && $ticket_line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET |
|
| 1997 | + ) { |
|
| 1998 | + $ticket = $ticket_line_item->ticket(); |
|
| 1999 | + if ($ticket instanceof EE_Ticket && $ticket->taxable() !== $ticket_line_item->is_taxable()) { |
|
| 2000 | + $ticket_line_item->set_is_taxable($ticket->taxable()); |
|
| 2001 | + $ticket_line_item->save(); |
|
| 2002 | + } |
|
| 2003 | + } |
|
| 2004 | + } |
|
| 2005 | + } |
|
| 2006 | + |
|
| 2007 | + |
|
| 2008 | + |
|
| 2009 | + /**************************************** @DEPRECATED METHODS *************************************** */ |
|
| 2010 | + /** |
|
| 2011 | + * @param EE_Line_Item $total_line_item |
|
| 2012 | + * @return EE_Line_Item |
|
| 2013 | + * @throws EE_Error |
|
| 2014 | + * @throws InvalidArgumentException |
|
| 2015 | + * @throws InvalidDataTypeException |
|
| 2016 | + * @throws InvalidInterfaceException |
|
| 2017 | + * @throws ReflectionException |
|
| 2018 | + * @deprecated |
|
| 2019 | + */ |
|
| 2020 | + public static function get_items_subtotal(EE_Line_Item $total_line_item) |
|
| 2021 | + { |
|
| 2022 | + EE_Error::doing_it_wrong( |
|
| 2023 | + 'EEH_Line_Item::get_items_subtotal()', |
|
| 2024 | + sprintf( |
|
| 2025 | + esc_html__('Method replaced with %1$s', 'event_espresso'), |
|
| 2026 | + 'EEH_Line_Item::get_pre_tax_subtotal()' |
|
| 2027 | + ), |
|
| 2028 | + '4.6.0' |
|
| 2029 | + ); |
|
| 2030 | + return self::get_pre_tax_subtotal($total_line_item); |
|
| 2031 | + } |
|
| 2032 | + |
|
| 2033 | + |
|
| 2034 | + /** |
|
| 2035 | + * @param EE_Transaction $transaction |
|
| 2036 | + * @return EE_Line_Item |
|
| 2037 | + * @throws EE_Error |
|
| 2038 | + * @throws InvalidArgumentException |
|
| 2039 | + * @throws InvalidDataTypeException |
|
| 2040 | + * @throws InvalidInterfaceException |
|
| 2041 | + * @throws ReflectionException |
|
| 2042 | + * @deprecated |
|
| 2043 | + */ |
|
| 2044 | + public static function create_default_total_line_item($transaction = null) |
|
| 2045 | + { |
|
| 2046 | + EE_Error::doing_it_wrong( |
|
| 2047 | + 'EEH_Line_Item::create_default_total_line_item()', |
|
| 2048 | + sprintf( |
|
| 2049 | + esc_html__('Method replaced with %1$s', 'event_espresso'), |
|
| 2050 | + 'EEH_Line_Item::create_total_line_item()' |
|
| 2051 | + ), |
|
| 2052 | + '4.6.0' |
|
| 2053 | + ); |
|
| 2054 | + return self::create_total_line_item($transaction); |
|
| 2055 | + } |
|
| 2056 | + |
|
| 2057 | + |
|
| 2058 | + /** |
|
| 2059 | + * @param EE_Line_Item $total_line_item |
|
| 2060 | + * @param EE_Transaction $transaction |
|
| 2061 | + * @return EE_Line_Item |
|
| 2062 | + * @throws EE_Error |
|
| 2063 | + * @throws InvalidArgumentException |
|
| 2064 | + * @throws InvalidDataTypeException |
|
| 2065 | + * @throws InvalidInterfaceException |
|
| 2066 | + * @throws ReflectionException |
|
| 2067 | + * @deprecated |
|
| 2068 | + */ |
|
| 2069 | + public static function create_default_tickets_subtotal(EE_Line_Item $total_line_item, $transaction = null) |
|
| 2070 | + { |
|
| 2071 | + EE_Error::doing_it_wrong( |
|
| 2072 | + 'EEH_Line_Item::create_default_tickets_subtotal()', |
|
| 2073 | + sprintf( |
|
| 2074 | + esc_html__('Method replaced with %1$s', 'event_espresso'), |
|
| 2075 | + 'EEH_Line_Item::create_pre_tax_subtotal()' |
|
| 2076 | + ), |
|
| 2077 | + '4.6.0' |
|
| 2078 | + ); |
|
| 2079 | + return self::create_pre_tax_subtotal($total_line_item, $transaction); |
|
| 2080 | + } |
|
| 2081 | + |
|
| 2082 | + |
|
| 2083 | + /** |
|
| 2084 | + * @param EE_Line_Item $total_line_item |
|
| 2085 | + * @param EE_Transaction $transaction |
|
| 2086 | + * @return EE_Line_Item |
|
| 2087 | + * @throws EE_Error |
|
| 2088 | + * @throws InvalidArgumentException |
|
| 2089 | + * @throws InvalidDataTypeException |
|
| 2090 | + * @throws InvalidInterfaceException |
|
| 2091 | + * @throws ReflectionException |
|
| 2092 | + * @deprecated |
|
| 2093 | + */ |
|
| 2094 | + public static function create_default_taxes_subtotal(EE_Line_Item $total_line_item, $transaction = null) |
|
| 2095 | + { |
|
| 2096 | + EE_Error::doing_it_wrong( |
|
| 2097 | + 'EEH_Line_Item::create_default_taxes_subtotal()', |
|
| 2098 | + sprintf( |
|
| 2099 | + esc_html__('Method replaced with %1$s', 'event_espresso'), |
|
| 2100 | + 'EEH_Line_Item::create_taxes_subtotal()' |
|
| 2101 | + ), |
|
| 2102 | + '4.6.0' |
|
| 2103 | + ); |
|
| 2104 | + return self::create_taxes_subtotal($total_line_item, $transaction); |
|
| 2105 | + } |
|
| 2106 | + |
|
| 2107 | + |
|
| 2108 | + /** |
|
| 2109 | + * @param EE_Line_Item $total_line_item |
|
| 2110 | + * @param EE_Transaction $transaction |
|
| 2111 | + * @return EE_Line_Item |
|
| 2112 | + * @throws EE_Error |
|
| 2113 | + * @throws InvalidArgumentException |
|
| 2114 | + * @throws InvalidDataTypeException |
|
| 2115 | + * @throws InvalidInterfaceException |
|
| 2116 | + * @throws ReflectionException |
|
| 2117 | + * @deprecated |
|
| 2118 | + */ |
|
| 2119 | + public static function create_default_event_subtotal(EE_Line_Item $total_line_item, $transaction = null) |
|
| 2120 | + { |
|
| 2121 | + EE_Error::doing_it_wrong( |
|
| 2122 | + 'EEH_Line_Item::create_default_event_subtotal()', |
|
| 2123 | + sprintf( |
|
| 2124 | + esc_html__('Method replaced with %1$s', 'event_espresso'), |
|
| 2125 | + 'EEH_Line_Item::create_event_subtotal()' |
|
| 2126 | + ), |
|
| 2127 | + '4.6.0' |
|
| 2128 | + ); |
|
| 2129 | + return self::create_event_subtotal($total_line_item, $transaction); |
|
| 2130 | + } |
|
| 2131 | 2131 | } |
@@ -30,7 +30,7 @@ discard block |
||
| 30 | 30 | private static function currencyFormatter() |
| 31 | 31 | { |
| 32 | 32 | static $currency_formatter; |
| 33 | - if (! $currency_formatter instanceof CurrencyFormatter) { |
|
| 33 | + if ( ! $currency_formatter instanceof CurrencyFormatter) { |
|
| 34 | 34 | $currency_formatter = LoaderFactory::getLoader()->getShared(CurrencyFormatter::class); |
| 35 | 35 | } |
| 36 | 36 | return $currency_formatter; |
@@ -83,7 +83,7 @@ discard block |
||
| 83 | 83 | 'LIN_code' => $code, |
| 84 | 84 | ] |
| 85 | 85 | ); |
| 86 | - $line_item = apply_filters( |
|
| 86 | + $line_item = apply_filters( |
|
| 87 | 87 | 'FHEE__EEH_Line_Item__add_unrelated_item__line_item', |
| 88 | 88 | $line_item, |
| 89 | 89 | $parent_line_item |
@@ -161,7 +161,7 @@ discard block |
||
| 161 | 161 | */ |
| 162 | 162 | public static function add_ticket_purchase(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1) |
| 163 | 163 | { |
| 164 | - if (! $total_line_item instanceof EE_Line_Item || ! $total_line_item->is_total()) { |
|
| 164 | + if ( ! $total_line_item instanceof EE_Line_Item || ! $total_line_item->is_total()) { |
|
| 165 | 165 | throw new EE_Error( |
| 166 | 166 | sprintf( |
| 167 | 167 | esc_html__( |
@@ -176,7 +176,7 @@ discard block |
||
| 176 | 176 | // either increment the qty for an existing ticket |
| 177 | 177 | $line_item = self::increment_ticket_qty_if_already_in_cart($total_line_item, $ticket, $qty); |
| 178 | 178 | // or add a new one |
| 179 | - if (! $line_item instanceof EE_Line_Item) { |
|
| 179 | + if ( ! $line_item instanceof EE_Line_Item) { |
|
| 180 | 180 | $line_item = self::create_ticket_line_item($total_line_item, $ticket, $qty); |
| 181 | 181 | } |
| 182 | 182 | $total_line_item->recalculate_total_including_taxes(); |
@@ -238,7 +238,7 @@ discard block |
||
| 238 | 238 | */ |
| 239 | 239 | public static function increment_quantity(EE_Line_Item $line_item, $qty = 1) |
| 240 | 240 | { |
| 241 | - if (! $line_item->is_percent()) { |
|
| 241 | + if ( ! $line_item->is_percent()) { |
|
| 242 | 242 | $qty += $line_item->quantity(); |
| 243 | 243 | $line_item->set_quantity($qty); |
| 244 | 244 | $line_item->set_total($line_item->unit_price() * $qty); |
@@ -267,7 +267,7 @@ discard block |
||
| 267 | 267 | */ |
| 268 | 268 | public static function decrement_quantity(EE_Line_Item $line_item, $qty = 1) |
| 269 | 269 | { |
| 270 | - if (! $line_item->is_percent()) { |
|
| 270 | + if ( ! $line_item->is_percent()) { |
|
| 271 | 271 | $qty = $line_item->quantity() - $qty; |
| 272 | 272 | $qty = max($qty, 0); |
| 273 | 273 | $line_item->set_quantity($qty); |
@@ -296,7 +296,7 @@ discard block |
||
| 296 | 296 | */ |
| 297 | 297 | public static function update_quantity(EE_Line_Item $line_item, $new_quantity) |
| 298 | 298 | { |
| 299 | - if (! $line_item->is_percent()) { |
|
| 299 | + if ( ! $line_item->is_percent()) { |
|
| 300 | 300 | $line_item->set_quantity($new_quantity); |
| 301 | 301 | $line_item->set_total($line_item->unit_price() * $new_quantity); |
| 302 | 302 | $line_item->save(); |
@@ -337,7 +337,7 @@ discard block |
||
| 337 | 337 | $line_item = EE_Line_Item::new_instance( |
| 338 | 338 | [ |
| 339 | 339 | 'LIN_name' => $ticket->name(), |
| 340 | - 'LIN_desc' => $ticket->description() !== '' ? $ticket->description() . ' ' . $event : $event, |
|
| 340 | + 'LIN_desc' => $ticket->description() !== '' ? $ticket->description().' '.$event : $event, |
|
| 341 | 341 | 'LIN_unit_price' => $ticket->price(), |
| 342 | 342 | 'LIN_quantity' => $qty, |
| 343 | 343 | 'LIN_is_taxable' => $ticket->taxable(), |
@@ -494,7 +494,7 @@ discard block |
||
| 494 | 494 | 'event_espresso' |
| 495 | 495 | ), |
| 496 | 496 | $ticket_line_item->name(), |
| 497 | - current_time(get_option('date_format') . ' ' . get_option('time_format')) |
|
| 497 | + current_time(get_option('date_format').' '.get_option('time_format')) |
|
| 498 | 498 | ), |
| 499 | 499 | 'LIN_unit_price' => 0, // $ticket_line_item->unit_price() |
| 500 | 500 | 'LIN_quantity' => $qty, |
@@ -558,7 +558,7 @@ discard block |
||
| 558 | 558 | ); |
| 559 | 559 | $cancellation_line_item = reset($cancellation_line_item); |
| 560 | 560 | // verify that this ticket was indeed previously cancelled |
| 561 | - if (! $cancellation_line_item instanceof EE_Line_Item) { |
|
| 561 | + if ( ! $cancellation_line_item instanceof EE_Line_Item) { |
|
| 562 | 562 | return false; |
| 563 | 563 | } |
| 564 | 564 | if ($cancellation_line_item->quantity() > $qty) { |
@@ -769,7 +769,7 @@ discard block |
||
| 769 | 769 | 'LIN_code' => 'taxes', |
| 770 | 770 | 'LIN_name' => esc_html__('Taxes', 'event_espresso'), |
| 771 | 771 | 'LIN_type' => EEM_Line_Item::type_tax_sub_total, |
| 772 | - 'LIN_order' => 1000,// this should always come last |
|
| 772 | + 'LIN_order' => 1000, // this should always come last |
|
| 773 | 773 | ] |
| 774 | 774 | ); |
| 775 | 775 | $tax_line_item = apply_filters( |
@@ -828,7 +828,7 @@ discard block |
||
| 828 | 828 | */ |
| 829 | 829 | public static function get_event_code($event) |
| 830 | 830 | { |
| 831 | - return 'event-' . ($event instanceof EE_Event ? $event->ID() : '0'); |
|
| 831 | + return 'event-'.($event instanceof EE_Event ? $event->ID() : '0'); |
|
| 832 | 832 | } |
| 833 | 833 | |
| 834 | 834 | |
@@ -877,7 +877,7 @@ discard block |
||
| 877 | 877 | public static function get_event_line_item_for_ticket(EE_Line_Item $grand_total, EE_Ticket $ticket) |
| 878 | 878 | { |
| 879 | 879 | $first_datetime = $ticket->first_datetime(); |
| 880 | - if (! $first_datetime instanceof EE_Datetime) { |
|
| 880 | + if ( ! $first_datetime instanceof EE_Datetime) { |
|
| 881 | 881 | throw new EE_Error( |
| 882 | 882 | sprintf( |
| 883 | 883 | esc_html__('The supplied ticket (ID %d) has no datetimes', 'event_espresso'), |
@@ -886,7 +886,7 @@ discard block |
||
| 886 | 886 | ); |
| 887 | 887 | } |
| 888 | 888 | $event = $first_datetime->event(); |
| 889 | - if (! $event instanceof EE_Event) { |
|
| 889 | + if ( ! $event instanceof EE_Event) { |
|
| 890 | 890 | throw new EE_Error( |
| 891 | 891 | sprintf( |
| 892 | 892 | esc_html__( |
@@ -898,7 +898,7 @@ discard block |
||
| 898 | 898 | ); |
| 899 | 899 | } |
| 900 | 900 | $events_sub_total = EEH_Line_Item::get_event_line_item($grand_total, $event); |
| 901 | - if (! $events_sub_total instanceof EE_Line_Item) { |
|
| 901 | + if ( ! $events_sub_total instanceof EE_Line_Item) { |
|
| 902 | 902 | throw new EE_Error( |
| 903 | 903 | sprintf( |
| 904 | 904 | esc_html__( |
@@ -934,7 +934,7 @@ discard block |
||
| 934 | 934 | $found = false; |
| 935 | 935 | foreach (EEH_Line_Item::get_event_subtotals($grand_total) as $event_line_item) { |
| 936 | 936 | // default event subtotal, we should only ever find this the first time this method is called |
| 937 | - if (! $event_line_item->OBJ_ID()) { |
|
| 937 | + if ( ! $event_line_item->OBJ_ID()) { |
|
| 938 | 938 | // let's use this! but first... set the event details |
| 939 | 939 | EEH_Line_Item::set_event_subtotal_details($event_line_item, $event); |
| 940 | 940 | $found = true; |
@@ -946,7 +946,7 @@ discard block |
||
| 946 | 946 | break; |
| 947 | 947 | } |
| 948 | 948 | } |
| 949 | - if (! $found) { |
|
| 949 | + if ( ! $found) { |
|
| 950 | 950 | // there is no event sub-total yet, so add it |
| 951 | 951 | $pre_tax_subtotal = EEH_Line_Item::get_pre_tax_subtotal($grand_total); |
| 952 | 952 | // create a new "event" subtotal below that |
@@ -1033,7 +1033,7 @@ discard block |
||
| 1033 | 1033 | 'FHEE__EEH_Line_Item__apply_taxes__tax_line_item', |
| 1034 | 1034 | $tax_line_item |
| 1035 | 1035 | ); |
| 1036 | - $updates = $taxes_line_item->add_child_line_item($tax_line_item) |
|
| 1036 | + $updates = $taxes_line_item->add_child_line_item($tax_line_item) |
|
| 1037 | 1037 | ? |
| 1038 | 1038 | true |
| 1039 | 1039 | : |
@@ -1065,7 +1065,7 @@ discard block |
||
| 1065 | 1065 | public static function ensure_taxes_applied($total_line_item) |
| 1066 | 1066 | { |
| 1067 | 1067 | $taxes_subtotal = self::get_taxes_subtotal($total_line_item); |
| 1068 | - if (! $taxes_subtotal->children()) { |
|
| 1068 | + if ( ! $taxes_subtotal->children()) { |
|
| 1069 | 1069 | self::apply_taxes($total_line_item); |
| 1070 | 1070 | } |
| 1071 | 1071 | return $taxes_subtotal->total(); |
@@ -1131,7 +1131,7 @@ discard block |
||
| 1131 | 1131 | do_action('AHEE_log', __FILE__, __FUNCTION__, ''); |
| 1132 | 1132 | |
| 1133 | 1133 | // check if only a single line_item_id was passed |
| 1134 | - if (! empty($line_item_codes) && ! is_array($line_item_codes)) { |
|
| 1134 | + if ( ! empty($line_item_codes) && ! is_array($line_item_codes)) { |
|
| 1135 | 1135 | // place single line_item_id in an array to appear as multiple line_item_ids |
| 1136 | 1136 | $line_item_codes = [$line_item_codes]; |
| 1137 | 1137 | } |
@@ -1242,7 +1242,7 @@ discard block |
||
| 1242 | 1242 | if ($code_substring_for_whitelist !== null) { |
| 1243 | 1243 | $whitelisted = strpos($line_item->code(), $code_substring_for_whitelist) !== false; |
| 1244 | 1244 | } |
| 1245 | - if (! $whitelisted && $line_item->is_line_item()) { |
|
| 1245 | + if ( ! $whitelisted && $line_item->is_line_item()) { |
|
| 1246 | 1246 | $line_item->set_is_taxable($taxable); |
| 1247 | 1247 | } |
| 1248 | 1248 | foreach ($line_item->children() as $child_line_item) { |
@@ -1602,7 +1602,7 @@ discard block |
||
| 1602 | 1602 | public static function visualize(EE_Line_Item $line_item, $indentation = 0) |
| 1603 | 1603 | { |
| 1604 | 1604 | echo defined('EE_TESTS_DIR') ? "\n" : '<br />'; |
| 1605 | - if (! $indentation) { |
|
| 1605 | + if ( ! $indentation) { |
|
| 1606 | 1606 | echo defined('EE_TESTS_DIR') ? "\n" : '<br />'; |
| 1607 | 1607 | } |
| 1608 | 1608 | for ($i = 0; $i < $indentation; $i++) { |
@@ -1613,12 +1613,12 @@ discard block |
||
| 1613 | 1613 | if ($line_item->is_percent()) { |
| 1614 | 1614 | $breakdown = "{$line_item->percent()}%"; |
| 1615 | 1615 | } else { |
| 1616 | - $breakdown = '$' . "{$line_item->unit_price()} x {$line_item->quantity()}"; |
|
| 1616 | + $breakdown = '$'."{$line_item->unit_price()} x {$line_item->quantity()}"; |
|
| 1617 | 1617 | } |
| 1618 | 1618 | } |
| 1619 | 1619 | echo wp_kses($line_item->name(), AllowedTags::getAllowedTags()); |
| 1620 | 1620 | echo " [ ID:{$line_item->ID()} | qty:{$line_item->quantity()} ] {$line_item->type()} : "; |
| 1621 | - echo '$' . (string) $line_item->total(); |
|
| 1621 | + echo '$'.(string) $line_item->total(); |
|
| 1622 | 1622 | if ($breakdown) { |
| 1623 | 1623 | echo " ( {$breakdown} )"; |
| 1624 | 1624 | } |
@@ -1689,7 +1689,7 @@ discard block |
||
| 1689 | 1689 | $billable_ticket_quantities |
| 1690 | 1690 | ); |
| 1691 | 1691 | // combine arrays but preserve numeric keys |
| 1692 | - $running_totals = |
|
| 1692 | + $running_totals = |
|
| 1693 | 1693 | array_replace_recursive($running_totals_from_subtotal, $running_totals); |
| 1694 | 1694 | $running_totals['total'] += $running_totals_from_subtotal['total']; |
| 1695 | 1695 | $running_totals['taxable']['total'] += $running_totals_from_subtotal['taxable']['total']; |
@@ -1711,8 +1711,8 @@ discard block |
||
| 1711 | 1711 | if ($line_item_id === 'taxable') { |
| 1712 | 1712 | continue; |
| 1713 | 1713 | } |
| 1714 | - $taxable_total = $running_totals['taxable'][ $line_item_id ]; |
|
| 1715 | - $running_totals[ $line_item_id ] += EEH_Line_Item::currencyFormatter()->roundForLocale( |
|
| 1714 | + $taxable_total = $running_totals['taxable'][$line_item_id]; |
|
| 1715 | + $running_totals[$line_item_id] += EEH_Line_Item::currencyFormatter()->roundForLocale( |
|
| 1716 | 1716 | $taxable_total * $tax_percent_decimal |
| 1717 | 1717 | ); |
| 1718 | 1718 | } |
@@ -1722,7 +1722,7 @@ discard block |
||
| 1722 | 1722 | // ticket line items or ???? |
| 1723 | 1723 | if ($child_line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET) { |
| 1724 | 1724 | // kk it's a ticket |
| 1725 | - if (isset($running_totals[ $child_line_item->ID() ])) { |
|
| 1725 | + if (isset($running_totals[$child_line_item->ID()])) { |
|
| 1726 | 1726 | // huh? that shouldn't happen. |
| 1727 | 1727 | $running_totals['total'] += $child_line_item->total(); |
| 1728 | 1728 | } else { |
@@ -1733,19 +1733,19 @@ discard block |
||
| 1733 | 1733 | $taxable_amount = 0; |
| 1734 | 1734 | } |
| 1735 | 1735 | // are we only calculating totals for some tickets? |
| 1736 | - if (isset($billable_ticket_quantities[ $child_line_item->OBJ_ID() ])) { |
|
| 1736 | + if (isset($billable_ticket_quantities[$child_line_item->OBJ_ID()])) { |
|
| 1737 | 1737 | $quantity = |
| 1738 | - $billable_ticket_quantities[ $child_line_item->OBJ_ID() ]; |
|
| 1739 | - $running_totals[ $child_line_item->ID() ] = $quantity |
|
| 1738 | + $billable_ticket_quantities[$child_line_item->OBJ_ID()]; |
|
| 1739 | + $running_totals[$child_line_item->ID()] = $quantity |
|
| 1740 | 1740 | ? $child_line_item->unit_price() |
| 1741 | 1741 | : 0; |
| 1742 | - $running_totals['taxable'][ $child_line_item->ID() ] = $quantity |
|
| 1742 | + $running_totals['taxable'][$child_line_item->ID()] = $quantity |
|
| 1743 | 1743 | ? $taxable_amount |
| 1744 | 1744 | : 0; |
| 1745 | 1745 | } else { |
| 1746 | 1746 | $quantity = $child_line_item->quantity(); |
| 1747 | - $running_totals[ $child_line_item->ID() ] = $child_line_item->unit_price(); |
|
| 1748 | - $running_totals['taxable'][ $child_line_item->ID() ] = $taxable_amount; |
|
| 1747 | + $running_totals[$child_line_item->ID()] = $child_line_item->unit_price(); |
|
| 1748 | + $running_totals['taxable'][$child_line_item->ID()] = $taxable_amount; |
|
| 1749 | 1749 | } |
| 1750 | 1750 | $running_totals['taxable']['total'] += $taxable_amount * $quantity; |
| 1751 | 1751 | $running_totals['total'] += $child_line_item->unit_price() * $quantity; |
@@ -1767,14 +1767,14 @@ discard block |
||
| 1767 | 1767 | } |
| 1768 | 1768 | // update the running totals |
| 1769 | 1769 | // yes this actually even works for the running grand total! |
| 1770 | - $running_totals[ $line_item_id ] = EEH_Line_Item::currencyFormatter()->roundForLocale( |
|
| 1770 | + $running_totals[$line_item_id] = EEH_Line_Item::currencyFormatter()->roundForLocale( |
|
| 1771 | 1771 | $line_items_percent_of_running_total * $this_running_total |
| 1772 | 1772 | ); |
| 1773 | 1773 | |
| 1774 | 1774 | if ($child_line_item->is_taxable()) { |
| 1775 | - $running_totals['taxable'][ $line_item_id ] = |
|
| 1775 | + $running_totals['taxable'][$line_item_id] = |
|
| 1776 | 1776 | EEH_Line_Item::currencyFormatter()->roundForLocale( |
| 1777 | - $line_items_percent_of_running_total * $running_totals['taxable'][ $line_item_id ] |
|
| 1777 | + $line_items_percent_of_running_total * $running_totals['taxable'][$line_item_id] |
|
| 1778 | 1778 | ); |
| 1779 | 1779 | } |
| 1780 | 1780 | } |
@@ -1802,15 +1802,15 @@ discard block |
||
| 1802 | 1802 | EE_Line_Item $ticket_line_item |
| 1803 | 1803 | ) { |
| 1804 | 1804 | static $final_prices_per_ticket_line_item = []; |
| 1805 | - if (empty($final_prices_per_ticket_line_item[ $total_line_item->ID() ])) { |
|
| 1806 | - $final_prices_per_ticket_line_item[ $total_line_item->ID() ] = |
|
| 1805 | + if (empty($final_prices_per_ticket_line_item[$total_line_item->ID()])) { |
|
| 1806 | + $final_prices_per_ticket_line_item[$total_line_item->ID()] = |
|
| 1807 | 1807 | EEH_Line_Item::calculate_reg_final_prices_per_line_item( |
| 1808 | 1808 | $total_line_item |
| 1809 | 1809 | ); |
| 1810 | 1810 | } |
| 1811 | 1811 | // ok now find this new registration's final price |
| 1812 | - if (isset($final_prices_per_ticket_line_item[ $total_line_item->ID() ][ $ticket_line_item->ID() ])) { |
|
| 1813 | - return $final_prices_per_ticket_line_item[ $total_line_item->ID() ][ $ticket_line_item->ID() ]; |
|
| 1812 | + if (isset($final_prices_per_ticket_line_item[$total_line_item->ID()][$ticket_line_item->ID()])) { |
|
| 1813 | + return $final_prices_per_ticket_line_item[$total_line_item->ID()][$ticket_line_item->ID()]; |
|
| 1814 | 1814 | } |
| 1815 | 1815 | $message = sprintf( |
| 1816 | 1816 | esc_html__( |
@@ -1820,7 +1820,7 @@ discard block |
||
| 1820 | 1820 | $ticket_line_item->ID() |
| 1821 | 1821 | ); |
| 1822 | 1822 | if (WP_DEBUG) { |
| 1823 | - $message .= '<br>' . print_r($final_prices_per_ticket_line_item, true); |
|
| 1823 | + $message .= '<br>'.print_r($final_prices_per_ticket_line_item, true); |
|
| 1824 | 1824 | throw new OutOfRangeException($message); |
| 1825 | 1825 | } |
| 1826 | 1826 | EE_Log::instance()->log(__CLASS__, __FUNCTION__, $message); |
@@ -14,299 +14,299 @@ |
||
| 14 | 14 | */ |
| 15 | 15 | class EEH_Money extends EEH_Base |
| 16 | 16 | { |
| 17 | - /** |
|
| 18 | - * @var CurrencyFormatter |
|
| 19 | - * @since $VID:$ |
|
| 20 | - */ |
|
| 21 | - private static $currency_formatter; |
|
| 17 | + /** |
|
| 18 | + * @var CurrencyFormatter |
|
| 19 | + * @since $VID:$ |
|
| 20 | + */ |
|
| 21 | + private static $currency_formatter; |
|
| 22 | 22 | |
| 23 | 23 | |
| 24 | - /** |
|
| 25 | - * @return CurrencyFormatter |
|
| 26 | - * @since $VID:$ |
|
| 27 | - */ |
|
| 28 | - private static function getCurrencyFormatter() |
|
| 29 | - { |
|
| 30 | - if (! EEH_Money::$currency_formatter instanceof CurrencyFormatter) { |
|
| 31 | - EEH_Money::$currency_formatter = LoaderFactory::getLoader()->getShared(CurrencyFormatter::class); |
|
| 32 | - } |
|
| 33 | - return EEH_Money::$currency_formatter; |
|
| 34 | - } |
|
| 24 | + /** |
|
| 25 | + * @return CurrencyFormatter |
|
| 26 | + * @since $VID:$ |
|
| 27 | + */ |
|
| 28 | + private static function getCurrencyFormatter() |
|
| 29 | + { |
|
| 30 | + if (! EEH_Money::$currency_formatter instanceof CurrencyFormatter) { |
|
| 31 | + EEH_Money::$currency_formatter = LoaderFactory::getLoader()->getShared(CurrencyFormatter::class); |
|
| 32 | + } |
|
| 33 | + return EEH_Money::$currency_formatter; |
|
| 34 | + } |
|
| 35 | 35 | |
| 36 | 36 | |
| 37 | - /** |
|
| 38 | - * @param string $CNT_ISO |
|
| 39 | - * @return Locale |
|
| 40 | - * @throws EE_Error |
|
| 41 | - * @since $VID:$ |
|
| 42 | - */ |
|
| 43 | - private static function getLocaleForCountryISO($CNT_ISO) |
|
| 44 | - { |
|
| 45 | - $currency_config = EEH_Money::get_currency_config($CNT_ISO); |
|
| 46 | - return EEH_Money::getCurrencyFormatter()->getLocaleForCurrencyISO($currency_config->code); |
|
| 47 | - } |
|
| 37 | + /** |
|
| 38 | + * @param string $CNT_ISO |
|
| 39 | + * @return Locale |
|
| 40 | + * @throws EE_Error |
|
| 41 | + * @since $VID:$ |
|
| 42 | + */ |
|
| 43 | + private static function getLocaleForCountryISO($CNT_ISO) |
|
| 44 | + { |
|
| 45 | + $currency_config = EEH_Money::get_currency_config($CNT_ISO); |
|
| 46 | + return EEH_Money::getCurrencyFormatter()->getLocaleForCurrencyISO($currency_config->code); |
|
| 47 | + } |
|
| 48 | 48 | |
| 49 | 49 | |
| 50 | - /** |
|
| 51 | - * @param float|int|string $money_value |
|
| 52 | - * @param int|null $format one of the CurrencyFormatter::FORMAT_* constants |
|
| 53 | - * @param string|Locale $locale locale name ex: en_US, en_CA, fr_CA, de_DE, etc, or Locale object |
|
| 54 | - * @return string |
|
| 55 | - * @since $VID:$ |
|
| 56 | - */ |
|
| 57 | - public static function formatForLocale( |
|
| 58 | - $money_value, |
|
| 59 | - $format = CurrencyFormatter::FORMAT_LOCALIZED_CURRENCY, |
|
| 60 | - $locale = '' |
|
| 61 | - ) { |
|
| 62 | - return EEH_Money::getCurrencyFormatter()->formatForLocale($money_value, $format, null, $locale); |
|
| 63 | - } |
|
| 50 | + /** |
|
| 51 | + * @param float|int|string $money_value |
|
| 52 | + * @param int|null $format one of the CurrencyFormatter::FORMAT_* constants |
|
| 53 | + * @param string|Locale $locale locale name ex: en_US, en_CA, fr_CA, de_DE, etc, or Locale object |
|
| 54 | + * @return string |
|
| 55 | + * @since $VID:$ |
|
| 56 | + */ |
|
| 57 | + public static function formatForLocale( |
|
| 58 | + $money_value, |
|
| 59 | + $format = CurrencyFormatter::FORMAT_LOCALIZED_CURRENCY, |
|
| 60 | + $locale = '' |
|
| 61 | + ) { |
|
| 62 | + return EEH_Money::getCurrencyFormatter()->formatForLocale($money_value, $format, null, $locale); |
|
| 63 | + } |
|
| 64 | 64 | |
| 65 | 65 | |
| 66 | - /** |
|
| 67 | - * @param string|Locale $locale locale name ex: en_US, en_CA, fr_CA, de_DE, etc, or Locale object |
|
| 68 | - * @return string ex: 'USD' |
|
| 69 | - * @since $VID:$ |
|
| 70 | - */ |
|
| 71 | - public static function getCurrencyIsoCodeForLocale($locale = '') |
|
| 72 | - { |
|
| 73 | - return EEH_Money::getCurrencyFormatter()->getCurrencyIsoCodeForLocale($locale); |
|
| 74 | - } |
|
| 66 | + /** |
|
| 67 | + * @param string|Locale $locale locale name ex: en_US, en_CA, fr_CA, de_DE, etc, or Locale object |
|
| 68 | + * @return string ex: 'USD' |
|
| 69 | + * @since $VID:$ |
|
| 70 | + */ |
|
| 71 | + public static function getCurrencyIsoCodeForLocale($locale = '') |
|
| 72 | + { |
|
| 73 | + return EEH_Money::getCurrencyFormatter()->getCurrencyIsoCodeForLocale($locale); |
|
| 74 | + } |
|
| 75 | 75 | |
| 76 | 76 | |
| 77 | - /** |
|
| 78 | - * @param string|Locale $locale locale name ex: en_US, en_CA, fr_CA, de_DE, etc, or Locale object |
|
| 79 | - * @return string ex: '$' |
|
| 80 | - * @since $VID:$ |
|
| 81 | - */ |
|
| 82 | - public static function getCurrencySymbolForLocale($locale = '') |
|
| 83 | - { |
|
| 84 | - return EEH_Money::getCurrencyFormatter()->getCurrencySymbolForLocale($locale); |
|
| 85 | - } |
|
| 77 | + /** |
|
| 78 | + * @param string|Locale $locale locale name ex: en_US, en_CA, fr_CA, de_DE, etc, or Locale object |
|
| 79 | + * @return string ex: '$' |
|
| 80 | + * @since $VID:$ |
|
| 81 | + */ |
|
| 82 | + public static function getCurrencySymbolForLocale($locale = '') |
|
| 83 | + { |
|
| 84 | + return EEH_Money::getCurrencyFormatter()->getCurrencySymbolForLocale($locale); |
|
| 85 | + } |
|
| 86 | 86 | |
| 87 | - /** |
|
| 88 | - * This removes all localized money formatting from the incoming value |
|
| 89 | - * Note: uses this site's currency settings for deciding what is considered a |
|
| 90 | - * "thousands separator" (usually the character "," ) |
|
| 91 | - * and what is a "decimal mark" (usually the character ".") |
|
| 92 | - * |
|
| 93 | - * @param int|float|string $money_value |
|
| 94 | - * @param string $CNT_ISO |
|
| 95 | - * @return float |
|
| 96 | - * @throws EE_Error |
|
| 97 | - */ |
|
| 98 | - public static function strip_localized_money_formatting($money_value, $CNT_ISO = '') |
|
| 99 | - { |
|
| 100 | - $locale = EEH_Money::getLocaleForCountryISO($CNT_ISO); |
|
| 101 | - return EEH_Money::getCurrencyFormatter()->parseForLocale($money_value, $locale); |
|
| 102 | - } |
|
| 87 | + /** |
|
| 88 | + * This removes all localized money formatting from the incoming value |
|
| 89 | + * Note: uses this site's currency settings for deciding what is considered a |
|
| 90 | + * "thousands separator" (usually the character "," ) |
|
| 91 | + * and what is a "decimal mark" (usually the character ".") |
|
| 92 | + * |
|
| 93 | + * @param int|float|string $money_value |
|
| 94 | + * @param string $CNT_ISO |
|
| 95 | + * @return float |
|
| 96 | + * @throws EE_Error |
|
| 97 | + */ |
|
| 98 | + public static function strip_localized_money_formatting($money_value, $CNT_ISO = '') |
|
| 99 | + { |
|
| 100 | + $locale = EEH_Money::getLocaleForCountryISO($CNT_ISO); |
|
| 101 | + return EEH_Money::getCurrencyFormatter()->parseForLocale($money_value, $locale); |
|
| 102 | + } |
|
| 103 | 103 | |
| 104 | 104 | |
| 105 | - /** |
|
| 106 | - * This converts an incoming localized money value into a standard float item (to three decimal places) |
|
| 107 | - * Only use this if you know the $money_value follows your currency configuration's |
|
| 108 | - * settings. Note: this uses this site's currency settings for deciding what is considered a |
|
| 109 | - * "thousands separator" (usually the character "," ) |
|
| 110 | - * and what is a "decimal mark" (usually the character ".") |
|
| 111 | - * |
|
| 112 | - * @param int|string $money_value |
|
| 113 | - * @return float |
|
| 114 | - */ |
|
| 115 | - public static function convert_to_float_from_localized_money($money_value) |
|
| 116 | - { |
|
| 117 | - return EEH_Money::getCurrencyFormatter()->precisionRound( |
|
| 118 | - EEH_Money::getCurrencyFormatter()->parseForLocale($money_value) |
|
| 119 | - ); |
|
| 120 | - } |
|
| 105 | + /** |
|
| 106 | + * This converts an incoming localized money value into a standard float item (to three decimal places) |
|
| 107 | + * Only use this if you know the $money_value follows your currency configuration's |
|
| 108 | + * settings. Note: this uses this site's currency settings for deciding what is considered a |
|
| 109 | + * "thousands separator" (usually the character "," ) |
|
| 110 | + * and what is a "decimal mark" (usually the character ".") |
|
| 111 | + * |
|
| 112 | + * @param int|string $money_value |
|
| 113 | + * @return float |
|
| 114 | + */ |
|
| 115 | + public static function convert_to_float_from_localized_money($money_value) |
|
| 116 | + { |
|
| 117 | + return EEH_Money::getCurrencyFormatter()->precisionRound( |
|
| 118 | + EEH_Money::getCurrencyFormatter()->parseForLocale($money_value) |
|
| 119 | + ); |
|
| 120 | + } |
|
| 121 | 121 | |
| 122 | 122 | |
| 123 | - /** |
|
| 124 | - * For comparing floats. Default operator is '=', but see the $operator below for all options. |
|
| 125 | - * This should be used to compare floats instead of normal '==' because floats |
|
| 126 | - * are inherently imprecise, and so you can sometimes have two floats that appear to be identical |
|
| 127 | - * but actually differ by 0.00000001. |
|
| 128 | - * |
|
| 129 | - * @see http://biostall.com/php-function-to-compare-floating-point-numbers |
|
| 130 | - * @param float $float1 |
|
| 131 | - * @param float $float2 |
|
| 132 | - * @param string $operator The operator. Valid options are =, <=, <, >=, >, <>, eq, lt, lte, gt, gte, ne |
|
| 133 | - * @return bool whether the equation is true or false |
|
| 134 | - * @throws EE_Error |
|
| 135 | - */ |
|
| 136 | - public static function compare_floats($float1, $float2, $operator = '=') |
|
| 137 | - { |
|
| 138 | - // Check numbers to 5 digits of precision |
|
| 139 | - $epsilon = 0.00001; |
|
| 140 | - $float1 = (float) $float1; |
|
| 141 | - $float2 = (float) $float2; |
|
| 142 | - switch ($operator) { |
|
| 143 | - // equal |
|
| 144 | - case "=": |
|
| 145 | - case "==": |
|
| 146 | - case "===": |
|
| 147 | - case "eq": |
|
| 148 | - if (abs($float1 - $float2) < $epsilon) { |
|
| 149 | - return true; |
|
| 150 | - } |
|
| 151 | - break; |
|
| 152 | - // less than |
|
| 153 | - case "<": |
|
| 154 | - case "lt": |
|
| 155 | - if (abs($float1 - $float2) < $epsilon) { |
|
| 156 | - return false; |
|
| 157 | - } else { |
|
| 158 | - if ($float1 < $float2) { |
|
| 159 | - return true; |
|
| 160 | - } |
|
| 161 | - } |
|
| 162 | - break; |
|
| 163 | - // less than or equal |
|
| 164 | - case "<=": |
|
| 165 | - case "lte": |
|
| 166 | - if ( |
|
| 167 | - EEH_Money::compare_floats($float1, $float2, '<') |
|
| 168 | - || EEH_Money::compare_floats($float1, $float2, '=') |
|
| 169 | - ) { |
|
| 170 | - return true; |
|
| 171 | - } |
|
| 172 | - break; |
|
| 173 | - // greater than |
|
| 174 | - case ">": |
|
| 175 | - case "gt": |
|
| 176 | - if (abs($float1 - $float2) < $epsilon) { |
|
| 177 | - return false; |
|
| 178 | - } else { |
|
| 179 | - if ($float1 > $float2) { |
|
| 180 | - return true; |
|
| 181 | - } |
|
| 182 | - } |
|
| 183 | - break; |
|
| 184 | - // greater than or equal |
|
| 185 | - case ">=": |
|
| 186 | - case "gte": |
|
| 187 | - if ( |
|
| 188 | - EEH_Money::compare_floats($float1, $float2, '>') |
|
| 189 | - || EEH_Money::compare_floats($float1, $float2, '=') |
|
| 190 | - ) { |
|
| 191 | - return true; |
|
| 192 | - } |
|
| 193 | - break; |
|
| 194 | - case "<>": |
|
| 195 | - case "!=": |
|
| 196 | - case "ne": |
|
| 197 | - if (abs($float1 - $float2) > $epsilon) { |
|
| 198 | - return true; |
|
| 199 | - } |
|
| 200 | - break; |
|
| 201 | - default: |
|
| 202 | - throw new EE_Error( |
|
| 203 | - sprintf( |
|
| 204 | - esc_html__( |
|
| 205 | - "Unknown operator %s in EEH_Money::compare_floats()", |
|
| 206 | - 'event_espresso' |
|
| 207 | - ), |
|
| 208 | - $operator |
|
| 209 | - ) |
|
| 210 | - ); |
|
| 211 | - } |
|
| 212 | - return false; |
|
| 213 | - } |
|
| 123 | + /** |
|
| 124 | + * For comparing floats. Default operator is '=', but see the $operator below for all options. |
|
| 125 | + * This should be used to compare floats instead of normal '==' because floats |
|
| 126 | + * are inherently imprecise, and so you can sometimes have two floats that appear to be identical |
|
| 127 | + * but actually differ by 0.00000001. |
|
| 128 | + * |
|
| 129 | + * @see http://biostall.com/php-function-to-compare-floating-point-numbers |
|
| 130 | + * @param float $float1 |
|
| 131 | + * @param float $float2 |
|
| 132 | + * @param string $operator The operator. Valid options are =, <=, <, >=, >, <>, eq, lt, lte, gt, gte, ne |
|
| 133 | + * @return bool whether the equation is true or false |
|
| 134 | + * @throws EE_Error |
|
| 135 | + */ |
|
| 136 | + public static function compare_floats($float1, $float2, $operator = '=') |
|
| 137 | + { |
|
| 138 | + // Check numbers to 5 digits of precision |
|
| 139 | + $epsilon = 0.00001; |
|
| 140 | + $float1 = (float) $float1; |
|
| 141 | + $float2 = (float) $float2; |
|
| 142 | + switch ($operator) { |
|
| 143 | + // equal |
|
| 144 | + case "=": |
|
| 145 | + case "==": |
|
| 146 | + case "===": |
|
| 147 | + case "eq": |
|
| 148 | + if (abs($float1 - $float2) < $epsilon) { |
|
| 149 | + return true; |
|
| 150 | + } |
|
| 151 | + break; |
|
| 152 | + // less than |
|
| 153 | + case "<": |
|
| 154 | + case "lt": |
|
| 155 | + if (abs($float1 - $float2) < $epsilon) { |
|
| 156 | + return false; |
|
| 157 | + } else { |
|
| 158 | + if ($float1 < $float2) { |
|
| 159 | + return true; |
|
| 160 | + } |
|
| 161 | + } |
|
| 162 | + break; |
|
| 163 | + // less than or equal |
|
| 164 | + case "<=": |
|
| 165 | + case "lte": |
|
| 166 | + if ( |
|
| 167 | + EEH_Money::compare_floats($float1, $float2, '<') |
|
| 168 | + || EEH_Money::compare_floats($float1, $float2, '=') |
|
| 169 | + ) { |
|
| 170 | + return true; |
|
| 171 | + } |
|
| 172 | + break; |
|
| 173 | + // greater than |
|
| 174 | + case ">": |
|
| 175 | + case "gt": |
|
| 176 | + if (abs($float1 - $float2) < $epsilon) { |
|
| 177 | + return false; |
|
| 178 | + } else { |
|
| 179 | + if ($float1 > $float2) { |
|
| 180 | + return true; |
|
| 181 | + } |
|
| 182 | + } |
|
| 183 | + break; |
|
| 184 | + // greater than or equal |
|
| 185 | + case ">=": |
|
| 186 | + case "gte": |
|
| 187 | + if ( |
|
| 188 | + EEH_Money::compare_floats($float1, $float2, '>') |
|
| 189 | + || EEH_Money::compare_floats($float1, $float2, '=') |
|
| 190 | + ) { |
|
| 191 | + return true; |
|
| 192 | + } |
|
| 193 | + break; |
|
| 194 | + case "<>": |
|
| 195 | + case "!=": |
|
| 196 | + case "ne": |
|
| 197 | + if (abs($float1 - $float2) > $epsilon) { |
|
| 198 | + return true; |
|
| 199 | + } |
|
| 200 | + break; |
|
| 201 | + default: |
|
| 202 | + throw new EE_Error( |
|
| 203 | + sprintf( |
|
| 204 | + esc_html__( |
|
| 205 | + "Unknown operator %s in EEH_Money::compare_floats()", |
|
| 206 | + 'event_espresso' |
|
| 207 | + ), |
|
| 208 | + $operator |
|
| 209 | + ) |
|
| 210 | + ); |
|
| 211 | + } |
|
| 212 | + return false; |
|
| 213 | + } |
|
| 214 | 214 | |
| 215 | 215 | |
| 216 | - /** |
|
| 217 | - * This returns a localized format string suitable for jQplot. |
|
| 218 | - * |
|
| 219 | - * @param string $CNT_ISO If this is provided, then will attempt to get the currency settings for the country. |
|
| 220 | - * Otherwise will use currency settings for current active country on site. |
|
| 221 | - * @return string |
|
| 222 | - * @throws EE_Error |
|
| 223 | - */ |
|
| 224 | - public static function get_format_for_jqplot($CNT_ISO = '') |
|
| 225 | - { |
|
| 226 | - // default format |
|
| 227 | - $format = 'f'; |
|
| 228 | - $locale = EEH_Money::getLocaleForCountryISO($CNT_ISO); |
|
| 229 | - // first get the decimal place and number of places |
|
| 230 | - $format = "%'" . $locale->currencyDecimalPoint() . $locale->decimalPrecision() . $format; |
|
| 231 | - $spacer = $locale->currencySymbolSpaceB4Positive() ? ' ' : ''; |
|
| 232 | - // currency symbol on right side. |
|
| 233 | - $format = $locale->currencySymbolB4Positive() |
|
| 234 | - ? $locale->currencySymbol() . $spacer . $format |
|
| 235 | - : $format . $spacer . $locale->currencySymbol(); |
|
| 236 | - return $format; |
|
| 237 | - } |
|
| 216 | + /** |
|
| 217 | + * This returns a localized format string suitable for jQplot. |
|
| 218 | + * |
|
| 219 | + * @param string $CNT_ISO If this is provided, then will attempt to get the currency settings for the country. |
|
| 220 | + * Otherwise will use currency settings for current active country on site. |
|
| 221 | + * @return string |
|
| 222 | + * @throws EE_Error |
|
| 223 | + */ |
|
| 224 | + public static function get_format_for_jqplot($CNT_ISO = '') |
|
| 225 | + { |
|
| 226 | + // default format |
|
| 227 | + $format = 'f'; |
|
| 228 | + $locale = EEH_Money::getLocaleForCountryISO($CNT_ISO); |
|
| 229 | + // first get the decimal place and number of places |
|
| 230 | + $format = "%'" . $locale->currencyDecimalPoint() . $locale->decimalPrecision() . $format; |
|
| 231 | + $spacer = $locale->currencySymbolSpaceB4Positive() ? ' ' : ''; |
|
| 232 | + // currency symbol on right side. |
|
| 233 | + $format = $locale->currencySymbolB4Positive() |
|
| 234 | + ? $locale->currencySymbol() . $spacer . $format |
|
| 235 | + : $format . $spacer . $locale->currencySymbol(); |
|
| 236 | + return $format; |
|
| 237 | + } |
|
| 238 | 238 | |
| 239 | 239 | |
| 240 | - /** |
|
| 241 | - * This returns a localized format string suitable for usage with the Google Charts API format param. |
|
| 242 | - * |
|
| 243 | - * @param string $CNT_ISO If this is provided, then will attempt to get the currency settings for the country. |
|
| 244 | - * Otherwise will use currency settings for current active country on site. |
|
| 245 | - * Note: GoogleCharts uses ICU pattern set |
|
| 246 | - * (@see http://icu-project.org/apiref/icu4c/classDecimalFormat.html#_details) |
|
| 247 | - * @return array |
|
| 248 | - * @throws EE_Error |
|
| 249 | - */ |
|
| 250 | - public static function get_format_for_google_charts($CNT_ISO = '') |
|
| 251 | - { |
|
| 252 | - $locale = EEH_Money::getLocaleForCountryISO($CNT_ISO); |
|
| 253 | - $decimal_places_placeholder = str_pad('', $locale->decimalPrecision(), '0'); |
|
| 254 | - // first get the decimal place and number of places |
|
| 255 | - $format = '#,##0.' . $decimal_places_placeholder; |
|
| 256 | - // currency symbol on right side. |
|
| 257 | - $format = $locale->currencySymbolB4Positive() |
|
| 258 | - ? $locale->currencySymbol() . $format |
|
| 259 | - : $format |
|
| 260 | - . $locale->currencySymbol(); |
|
| 261 | - $formatterObject = [ |
|
| 262 | - 'decimalSymbol' => $locale->currencyDecimalPoint(), |
|
| 263 | - 'groupingSymbol' => $locale->currencyThousandsSeparator(), |
|
| 264 | - 'fractionDigits' => $locale->decimalPrecision(), |
|
| 265 | - ]; |
|
| 266 | - if ($locale->currencySymbolB4Positive()) { |
|
| 267 | - $formatterObject['prefix'] = $locale->currencySymbol(); |
|
| 268 | - } else { |
|
| 269 | - $formatterObject['suffix'] = $locale->currencySymbol(); |
|
| 270 | - } |
|
| 271 | - return [ |
|
| 272 | - 'format' => $format, |
|
| 273 | - 'formatterObject' => $formatterObject, |
|
| 274 | - ]; |
|
| 275 | - } |
|
| 240 | + /** |
|
| 241 | + * This returns a localized format string suitable for usage with the Google Charts API format param. |
|
| 242 | + * |
|
| 243 | + * @param string $CNT_ISO If this is provided, then will attempt to get the currency settings for the country. |
|
| 244 | + * Otherwise will use currency settings for current active country on site. |
|
| 245 | + * Note: GoogleCharts uses ICU pattern set |
|
| 246 | + * (@see http://icu-project.org/apiref/icu4c/classDecimalFormat.html#_details) |
|
| 247 | + * @return array |
|
| 248 | + * @throws EE_Error |
|
| 249 | + */ |
|
| 250 | + public static function get_format_for_google_charts($CNT_ISO = '') |
|
| 251 | + { |
|
| 252 | + $locale = EEH_Money::getLocaleForCountryISO($CNT_ISO); |
|
| 253 | + $decimal_places_placeholder = str_pad('', $locale->decimalPrecision(), '0'); |
|
| 254 | + // first get the decimal place and number of places |
|
| 255 | + $format = '#,##0.' . $decimal_places_placeholder; |
|
| 256 | + // currency symbol on right side. |
|
| 257 | + $format = $locale->currencySymbolB4Positive() |
|
| 258 | + ? $locale->currencySymbol() . $format |
|
| 259 | + : $format |
|
| 260 | + . $locale->currencySymbol(); |
|
| 261 | + $formatterObject = [ |
|
| 262 | + 'decimalSymbol' => $locale->currencyDecimalPoint(), |
|
| 263 | + 'groupingSymbol' => $locale->currencyThousandsSeparator(), |
|
| 264 | + 'fractionDigits' => $locale->decimalPrecision(), |
|
| 265 | + ]; |
|
| 266 | + if ($locale->currencySymbolB4Positive()) { |
|
| 267 | + $formatterObject['prefix'] = $locale->currencySymbol(); |
|
| 268 | + } else { |
|
| 269 | + $formatterObject['suffix'] = $locale->currencySymbol(); |
|
| 270 | + } |
|
| 271 | + return [ |
|
| 272 | + 'format' => $format, |
|
| 273 | + 'formatterObject' => $formatterObject, |
|
| 274 | + ]; |
|
| 275 | + } |
|
| 276 | 276 | |
| 277 | 277 | |
| 278 | - /** |
|
| 279 | - * @param string $CNT_ISO |
|
| 280 | - * @return EE_Currency_Config|null |
|
| 281 | - * @throws EE_Error |
|
| 282 | - */ |
|
| 283 | - public static function get_currency_config($CNT_ISO = '') |
|
| 284 | - { |
|
| 285 | - // if CNT_ISO passed lets try to get currency settings for it. |
|
| 286 | - $currency_config = $CNT_ISO !== '' |
|
| 287 | - ? new EE_Currency_Config($CNT_ISO) |
|
| 288 | - : null; |
|
| 289 | - // default currency settings for site if not set |
|
| 290 | - if (! $currency_config instanceof EE_Currency_Config) { |
|
| 291 | - $currency_config = EE_Registry::instance()->CFG->currency instanceof EE_Currency_Config |
|
| 292 | - ? EE_Registry::instance()->CFG->currency |
|
| 293 | - : new EE_Currency_Config(); |
|
| 294 | - } |
|
| 295 | - return $currency_config; |
|
| 296 | - } |
|
| 278 | + /** |
|
| 279 | + * @param string $CNT_ISO |
|
| 280 | + * @return EE_Currency_Config|null |
|
| 281 | + * @throws EE_Error |
|
| 282 | + */ |
|
| 283 | + public static function get_currency_config($CNT_ISO = '') |
|
| 284 | + { |
|
| 285 | + // if CNT_ISO passed lets try to get currency settings for it. |
|
| 286 | + $currency_config = $CNT_ISO !== '' |
|
| 287 | + ? new EE_Currency_Config($CNT_ISO) |
|
| 288 | + : null; |
|
| 289 | + // default currency settings for site if not set |
|
| 290 | + if (! $currency_config instanceof EE_Currency_Config) { |
|
| 291 | + $currency_config = EE_Registry::instance()->CFG->currency instanceof EE_Currency_Config |
|
| 292 | + ? EE_Registry::instance()->CFG->currency |
|
| 293 | + : new EE_Currency_Config(); |
|
| 294 | + } |
|
| 295 | + return $currency_config; |
|
| 296 | + } |
|
| 297 | 297 | |
| 298 | 298 | |
| 299 | - /** |
|
| 300 | - * Rounds the number to a whole penny amount |
|
| 301 | - * |
|
| 302 | - * @param float $amount |
|
| 303 | - * @param string $currency_code |
|
| 304 | - * @return float |
|
| 305 | - * @throws EE_Error |
|
| 306 | - */ |
|
| 307 | - public static function round_for_currency($amount, $currency_code = '') |
|
| 308 | - { |
|
| 309 | - $locale = EEH_Money::getLocaleForCountryISO($currency_code); |
|
| 310 | - return EEH_Money::getCurrencyFormatter()->roundForLocale($amount, $locale); |
|
| 311 | - } |
|
| 299 | + /** |
|
| 300 | + * Rounds the number to a whole penny amount |
|
| 301 | + * |
|
| 302 | + * @param float $amount |
|
| 303 | + * @param string $currency_code |
|
| 304 | + * @return float |
|
| 305 | + * @throws EE_Error |
|
| 306 | + */ |
|
| 307 | + public static function round_for_currency($amount, $currency_code = '') |
|
| 308 | + { |
|
| 309 | + $locale = EEH_Money::getLocaleForCountryISO($currency_code); |
|
| 310 | + return EEH_Money::getCurrencyFormatter()->roundForLocale($amount, $locale); |
|
| 311 | + } |
|
| 312 | 312 | } |
@@ -27,7 +27,7 @@ discard block |
||
| 27 | 27 | */ |
| 28 | 28 | private static function getCurrencyFormatter() |
| 29 | 29 | { |
| 30 | - if (! EEH_Money::$currency_formatter instanceof CurrencyFormatter) { |
|
| 30 | + if ( ! EEH_Money::$currency_formatter instanceof CurrencyFormatter) { |
|
| 31 | 31 | EEH_Money::$currency_formatter = LoaderFactory::getLoader()->getShared(CurrencyFormatter::class); |
| 32 | 32 | } |
| 33 | 33 | return EEH_Money::$currency_formatter; |
@@ -224,15 +224,15 @@ discard block |
||
| 224 | 224 | public static function get_format_for_jqplot($CNT_ISO = '') |
| 225 | 225 | { |
| 226 | 226 | // default format |
| 227 | - $format = 'f'; |
|
| 227 | + $format = 'f'; |
|
| 228 | 228 | $locale = EEH_Money::getLocaleForCountryISO($CNT_ISO); |
| 229 | 229 | // first get the decimal place and number of places |
| 230 | - $format = "%'" . $locale->currencyDecimalPoint() . $locale->decimalPrecision() . $format; |
|
| 230 | + $format = "%'".$locale->currencyDecimalPoint().$locale->decimalPrecision().$format; |
|
| 231 | 231 | $spacer = $locale->currencySymbolSpaceB4Positive() ? ' ' : ''; |
| 232 | 232 | // currency symbol on right side. |
| 233 | 233 | $format = $locale->currencySymbolB4Positive() |
| 234 | - ? $locale->currencySymbol() . $spacer . $format |
|
| 235 | - : $format . $spacer . $locale->currencySymbol(); |
|
| 234 | + ? $locale->currencySymbol().$spacer.$format |
|
| 235 | + : $format.$spacer.$locale->currencySymbol(); |
|
| 236 | 236 | return $format; |
| 237 | 237 | } |
| 238 | 238 | |
@@ -252,10 +252,10 @@ discard block |
||
| 252 | 252 | $locale = EEH_Money::getLocaleForCountryISO($CNT_ISO); |
| 253 | 253 | $decimal_places_placeholder = str_pad('', $locale->decimalPrecision(), '0'); |
| 254 | 254 | // first get the decimal place and number of places |
| 255 | - $format = '#,##0.' . $decimal_places_placeholder; |
|
| 255 | + $format = '#,##0.'.$decimal_places_placeholder; |
|
| 256 | 256 | // currency symbol on right side. |
| 257 | - $format = $locale->currencySymbolB4Positive() |
|
| 258 | - ? $locale->currencySymbol() . $format |
|
| 257 | + $format = $locale->currencySymbolB4Positive() |
|
| 258 | + ? $locale->currencySymbol().$format |
|
| 259 | 259 | : $format |
| 260 | 260 | . $locale->currencySymbol(); |
| 261 | 261 | $formatterObject = [ |
@@ -287,7 +287,7 @@ discard block |
||
| 287 | 287 | ? new EE_Currency_Config($CNT_ISO) |
| 288 | 288 | : null; |
| 289 | 289 | // default currency settings for site if not set |
| 290 | - if (! $currency_config instanceof EE_Currency_Config) { |
|
| 290 | + if ( ! $currency_config instanceof EE_Currency_Config) { |
|
| 291 | 291 | $currency_config = EE_Registry::instance()->CFG->currency instanceof EE_Currency_Config |
| 292 | 292 | ? EE_Registry::instance()->CFG->currency |
| 293 | 293 | : new EE_Currency_Config(); |
@@ -33,372 +33,372 @@ |
||
| 33 | 33 | */ |
| 34 | 34 | class EEH_Inflector |
| 35 | 35 | { |
| 36 | - /** |
|
| 37 | - * Just calls self::pluralize and strtolower on $word and returns it |
|
| 38 | - * |
|
| 39 | - * @param string $word |
|
| 40 | - * @return string |
|
| 41 | - */ |
|
| 42 | - public static function pluralize_and_lower($word) |
|
| 43 | - { |
|
| 44 | - return strtolower(self::pluralize($word)); |
|
| 45 | - } |
|
| 46 | - |
|
| 47 | - |
|
| 48 | - /** |
|
| 49 | - * @param string $word |
|
| 50 | - * @return mixed |
|
| 51 | - */ |
|
| 52 | - public static function singularize_and_upper($word) |
|
| 53 | - { |
|
| 54 | - return str_replace(' ', '_', self::humanize(self::singularize($word), 'all')); |
|
| 55 | - } |
|
| 56 | - |
|
| 57 | - |
|
| 58 | - /** |
|
| 59 | - * Pluralizes English nouns. |
|
| 60 | - * |
|
| 61 | - * @access public |
|
| 62 | - * @static |
|
| 63 | - * @param string $word English noun to pluralize |
|
| 64 | - * @return string Plural noun |
|
| 65 | - */ |
|
| 66 | - public static function pluralize($word) |
|
| 67 | - { |
|
| 68 | - $plural = [ |
|
| 69 | - '/(quiz)$/i' => '\1zes', |
|
| 70 | - '/^(ox)$/i' => '\1en', |
|
| 71 | - '/([m|l])ouse$/i' => '\1ice', |
|
| 72 | - '/(matr|vert|ind)ix|ex$/i' => '\1ices', |
|
| 73 | - '/(x|ch|ss|sh)$/i' => '\1es', |
|
| 74 | - '/([^aeiouy]|qu)ies$/i' => '\1y', |
|
| 75 | - '/([^aeiouy]|qu)y$/i' => '\1ies', |
|
| 76 | - '/(hive)$/i' => '\1s', |
|
| 77 | - '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves', |
|
| 78 | - '/sis$/i' => 'ses', |
|
| 79 | - '/([ti])um$/i' => '\1a', |
|
| 80 | - '/(buffal|tomat)o$/i' => '\1oes', |
|
| 81 | - '/(bu)s$/i' => '\1ses', |
|
| 82 | - '/(alias|status)/i' => '\1es', |
|
| 83 | - '/(octop|vir)us$/i' => '\1i', |
|
| 84 | - '/(ax|test)is$/i' => '\1es', |
|
| 85 | - '/s$/i' => 's', |
|
| 86 | - '/$/' => 's', |
|
| 87 | - ]; |
|
| 88 | - |
|
| 89 | - $uncountable = ['equipment', 'information', 'rice', 'money', 'species', 'series', 'fish', 'sheep']; |
|
| 90 | - |
|
| 91 | - $irregular = [ |
|
| 92 | - 'person' => 'people', |
|
| 93 | - 'man' => 'men', |
|
| 94 | - 'child' => 'children', |
|
| 95 | - 'sex' => 'sexes', |
|
| 96 | - 'move' => 'moves', |
|
| 97 | - ]; |
|
| 98 | - |
|
| 99 | - $lowercased_word = strtolower($word); |
|
| 100 | - |
|
| 101 | - foreach ($uncountable as $_uncountable) { |
|
| 102 | - // even though the word "price" ends in "rice", it can be pluralized, |
|
| 103 | - // so check the previous character isn't a letter |
|
| 104 | - if ( |
|
| 105 | - substr($lowercased_word, (-1 * strlen($_uncountable))) == $_uncountable |
|
| 106 | - && ! ctype_alpha($lowercased_word[ strlen($lowercased_word) - strlen($_uncountable) ]) |
|
| 107 | - ) { |
|
| 108 | - return $word; |
|
| 109 | - } |
|
| 110 | - } |
|
| 111 | - |
|
| 112 | - foreach ($irregular as $_plural => $_singular) { |
|
| 113 | - if (preg_match('/(' . $_plural . ')$/i', $word, $arr)) { |
|
| 114 | - return preg_replace('/(' . $_plural . ')$/i', substr($arr[0], 0, 1) . substr($_singular, 1), $word); |
|
| 115 | - } |
|
| 116 | - } |
|
| 117 | - |
|
| 118 | - foreach ($plural as $rule => $replacement) { |
|
| 119 | - if (preg_match($rule, $word)) { |
|
| 120 | - return preg_replace($rule, $replacement, $word); |
|
| 121 | - } |
|
| 122 | - } |
|
| 123 | - return false; |
|
| 124 | - } |
|
| 125 | - |
|
| 126 | - |
|
| 127 | - /** |
|
| 128 | - * Singularizes English nouns. |
|
| 129 | - * |
|
| 130 | - * @access public |
|
| 131 | - * @static |
|
| 132 | - * @param string $word English noun to singularize |
|
| 133 | - * @return string Singular noun. |
|
| 134 | - */ |
|
| 135 | - public static function singularize($word) |
|
| 136 | - { |
|
| 137 | - $singular = [ |
|
| 138 | - '/(quiz)zes$/i' => '\1', |
|
| 139 | - '/(matr)ices$/i' => '\1ix', |
|
| 140 | - '/(vert|ind)ices$/i' => '\1ex', |
|
| 141 | - '/^(ox)en/i' => '\1', |
|
| 142 | - '/(alias|status)es$/i' => '\1', |
|
| 143 | - '/([octop|vir])i$/i' => '\1us', |
|
| 144 | - '/(cris|ax|test)es$/i' => '\1is', |
|
| 145 | - '/(shoe)s$/i' => '\1', |
|
| 146 | - '/(o)es$/i' => '\1', |
|
| 147 | - '/(bus)es$/i' => '\1', |
|
| 148 | - '/([m|l])ice$/i' => '\1ouse', |
|
| 149 | - '/(x|ch|ss|sh)es$/i' => '\1', |
|
| 150 | - '/(m)ovies$/i' => '\1ovie', |
|
| 151 | - '/(s)eries$/i' => '\1eries', |
|
| 152 | - '/([^aeiouy]|qu)ies$/i' => '\1y', |
|
| 153 | - '/([lr])ves$/i' => '\1f', |
|
| 154 | - '/(tive)s$/i' => '\1', |
|
| 155 | - '/(hive)s$/i' => '\1', |
|
| 156 | - '/([^f])ves$/i' => '\1fe', |
|
| 157 | - '/(^analy)ses$/i' => '\1sis', |
|
| 158 | - '/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis', |
|
| 159 | - '/([ti])a$/i' => '\1um', |
|
| 160 | - '/(n)ews$/i' => '\1ews', |
|
| 161 | - '/s$/i' => '', |
|
| 162 | - ]; |
|
| 163 | - |
|
| 164 | - $uncountable = ['equipment', 'information', 'rice', 'money', 'species', 'series', 'fish', 'sheep']; |
|
| 165 | - |
|
| 166 | - $irregular = [ |
|
| 167 | - 'person' => 'people', |
|
| 168 | - 'man' => 'men', |
|
| 169 | - 'child' => 'children', |
|
| 170 | - 'sex' => 'sexes', |
|
| 171 | - 'move' => 'moves', |
|
| 172 | - ]; |
|
| 173 | - |
|
| 174 | - $lowercased_word = strtolower($word); |
|
| 175 | - foreach ($uncountable as $_uncountable) { |
|
| 176 | - if (substr($lowercased_word, (-1 * strlen($_uncountable))) == $_uncountable) { |
|
| 177 | - return $word; |
|
| 178 | - } |
|
| 179 | - } |
|
| 180 | - |
|
| 181 | - foreach ($irregular as $_plural => $_singular) { |
|
| 182 | - if (preg_match('/(' . $_singular . ')$/i', $word, $arr)) { |
|
| 183 | - return preg_replace('/(' . $_singular . ')$/i', substr($arr[0], 0, 1) . substr($_plural, 1), $word); |
|
| 184 | - } |
|
| 185 | - } |
|
| 186 | - |
|
| 187 | - foreach ($singular as $rule => $replacement) { |
|
| 188 | - if (preg_match($rule, $word)) { |
|
| 189 | - return preg_replace($rule, $replacement, $word); |
|
| 190 | - } |
|
| 191 | - } |
|
| 192 | - |
|
| 193 | - return $word; |
|
| 194 | - } |
|
| 195 | - |
|
| 196 | - |
|
| 197 | - /** |
|
| 198 | - * Converts an underscored or CamelCase word into a English |
|
| 199 | - * sentence. |
|
| 200 | - * |
|
| 201 | - * The titleize static function converts text like "WelcomePage", |
|
| 202 | - * "welcome_page" or "welcome page" to this "Welcome |
|
| 203 | - * Page". |
|
| 204 | - * If second parameter is set to 'first' it will only |
|
| 205 | - * capitalize the first character of the title. |
|
| 206 | - * |
|
| 207 | - * @access public |
|
| 208 | - * @static |
|
| 209 | - * @param string $word Word to format as tile |
|
| 210 | - * @param string $uppercase If set to 'first' it will only uppercase the |
|
| 211 | - * first character. Otherwise it will uppercase all |
|
| 212 | - * the words in the title. |
|
| 213 | - * @return string Text formatted as title |
|
| 214 | - */ |
|
| 215 | - public static function titleize($word, $uppercase = '') |
|
| 216 | - { |
|
| 217 | - $uppercase = $uppercase === 'first' ? 'ucfirst' : 'ucwords'; |
|
| 218 | - return $uppercase(EEH_Inflector::humanize(EEH_Inflector::underscore($word))); |
|
| 219 | - } |
|
| 220 | - |
|
| 221 | - |
|
| 222 | - /** |
|
| 223 | - * Returns given word as CamelCased |
|
| 224 | - * |
|
| 225 | - * Converts a word like "send_email" to "SendEmail". It |
|
| 226 | - * will remove non alphanumeric character from the word, so |
|
| 227 | - * "who's online" will be converted to "WhoSOnline" |
|
| 228 | - * |
|
| 229 | - * @access public |
|
| 230 | - * @static |
|
| 231 | - * @param string $word Word to convert to camel case |
|
| 232 | - * @return string UpperCamelCasedWord |
|
| 233 | - * @see variablize |
|
| 234 | - */ |
|
| 235 | - public static function camelize($word) |
|
| 236 | - { |
|
| 237 | - return str_replace(' ', '', ucwords(preg_replace('/[^A-Z^a-z^0-9]+/', ' ', $word))); |
|
| 238 | - } |
|
| 239 | - |
|
| 240 | - |
|
| 241 | - /** |
|
| 242 | - * Camelizes all but the first word. This is handy converting a method which followed EE4 legacy naming convention |
|
| 243 | - * with the new PSR-based naming conventions |
|
| 244 | - * |
|
| 245 | - * @param $word |
|
| 246 | - * @return string |
|
| 247 | - */ |
|
| 248 | - public static function camelize_all_but_first($word) |
|
| 249 | - { |
|
| 250 | - return lcfirst(EEH_Inflector::camelize($word)); |
|
| 251 | - } |
|
| 252 | - |
|
| 253 | - |
|
| 254 | - /** |
|
| 255 | - * Converts a word "into_it_s_underscored_version" |
|
| 256 | - * |
|
| 257 | - * Convert any "CamelCased" or "ordinary Word" into an |
|
| 258 | - * "underscored_word". |
|
| 259 | - * |
|
| 260 | - * This can be really useful for creating friendly URLs. |
|
| 261 | - * |
|
| 262 | - * @access public |
|
| 263 | - * @static |
|
| 264 | - * @param string $word Word to underscore |
|
| 265 | - * @return string Underscored word |
|
| 266 | - */ |
|
| 267 | - public static function underscore($word) |
|
| 268 | - { |
|
| 269 | - return strtolower( |
|
| 270 | - preg_replace( |
|
| 271 | - '/[^A-Z^a-z^0-9]+/', |
|
| 272 | - '_', |
|
| 273 | - preg_replace('/([a-zd])([A-Z])/', '1_2', preg_replace('/([A-Z]+)([A-Z][a-z])/', '1_2', $word)) |
|
| 274 | - ) |
|
| 275 | - ); |
|
| 276 | - } |
|
| 277 | - |
|
| 278 | - |
|
| 279 | - /** |
|
| 280 | - * Returns a human-readable string from $word |
|
| 281 | - * |
|
| 282 | - * Returns a human-readable string from $word, by replacing |
|
| 283 | - * underscores with a space, and by upper-casing the initial |
|
| 284 | - * character by default. |
|
| 285 | - * |
|
| 286 | - * If you need to uppercase all the words you just have to |
|
| 287 | - * pass 'all' as a second parameter. |
|
| 288 | - * |
|
| 289 | - * @access public |
|
| 290 | - * @static |
|
| 291 | - * @param string $word String to "humanize" |
|
| 292 | - * @param string $uppercase If set to 'all' it will uppercase all the words |
|
| 293 | - * instead of just the first one. |
|
| 294 | - * @return string Human-readable word |
|
| 295 | - */ |
|
| 296 | - public static function humanize($word, $uppercase = '') |
|
| 297 | - { |
|
| 298 | - // make special exceptions for acronyms |
|
| 299 | - $word = str_replace('wp_', 'WP_', $word); |
|
| 300 | - $uppercase = $uppercase === 'all' ? 'ucwords' : 'ucfirst'; |
|
| 301 | - return $uppercase(str_replace('_', ' ', preg_replace('/_id$/', '', $word))); |
|
| 302 | - } |
|
| 303 | - |
|
| 304 | - |
|
| 305 | - /** |
|
| 306 | - * Same as camelize but first char is underscored |
|
| 307 | - * |
|
| 308 | - * Converts a word like "send_email" to "sendEmail". It |
|
| 309 | - * will remove non alphanumeric character from the word, so |
|
| 310 | - * "who's online" will be converted to "whoSOnline" |
|
| 311 | - * |
|
| 312 | - * @access public |
|
| 313 | - * @static |
|
| 314 | - * @param string $word Word to lowerCamelCase |
|
| 315 | - * @return string Returns a lowerCamelCasedWord |
|
| 316 | - * @see camelize |
|
| 317 | - */ |
|
| 318 | - public static function variablize($word) |
|
| 319 | - { |
|
| 320 | - $word = EEH_Inflector::camelize($word); |
|
| 321 | - return strtolower($word[0]) . substr($word, 1); |
|
| 322 | - } |
|
| 323 | - |
|
| 324 | - |
|
| 325 | - /** |
|
| 326 | - * Converts a class name to its table name according to rails |
|
| 327 | - * naming conventions. |
|
| 328 | - * |
|
| 329 | - * Converts "Person" to "people" |
|
| 330 | - * |
|
| 331 | - * @access public |
|
| 332 | - * @static |
|
| 333 | - * @param string $class_name Class name for getting related table_name. |
|
| 334 | - * @return string plural_table_name |
|
| 335 | - * @see classify |
|
| 336 | - */ |
|
| 337 | - public static function tableize($class_name) |
|
| 338 | - { |
|
| 339 | - return EEH_Inflector::pluralize(EEH_Inflector::underscore($class_name)); |
|
| 340 | - } |
|
| 341 | - |
|
| 342 | - |
|
| 343 | - /** |
|
| 344 | - * Converts a table name to its class name according to rails |
|
| 345 | - * naming conventions. |
|
| 346 | - * |
|
| 347 | - * Converts "people" to "Person" |
|
| 348 | - * |
|
| 349 | - * @access public |
|
| 350 | - * @static |
|
| 351 | - * @param string $table_name Table name for getting related ClassName. |
|
| 352 | - * @return string SingularClassName |
|
| 353 | - * @see tableize |
|
| 354 | - */ |
|
| 355 | - public static function classify($table_name) |
|
| 356 | - { |
|
| 357 | - return EEH_Inflector::camelize(EEH_Inflector::singularize($table_name)); |
|
| 358 | - } |
|
| 359 | - |
|
| 360 | - |
|
| 361 | - /** |
|
| 362 | - * Converts number to its ordinal English form. |
|
| 363 | - * |
|
| 364 | - * This method converts 13 to 13th, 2 to 2nd ... |
|
| 365 | - * |
|
| 366 | - * @access public |
|
| 367 | - * @static |
|
| 368 | - * @param integer $number Number to get its ordinal value |
|
| 369 | - * @return string Ordinal representation of given string. |
|
| 370 | - */ |
|
| 371 | - public static function ordinalize($number) |
|
| 372 | - { |
|
| 373 | - if (in_array(($number % 100), range(11, 13))) { |
|
| 374 | - return $number . 'th'; |
|
| 375 | - } else { |
|
| 376 | - switch (($number % 10)) { |
|
| 377 | - case 1: |
|
| 378 | - return $number . 'st'; |
|
| 379 | - break; |
|
| 380 | - case 2: |
|
| 381 | - return $number . 'nd'; |
|
| 382 | - break; |
|
| 383 | - case 3: |
|
| 384 | - return $number . 'rd'; |
|
| 385 | - default: |
|
| 386 | - return $number . 'th'; |
|
| 387 | - break; |
|
| 388 | - } |
|
| 389 | - } |
|
| 390 | - } |
|
| 391 | - |
|
| 392 | - |
|
| 393 | - /** |
|
| 394 | - * @param $string |
|
| 395 | - * @return string |
|
| 396 | - */ |
|
| 397 | - public static function add_indefinite_article($string) |
|
| 398 | - { |
|
| 399 | - if (strtolower($string) === 'null') { |
|
| 400 | - return $string; |
|
| 401 | - } |
|
| 402 | - return (stripos('aeiou', $string[0]) !== false ? 'an ' : 'a ') . $string; |
|
| 403 | - } |
|
| 36 | + /** |
|
| 37 | + * Just calls self::pluralize and strtolower on $word and returns it |
|
| 38 | + * |
|
| 39 | + * @param string $word |
|
| 40 | + * @return string |
|
| 41 | + */ |
|
| 42 | + public static function pluralize_and_lower($word) |
|
| 43 | + { |
|
| 44 | + return strtolower(self::pluralize($word)); |
|
| 45 | + } |
|
| 46 | + |
|
| 47 | + |
|
| 48 | + /** |
|
| 49 | + * @param string $word |
|
| 50 | + * @return mixed |
|
| 51 | + */ |
|
| 52 | + public static function singularize_and_upper($word) |
|
| 53 | + { |
|
| 54 | + return str_replace(' ', '_', self::humanize(self::singularize($word), 'all')); |
|
| 55 | + } |
|
| 56 | + |
|
| 57 | + |
|
| 58 | + /** |
|
| 59 | + * Pluralizes English nouns. |
|
| 60 | + * |
|
| 61 | + * @access public |
|
| 62 | + * @static |
|
| 63 | + * @param string $word English noun to pluralize |
|
| 64 | + * @return string Plural noun |
|
| 65 | + */ |
|
| 66 | + public static function pluralize($word) |
|
| 67 | + { |
|
| 68 | + $plural = [ |
|
| 69 | + '/(quiz)$/i' => '\1zes', |
|
| 70 | + '/^(ox)$/i' => '\1en', |
|
| 71 | + '/([m|l])ouse$/i' => '\1ice', |
|
| 72 | + '/(matr|vert|ind)ix|ex$/i' => '\1ices', |
|
| 73 | + '/(x|ch|ss|sh)$/i' => '\1es', |
|
| 74 | + '/([^aeiouy]|qu)ies$/i' => '\1y', |
|
| 75 | + '/([^aeiouy]|qu)y$/i' => '\1ies', |
|
| 76 | + '/(hive)$/i' => '\1s', |
|
| 77 | + '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves', |
|
| 78 | + '/sis$/i' => 'ses', |
|
| 79 | + '/([ti])um$/i' => '\1a', |
|
| 80 | + '/(buffal|tomat)o$/i' => '\1oes', |
|
| 81 | + '/(bu)s$/i' => '\1ses', |
|
| 82 | + '/(alias|status)/i' => '\1es', |
|
| 83 | + '/(octop|vir)us$/i' => '\1i', |
|
| 84 | + '/(ax|test)is$/i' => '\1es', |
|
| 85 | + '/s$/i' => 's', |
|
| 86 | + '/$/' => 's', |
|
| 87 | + ]; |
|
| 88 | + |
|
| 89 | + $uncountable = ['equipment', 'information', 'rice', 'money', 'species', 'series', 'fish', 'sheep']; |
|
| 90 | + |
|
| 91 | + $irregular = [ |
|
| 92 | + 'person' => 'people', |
|
| 93 | + 'man' => 'men', |
|
| 94 | + 'child' => 'children', |
|
| 95 | + 'sex' => 'sexes', |
|
| 96 | + 'move' => 'moves', |
|
| 97 | + ]; |
|
| 98 | + |
|
| 99 | + $lowercased_word = strtolower($word); |
|
| 100 | + |
|
| 101 | + foreach ($uncountable as $_uncountable) { |
|
| 102 | + // even though the word "price" ends in "rice", it can be pluralized, |
|
| 103 | + // so check the previous character isn't a letter |
|
| 104 | + if ( |
|
| 105 | + substr($lowercased_word, (-1 * strlen($_uncountable))) == $_uncountable |
|
| 106 | + && ! ctype_alpha($lowercased_word[ strlen($lowercased_word) - strlen($_uncountable) ]) |
|
| 107 | + ) { |
|
| 108 | + return $word; |
|
| 109 | + } |
|
| 110 | + } |
|
| 111 | + |
|
| 112 | + foreach ($irregular as $_plural => $_singular) { |
|
| 113 | + if (preg_match('/(' . $_plural . ')$/i', $word, $arr)) { |
|
| 114 | + return preg_replace('/(' . $_plural . ')$/i', substr($arr[0], 0, 1) . substr($_singular, 1), $word); |
|
| 115 | + } |
|
| 116 | + } |
|
| 117 | + |
|
| 118 | + foreach ($plural as $rule => $replacement) { |
|
| 119 | + if (preg_match($rule, $word)) { |
|
| 120 | + return preg_replace($rule, $replacement, $word); |
|
| 121 | + } |
|
| 122 | + } |
|
| 123 | + return false; |
|
| 124 | + } |
|
| 125 | + |
|
| 126 | + |
|
| 127 | + /** |
|
| 128 | + * Singularizes English nouns. |
|
| 129 | + * |
|
| 130 | + * @access public |
|
| 131 | + * @static |
|
| 132 | + * @param string $word English noun to singularize |
|
| 133 | + * @return string Singular noun. |
|
| 134 | + */ |
|
| 135 | + public static function singularize($word) |
|
| 136 | + { |
|
| 137 | + $singular = [ |
|
| 138 | + '/(quiz)zes$/i' => '\1', |
|
| 139 | + '/(matr)ices$/i' => '\1ix', |
|
| 140 | + '/(vert|ind)ices$/i' => '\1ex', |
|
| 141 | + '/^(ox)en/i' => '\1', |
|
| 142 | + '/(alias|status)es$/i' => '\1', |
|
| 143 | + '/([octop|vir])i$/i' => '\1us', |
|
| 144 | + '/(cris|ax|test)es$/i' => '\1is', |
|
| 145 | + '/(shoe)s$/i' => '\1', |
|
| 146 | + '/(o)es$/i' => '\1', |
|
| 147 | + '/(bus)es$/i' => '\1', |
|
| 148 | + '/([m|l])ice$/i' => '\1ouse', |
|
| 149 | + '/(x|ch|ss|sh)es$/i' => '\1', |
|
| 150 | + '/(m)ovies$/i' => '\1ovie', |
|
| 151 | + '/(s)eries$/i' => '\1eries', |
|
| 152 | + '/([^aeiouy]|qu)ies$/i' => '\1y', |
|
| 153 | + '/([lr])ves$/i' => '\1f', |
|
| 154 | + '/(tive)s$/i' => '\1', |
|
| 155 | + '/(hive)s$/i' => '\1', |
|
| 156 | + '/([^f])ves$/i' => '\1fe', |
|
| 157 | + '/(^analy)ses$/i' => '\1sis', |
|
| 158 | + '/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis', |
|
| 159 | + '/([ti])a$/i' => '\1um', |
|
| 160 | + '/(n)ews$/i' => '\1ews', |
|
| 161 | + '/s$/i' => '', |
|
| 162 | + ]; |
|
| 163 | + |
|
| 164 | + $uncountable = ['equipment', 'information', 'rice', 'money', 'species', 'series', 'fish', 'sheep']; |
|
| 165 | + |
|
| 166 | + $irregular = [ |
|
| 167 | + 'person' => 'people', |
|
| 168 | + 'man' => 'men', |
|
| 169 | + 'child' => 'children', |
|
| 170 | + 'sex' => 'sexes', |
|
| 171 | + 'move' => 'moves', |
|
| 172 | + ]; |
|
| 173 | + |
|
| 174 | + $lowercased_word = strtolower($word); |
|
| 175 | + foreach ($uncountable as $_uncountable) { |
|
| 176 | + if (substr($lowercased_word, (-1 * strlen($_uncountable))) == $_uncountable) { |
|
| 177 | + return $word; |
|
| 178 | + } |
|
| 179 | + } |
|
| 180 | + |
|
| 181 | + foreach ($irregular as $_plural => $_singular) { |
|
| 182 | + if (preg_match('/(' . $_singular . ')$/i', $word, $arr)) { |
|
| 183 | + return preg_replace('/(' . $_singular . ')$/i', substr($arr[0], 0, 1) . substr($_plural, 1), $word); |
|
| 184 | + } |
|
| 185 | + } |
|
| 186 | + |
|
| 187 | + foreach ($singular as $rule => $replacement) { |
|
| 188 | + if (preg_match($rule, $word)) { |
|
| 189 | + return preg_replace($rule, $replacement, $word); |
|
| 190 | + } |
|
| 191 | + } |
|
| 192 | + |
|
| 193 | + return $word; |
|
| 194 | + } |
|
| 195 | + |
|
| 196 | + |
|
| 197 | + /** |
|
| 198 | + * Converts an underscored or CamelCase word into a English |
|
| 199 | + * sentence. |
|
| 200 | + * |
|
| 201 | + * The titleize static function converts text like "WelcomePage", |
|
| 202 | + * "welcome_page" or "welcome page" to this "Welcome |
|
| 203 | + * Page". |
|
| 204 | + * If second parameter is set to 'first' it will only |
|
| 205 | + * capitalize the first character of the title. |
|
| 206 | + * |
|
| 207 | + * @access public |
|
| 208 | + * @static |
|
| 209 | + * @param string $word Word to format as tile |
|
| 210 | + * @param string $uppercase If set to 'first' it will only uppercase the |
|
| 211 | + * first character. Otherwise it will uppercase all |
|
| 212 | + * the words in the title. |
|
| 213 | + * @return string Text formatted as title |
|
| 214 | + */ |
|
| 215 | + public static function titleize($word, $uppercase = '') |
|
| 216 | + { |
|
| 217 | + $uppercase = $uppercase === 'first' ? 'ucfirst' : 'ucwords'; |
|
| 218 | + return $uppercase(EEH_Inflector::humanize(EEH_Inflector::underscore($word))); |
|
| 219 | + } |
|
| 220 | + |
|
| 221 | + |
|
| 222 | + /** |
|
| 223 | + * Returns given word as CamelCased |
|
| 224 | + * |
|
| 225 | + * Converts a word like "send_email" to "SendEmail". It |
|
| 226 | + * will remove non alphanumeric character from the word, so |
|
| 227 | + * "who's online" will be converted to "WhoSOnline" |
|
| 228 | + * |
|
| 229 | + * @access public |
|
| 230 | + * @static |
|
| 231 | + * @param string $word Word to convert to camel case |
|
| 232 | + * @return string UpperCamelCasedWord |
|
| 233 | + * @see variablize |
|
| 234 | + */ |
|
| 235 | + public static function camelize($word) |
|
| 236 | + { |
|
| 237 | + return str_replace(' ', '', ucwords(preg_replace('/[^A-Z^a-z^0-9]+/', ' ', $word))); |
|
| 238 | + } |
|
| 239 | + |
|
| 240 | + |
|
| 241 | + /** |
|
| 242 | + * Camelizes all but the first word. This is handy converting a method which followed EE4 legacy naming convention |
|
| 243 | + * with the new PSR-based naming conventions |
|
| 244 | + * |
|
| 245 | + * @param $word |
|
| 246 | + * @return string |
|
| 247 | + */ |
|
| 248 | + public static function camelize_all_but_first($word) |
|
| 249 | + { |
|
| 250 | + return lcfirst(EEH_Inflector::camelize($word)); |
|
| 251 | + } |
|
| 252 | + |
|
| 253 | + |
|
| 254 | + /** |
|
| 255 | + * Converts a word "into_it_s_underscored_version" |
|
| 256 | + * |
|
| 257 | + * Convert any "CamelCased" or "ordinary Word" into an |
|
| 258 | + * "underscored_word". |
|
| 259 | + * |
|
| 260 | + * This can be really useful for creating friendly URLs. |
|
| 261 | + * |
|
| 262 | + * @access public |
|
| 263 | + * @static |
|
| 264 | + * @param string $word Word to underscore |
|
| 265 | + * @return string Underscored word |
|
| 266 | + */ |
|
| 267 | + public static function underscore($word) |
|
| 268 | + { |
|
| 269 | + return strtolower( |
|
| 270 | + preg_replace( |
|
| 271 | + '/[^A-Z^a-z^0-9]+/', |
|
| 272 | + '_', |
|
| 273 | + preg_replace('/([a-zd])([A-Z])/', '1_2', preg_replace('/([A-Z]+)([A-Z][a-z])/', '1_2', $word)) |
|
| 274 | + ) |
|
| 275 | + ); |
|
| 276 | + } |
|
| 277 | + |
|
| 278 | + |
|
| 279 | + /** |
|
| 280 | + * Returns a human-readable string from $word |
|
| 281 | + * |
|
| 282 | + * Returns a human-readable string from $word, by replacing |
|
| 283 | + * underscores with a space, and by upper-casing the initial |
|
| 284 | + * character by default. |
|
| 285 | + * |
|
| 286 | + * If you need to uppercase all the words you just have to |
|
| 287 | + * pass 'all' as a second parameter. |
|
| 288 | + * |
|
| 289 | + * @access public |
|
| 290 | + * @static |
|
| 291 | + * @param string $word String to "humanize" |
|
| 292 | + * @param string $uppercase If set to 'all' it will uppercase all the words |
|
| 293 | + * instead of just the first one. |
|
| 294 | + * @return string Human-readable word |
|
| 295 | + */ |
|
| 296 | + public static function humanize($word, $uppercase = '') |
|
| 297 | + { |
|
| 298 | + // make special exceptions for acronyms |
|
| 299 | + $word = str_replace('wp_', 'WP_', $word); |
|
| 300 | + $uppercase = $uppercase === 'all' ? 'ucwords' : 'ucfirst'; |
|
| 301 | + return $uppercase(str_replace('_', ' ', preg_replace('/_id$/', '', $word))); |
|
| 302 | + } |
|
| 303 | + |
|
| 304 | + |
|
| 305 | + /** |
|
| 306 | + * Same as camelize but first char is underscored |
|
| 307 | + * |
|
| 308 | + * Converts a word like "send_email" to "sendEmail". It |
|
| 309 | + * will remove non alphanumeric character from the word, so |
|
| 310 | + * "who's online" will be converted to "whoSOnline" |
|
| 311 | + * |
|
| 312 | + * @access public |
|
| 313 | + * @static |
|
| 314 | + * @param string $word Word to lowerCamelCase |
|
| 315 | + * @return string Returns a lowerCamelCasedWord |
|
| 316 | + * @see camelize |
|
| 317 | + */ |
|
| 318 | + public static function variablize($word) |
|
| 319 | + { |
|
| 320 | + $word = EEH_Inflector::camelize($word); |
|
| 321 | + return strtolower($word[0]) . substr($word, 1); |
|
| 322 | + } |
|
| 323 | + |
|
| 324 | + |
|
| 325 | + /** |
|
| 326 | + * Converts a class name to its table name according to rails |
|
| 327 | + * naming conventions. |
|
| 328 | + * |
|
| 329 | + * Converts "Person" to "people" |
|
| 330 | + * |
|
| 331 | + * @access public |
|
| 332 | + * @static |
|
| 333 | + * @param string $class_name Class name for getting related table_name. |
|
| 334 | + * @return string plural_table_name |
|
| 335 | + * @see classify |
|
| 336 | + */ |
|
| 337 | + public static function tableize($class_name) |
|
| 338 | + { |
|
| 339 | + return EEH_Inflector::pluralize(EEH_Inflector::underscore($class_name)); |
|
| 340 | + } |
|
| 341 | + |
|
| 342 | + |
|
| 343 | + /** |
|
| 344 | + * Converts a table name to its class name according to rails |
|
| 345 | + * naming conventions. |
|
| 346 | + * |
|
| 347 | + * Converts "people" to "Person" |
|
| 348 | + * |
|
| 349 | + * @access public |
|
| 350 | + * @static |
|
| 351 | + * @param string $table_name Table name for getting related ClassName. |
|
| 352 | + * @return string SingularClassName |
|
| 353 | + * @see tableize |
|
| 354 | + */ |
|
| 355 | + public static function classify($table_name) |
|
| 356 | + { |
|
| 357 | + return EEH_Inflector::camelize(EEH_Inflector::singularize($table_name)); |
|
| 358 | + } |
|
| 359 | + |
|
| 360 | + |
|
| 361 | + /** |
|
| 362 | + * Converts number to its ordinal English form. |
|
| 363 | + * |
|
| 364 | + * This method converts 13 to 13th, 2 to 2nd ... |
|
| 365 | + * |
|
| 366 | + * @access public |
|
| 367 | + * @static |
|
| 368 | + * @param integer $number Number to get its ordinal value |
|
| 369 | + * @return string Ordinal representation of given string. |
|
| 370 | + */ |
|
| 371 | + public static function ordinalize($number) |
|
| 372 | + { |
|
| 373 | + if (in_array(($number % 100), range(11, 13))) { |
|
| 374 | + return $number . 'th'; |
|
| 375 | + } else { |
|
| 376 | + switch (($number % 10)) { |
|
| 377 | + case 1: |
|
| 378 | + return $number . 'st'; |
|
| 379 | + break; |
|
| 380 | + case 2: |
|
| 381 | + return $number . 'nd'; |
|
| 382 | + break; |
|
| 383 | + case 3: |
|
| 384 | + return $number . 'rd'; |
|
| 385 | + default: |
|
| 386 | + return $number . 'th'; |
|
| 387 | + break; |
|
| 388 | + } |
|
| 389 | + } |
|
| 390 | + } |
|
| 391 | + |
|
| 392 | + |
|
| 393 | + /** |
|
| 394 | + * @param $string |
|
| 395 | + * @return string |
|
| 396 | + */ |
|
| 397 | + public static function add_indefinite_article($string) |
|
| 398 | + { |
|
| 399 | + if (strtolower($string) === 'null') { |
|
| 400 | + return $string; |
|
| 401 | + } |
|
| 402 | + return (stripos('aeiou', $string[0]) !== false ? 'an ' : 'a ') . $string; |
|
| 403 | + } |
|
| 404 | 404 | } |
@@ -24,745 +24,745 @@ |
||
| 24 | 24 | */ |
| 25 | 25 | class EEH_File extends EEH_Base implements EEHI_File |
| 26 | 26 | { |
| 27 | - /** |
|
| 28 | - * @var string $_credentials_form |
|
| 29 | - */ |
|
| 30 | - private static $_credentials_form; |
|
| 31 | - |
|
| 32 | - protected static $_wp_filesystem_direct; |
|
| 33 | - |
|
| 34 | - |
|
| 35 | - /** |
|
| 36 | - * @param string|null $filepath the filepath we want to work in. If its in the |
|
| 37 | - * wp uploads directory, we'll want to just use the filesystem directly. |
|
| 38 | - * If not provided, we have to assume its not in the uploads directory |
|
| 39 | - * @return WP_Filesystem_Base |
|
| 40 | - * @throws EE_Error if filesystem credentials are required |
|
| 41 | - */ |
|
| 42 | - private static function _get_wp_filesystem($filepath = null) |
|
| 43 | - { |
|
| 44 | - if ( |
|
| 45 | - apply_filters( |
|
| 46 | - 'FHEE__EEH_File___get_wp_filesystem__allow_using_filesystem_direct', |
|
| 47 | - $filepath && EEH_File::is_in_uploads_folder($filepath), |
|
| 48 | - $filepath |
|
| 49 | - ) |
|
| 50 | - ) { |
|
| 51 | - if (! EEH_File::$_wp_filesystem_direct instanceof WP_Filesystem_Direct) { |
|
| 52 | - require_once(ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php'); |
|
| 53 | - $method = 'direct'; |
|
| 54 | - $wp_filesystem_direct_file = |
|
| 55 | - apply_filters( |
|
| 56 | - 'filesystem_method_file', |
|
| 57 | - ABSPATH . 'wp-admin/includes/class-wp-filesystem-' . $method . '.php', |
|
| 58 | - $method |
|
| 59 | - ); |
|
| 60 | - // check constants defined, just like in wp-admin/includes/file.php's WP_Filesystem() |
|
| 61 | - if (! defined('FS_CHMOD_DIR')) { |
|
| 62 | - define('FS_CHMOD_DIR', (fileperms(ABSPATH) & 0777 | 0755)); |
|
| 63 | - } |
|
| 64 | - if (! defined('FS_CHMOD_FILE')) { |
|
| 65 | - define('FS_CHMOD_FILE', (fileperms(ABSPATH . 'index.php') & 0777 | 0644)); |
|
| 66 | - } |
|
| 67 | - require_once($wp_filesystem_direct_file); |
|
| 68 | - EEH_File::$_wp_filesystem_direct = new WP_Filesystem_Direct([]); |
|
| 69 | - } |
|
| 70 | - return EEH_File::$_wp_filesystem_direct; |
|
| 71 | - } |
|
| 72 | - global $wp_filesystem; |
|
| 73 | - // no filesystem setup ??? |
|
| 74 | - if (! $wp_filesystem instanceof WP_Filesystem_Base) { |
|
| 75 | - // if some eager beaver's just trying to get in there too early... |
|
| 76 | - // let them do it, because we are one of those eager beavers! :P |
|
| 77 | - /** |
|
| 78 | - * more explanations are probably merited. http://codex.wordpress.org/Filesystem_API#Initializing_WP_Filesystem_Base |
|
| 79 | - * says WP_Filesystem should be used after 'wp_loaded', but currently EE's activation process |
|
| 80 | - * is setup to mostly happen on 'init', and refactoring to have it happen on |
|
| 81 | - * 'wp_loaded' is too much work on a BETA milestone. |
|
| 82 | - * So this fix is expected to work if the WP files are owned by the server user, |
|
| 83 | - * but probably not if the user needs to enter their FTP credentials to modify files |
|
| 84 | - * and there may be troubles if the WP files are owned by a different user |
|
| 85 | - * than the server user. But both of these issues should exist in 4.4 and earlier too |
|
| 86 | - */ |
|
| 87 | - if (false && ! did_action('wp_loaded')) { |
|
| 88 | - $msg = esc_html__( |
|
| 89 | - 'An attempt to access and/or write to a file on the server could not be completed due to a lack of sufficient credentials.', |
|
| 90 | - 'event_espresso' |
|
| 91 | - ); |
|
| 92 | - if (WP_DEBUG) { |
|
| 93 | - $msg .= '<br />'; |
|
| 94 | - $msg .= esc_html__( |
|
| 95 | - 'The WP Filesystem can not be accessed until after the "wp_loaded" hook has run, so it\'s best not to attempt access until the "admin_init" hookpoint.', |
|
| 96 | - 'event_espresso' |
|
| 97 | - ); |
|
| 98 | - } |
|
| 99 | - throw new EE_Error($msg); |
|
| 100 | - } else { |
|
| 101 | - // should be loaded if we are past the wp_loaded hook... |
|
| 102 | - if (! function_exists('WP_Filesystem')) { |
|
| 103 | - require_once(ABSPATH . 'wp-admin/includes/file.php'); |
|
| 104 | - require_once(ABSPATH . 'wp-admin/includes/template.php'); |
|
| 105 | - } |
|
| 106 | - // turn on output buffering so that we can capture the credentials form |
|
| 107 | - ob_start(); |
|
| 108 | - $credentials = request_filesystem_credentials(''); |
|
| 109 | - // store credentials form for the time being |
|
| 110 | - EEH_File::$_credentials_form = ob_get_clean(); |
|
| 111 | - // basically check for direct or previously configured access |
|
| 112 | - if (! WP_Filesystem($credentials)) { |
|
| 113 | - // if credentials do NOT exist |
|
| 114 | - if ($credentials === false) { |
|
| 115 | - add_action('admin_notices', ['EEH_File', 'display_request_filesystem_credentials_form'], 999); |
|
| 116 | - throw new EE_Error( |
|
| 117 | - esc_html__( |
|
| 118 | - 'An attempt to access and/or write to a file on the server could not be completed due to a lack of sufficient credentials.', |
|
| 119 | - 'event_espresso' |
|
| 120 | - ) |
|
| 121 | - ); |
|
| 122 | - } elseif (is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code()) { |
|
| 123 | - add_action('admin_notices', ['EEH_File', 'display_request_filesystem_credentials_form'], 999); |
|
| 124 | - throw new EE_Error( |
|
| 125 | - sprintf( |
|
| 126 | - esc_html__('WP Filesystem Error: $1%s', 'event_espresso'), |
|
| 127 | - $wp_filesystem->errors->get_error_message() |
|
| 128 | - ) |
|
| 129 | - ); |
|
| 130 | - } |
|
| 131 | - } |
|
| 132 | - } |
|
| 133 | - } |
|
| 134 | - return $wp_filesystem; |
|
| 135 | - } |
|
| 136 | - |
|
| 137 | - |
|
| 138 | - /** |
|
| 139 | - * display_request_filesystem_credentials_form |
|
| 140 | - */ |
|
| 141 | - public static function display_request_filesystem_credentials_form() |
|
| 142 | - { |
|
| 143 | - if (! empty(EEH_File::$_credentials_form)) { |
|
| 144 | - echo '<div class="updated espresso-notices-attention"><p>' . EEH_File::$_credentials_form . '</p></div>'; |
|
| 145 | - } |
|
| 146 | - } |
|
| 147 | - |
|
| 148 | - |
|
| 149 | - /** |
|
| 150 | - * verify_filepath_and_permissions |
|
| 151 | - * checks that a file is readable and has sufficient file permissions set to access |
|
| 152 | - * |
|
| 153 | - * @access public |
|
| 154 | - * @param string $full_file_path - full server path to the folder or file |
|
| 155 | - * @param string $file_name - name of file if checking a file |
|
| 156 | - * @param string $file_ext - file extension (ie: "php") if checking a file |
|
| 157 | - * @param string $type_of_file - general type of file (ie: "module"), this is only used to improve error messages |
|
| 158 | - * @return bool |
|
| 159 | - * @throws EE_Error if filesystem credentials are required |
|
| 160 | - */ |
|
| 161 | - public static function verify_filepath_and_permissions( |
|
| 162 | - $full_file_path = '', |
|
| 163 | - $file_name = '', |
|
| 164 | - $file_ext = '', |
|
| 165 | - $type_of_file = '' |
|
| 166 | - ) { |
|
| 167 | - // load WP_Filesystem and set file permissions |
|
| 168 | - $wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path); |
|
| 169 | - $full_file_path = EEH_File::standardise_directory_separators($full_file_path); |
|
| 170 | - if (! $wp_filesystem->is_readable(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path))) { |
|
| 171 | - $file_name = ! empty($type_of_file) ? $file_name . ' ' . $type_of_file : $file_name; |
|
| 172 | - $file_name .= ! empty($file_ext) ? ' file' : ' folder'; |
|
| 173 | - $msg = sprintf( |
|
| 174 | - esc_html__( |
|
| 175 | - 'The requested %1$s could not be found or is not readable, possibly due to an incorrect filepath, or incorrect file permissions.%2$s', |
|
| 176 | - 'event_espresso' |
|
| 177 | - ), |
|
| 178 | - $file_name, |
|
| 179 | - '<br />' |
|
| 180 | - ); |
|
| 181 | - if (EEH_File::exists($full_file_path)) { |
|
| 182 | - $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_file_path, $type_of_file); |
|
| 183 | - } else { |
|
| 184 | - // no file permissions means the file was not found |
|
| 185 | - $msg .= sprintf( |
|
| 186 | - esc_html__('Please ensure the following path is correct: "%s".', 'event_espresso'), |
|
| 187 | - $full_file_path |
|
| 188 | - ); |
|
| 189 | - } |
|
| 190 | - if (defined('WP_DEBUG') && WP_DEBUG) { |
|
| 191 | - throw new EE_Error($msg . '||' . $msg); |
|
| 192 | - } |
|
| 193 | - return false; |
|
| 194 | - } |
|
| 195 | - return true; |
|
| 196 | - } |
|
| 197 | - |
|
| 198 | - |
|
| 199 | - /** |
|
| 200 | - * _permissions_error_for_unreadable_filepath - attempts to determine why permissions are set incorrectly for a |
|
| 201 | - * file or folder |
|
| 202 | - * |
|
| 203 | - * @access private |
|
| 204 | - * @param string $full_file_path - full server path to the folder or file |
|
| 205 | - * @param string $type_of_file - general type of file (ie: "module"), this is only used to improve error messages |
|
| 206 | - * @return string |
|
| 207 | - * @throws EE_Error if filesystem credentials are required |
|
| 208 | - */ |
|
| 209 | - private static function _permissions_error_for_unreadable_filepath($full_file_path = '', $type_of_file = '') |
|
| 210 | - { |
|
| 211 | - // load WP_Filesystem and set file permissions |
|
| 212 | - $wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path); |
|
| 213 | - // check file permissions |
|
| 214 | - $perms = $wp_filesystem->getchmod(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path)); |
|
| 215 | - if ($perms) { |
|
| 216 | - // file permissions exist, but way be set incorrectly |
|
| 217 | - $type_of_file = ! empty($type_of_file) ? $type_of_file . ' ' : ''; |
|
| 218 | - $type_of_file .= ! empty($type_of_file) ? 'file' : 'folder'; |
|
| 219 | - return sprintf( |
|
| 220 | - esc_html__( |
|
| 221 | - 'File permissions for the requested %1$s are currently set at "%2$s". The recommended permissions are 644 for files and 755 for folders.', |
|
| 222 | - 'event_espresso' |
|
| 223 | - ), |
|
| 224 | - $type_of_file, |
|
| 225 | - $perms |
|
| 226 | - ); |
|
| 227 | - } |
|
| 228 | - // file exists but file permissions could not be read ?!?! |
|
| 229 | - return sprintf( |
|
| 230 | - esc_html__( |
|
| 231 | - 'Please ensure that the server and/or PHP configuration allows the current process to access the following file: "%s".', |
|
| 232 | - 'event_espresso' |
|
| 233 | - ), |
|
| 234 | - $full_file_path |
|
| 235 | - ); |
|
| 236 | - } |
|
| 237 | - |
|
| 238 | - |
|
| 239 | - /** |
|
| 240 | - * ensure_folder_exists_and_is_writable |
|
| 241 | - * ensures that a folder exists and is writable, will attempt to create folder if it does not exist |
|
| 242 | - * Also ensures all the parent folders exist, and if not tries to create them. |
|
| 243 | - * Also, if this function creates the folder, adds a .htaccess file and index.html file |
|
| 244 | - * |
|
| 245 | - * @param string $folder |
|
| 246 | - * @return bool false if folder isn't writable; true if it exists and is writeable, |
|
| 247 | - * @throws EE_Error if the folder exists and is writeable, but for some reason we |
|
| 248 | - * can't write to it |
|
| 249 | - */ |
|
| 250 | - public static function ensure_folder_exists_and_is_writable($folder = '') |
|
| 251 | - { |
|
| 252 | - if (empty($folder)) { |
|
| 253 | - return false; |
|
| 254 | - } |
|
| 255 | - // remove ending / |
|
| 256 | - $folder = EEH_File::standardise_directory_separators(rtrim($folder, '/\\')); |
|
| 257 | - $parent_folder = EEH_File::get_parent_folder($folder); |
|
| 258 | - // add / to folder |
|
| 259 | - $folder = EEH_File::end_with_directory_separator($folder); |
|
| 260 | - $wp_filesystem = EEH_File::_get_wp_filesystem($folder); |
|
| 261 | - if (! $wp_filesystem->is_dir(EEH_File::convert_local_filepath_to_remote_filepath($folder))) { |
|
| 262 | - // ok so it doesn't exist. Does its parent? Can we write to it? |
|
| 263 | - if (! EEH_File::ensure_folder_exists_and_is_writable($parent_folder)) { |
|
| 264 | - return false; |
|
| 265 | - } |
|
| 266 | - if (! EEH_File::verify_is_writable($parent_folder)) { |
|
| 267 | - return false; |
|
| 268 | - } else { |
|
| 269 | - if (! $wp_filesystem->mkdir(EEH_File::convert_local_filepath_to_remote_filepath($folder))) { |
|
| 270 | - if (defined('WP_DEBUG') && WP_DEBUG) { |
|
| 271 | - $msg = sprintf(esc_html__('"%s" could not be created.', 'event_espresso'), $folder); |
|
| 272 | - $msg .= EEH_File::_permissions_error_for_unreadable_filepath($folder); |
|
| 273 | - throw new EE_Error($msg); |
|
| 274 | - } |
|
| 275 | - return false; |
|
| 276 | - } |
|
| 277 | - EEH_File::add_index_file($folder); |
|
| 278 | - } |
|
| 279 | - } elseif (! EEH_File::verify_is_writable($folder)) { |
|
| 280 | - return false; |
|
| 281 | - } |
|
| 282 | - return true; |
|
| 283 | - } |
|
| 284 | - |
|
| 285 | - |
|
| 286 | - /** |
|
| 287 | - * verify_is_writable - checks if a file or folder is writable |
|
| 288 | - * |
|
| 289 | - * @param string $full_path - full server path to file or folder |
|
| 290 | - * @param string $file_or_folder - whether checking a file or folder |
|
| 291 | - * @return bool |
|
| 292 | - * @throws EE_Error if filesystem credentials are required |
|
| 293 | - */ |
|
| 294 | - public static function verify_is_writable($full_path = '', $file_or_folder = 'folder') |
|
| 295 | - { |
|
| 296 | - // load WP_Filesystem and set file permissions |
|
| 297 | - $wp_filesystem = EEH_File::_get_wp_filesystem($full_path); |
|
| 298 | - $full_path = EEH_File::standardise_directory_separators($full_path); |
|
| 299 | - if (! $wp_filesystem->is_writable(EEH_File::convert_local_filepath_to_remote_filepath($full_path))) { |
|
| 300 | - if (defined('WP_DEBUG') && WP_DEBUG) { |
|
| 301 | - $msg = sprintf( |
|
| 302 | - esc_html__('The "%1$s" %2$s is not writable.', 'event_espresso'), |
|
| 303 | - $full_path, |
|
| 304 | - $file_or_folder |
|
| 305 | - ); |
|
| 306 | - $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_path); |
|
| 307 | - throw new EE_Error($msg); |
|
| 308 | - } |
|
| 309 | - return false; |
|
| 310 | - } |
|
| 311 | - return true; |
|
| 312 | - } |
|
| 313 | - |
|
| 314 | - |
|
| 315 | - /** |
|
| 316 | - * ensure_file_exists_and_is_writable |
|
| 317 | - * ensures that a file exists and is writable, will attempt to create file if it does not exist. |
|
| 318 | - * Also ensures all the parent folders exist, and if not tries to create them. |
|
| 319 | - * |
|
| 320 | - * @param string $full_file_path |
|
| 321 | - * @return bool |
|
| 322 | - * @throws EE_Error if filesystem credentials are required |
|
| 323 | - */ |
|
| 324 | - public static function ensure_file_exists_and_is_writable($full_file_path = '') |
|
| 325 | - { |
|
| 326 | - // load WP_Filesystem and set file permissions |
|
| 327 | - $wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path); |
|
| 328 | - $full_file_path = EEH_File::standardise_directory_separators($full_file_path); |
|
| 329 | - $parent_folder = EEH_File::get_parent_folder($full_file_path); |
|
| 330 | - if (! EEH_File::exists($full_file_path)) { |
|
| 331 | - if (! EEH_File::ensure_folder_exists_and_is_writable($parent_folder)) { |
|
| 332 | - return false; |
|
| 333 | - } |
|
| 334 | - if (! $wp_filesystem->touch(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path))) { |
|
| 335 | - if (defined('WP_DEBUG') && WP_DEBUG) { |
|
| 336 | - $msg = |
|
| 337 | - sprintf(esc_html__('The "%s" file could not be created.', 'event_espresso'), $full_file_path); |
|
| 338 | - $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_file_path); |
|
| 339 | - throw new EE_Error($msg); |
|
| 340 | - } |
|
| 341 | - return false; |
|
| 342 | - } |
|
| 343 | - } |
|
| 344 | - if (! EEH_File::verify_is_writable($full_file_path, 'file')) { |
|
| 345 | - return false; |
|
| 346 | - } |
|
| 347 | - return true; |
|
| 348 | - } |
|
| 349 | - |
|
| 350 | - |
|
| 351 | - /** |
|
| 352 | - * Gets the parent folder. If provided with file, gets the folder that contains it. |
|
| 353 | - * If provided a folder, gets its parent folder. |
|
| 354 | - * |
|
| 355 | - * @param string $file_or_folder_path |
|
| 356 | - * @return string parent folder, ENDING with a directory separator |
|
| 357 | - */ |
|
| 358 | - public static function get_parent_folder($file_or_folder_path) |
|
| 359 | - { |
|
| 360 | - // find the last /, ignoring a / on the very end |
|
| 361 | - // eg if given "/var/something/somewhere/", we want to get "somewhere"'s |
|
| 362 | - // parent folder, "/var/something/" |
|
| 363 | - $ds = strlen($file_or_folder_path) > 1 |
|
| 364 | - ? strrpos($file_or_folder_path, '/', -2) |
|
| 365 | - : strlen($file_or_folder_path); |
|
| 366 | - return substr($file_or_folder_path, 0, $ds + 1); |
|
| 367 | - } |
|
| 368 | - |
|
| 369 | - // public static function ensure_folder_exists_recursively( $folder ) { |
|
| 370 | - // |
|
| 371 | - // } |
|
| 372 | - |
|
| 373 | - |
|
| 374 | - /** |
|
| 375 | - * get_file_contents |
|
| 376 | - * |
|
| 377 | - * @param string $full_file_path |
|
| 378 | - * @return string |
|
| 379 | - * @throws EE_Error if filesystem credentials are required |
|
| 380 | - */ |
|
| 381 | - public static function get_file_contents($full_file_path = '') |
|
| 382 | - { |
|
| 383 | - $full_file_path = EEH_File::standardise_directory_separators($full_file_path); |
|
| 384 | - if ( |
|
| 385 | - EEH_File::verify_filepath_and_permissions( |
|
| 386 | - $full_file_path, |
|
| 387 | - EEH_File::get_filename_from_filepath($full_file_path), |
|
| 388 | - EEH_File::get_file_extension($full_file_path) |
|
| 389 | - ) |
|
| 390 | - ) { |
|
| 391 | - // load WP_Filesystem and set file permissions |
|
| 392 | - $wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path); |
|
| 393 | - return $wp_filesystem->get_contents(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path)); |
|
| 394 | - } |
|
| 395 | - return ''; |
|
| 396 | - } |
|
| 397 | - |
|
| 398 | - |
|
| 399 | - /** |
|
| 400 | - * write_file |
|
| 401 | - * |
|
| 402 | - * @param string $full_file_path |
|
| 403 | - * @param string $file_contents - the content to be written to the file |
|
| 404 | - * @param string $file_type |
|
| 405 | - * @return bool |
|
| 406 | - * @throws EE_Error if filesystem credentials are required |
|
| 407 | - */ |
|
| 408 | - public static function write_to_file($full_file_path = '', $file_contents = '', $file_type = '') |
|
| 409 | - { |
|
| 410 | - $full_file_path = EEH_File::standardise_directory_separators($full_file_path); |
|
| 411 | - $file_type = ! empty($file_type) ? rtrim($file_type, ' ') . ' ' : ''; |
|
| 412 | - $folder = EEH_File::remove_filename_from_filepath($full_file_path); |
|
| 413 | - if (! EEH_File::verify_is_writable($folder)) { |
|
| 414 | - if (defined('WP_DEBUG') && WP_DEBUG) { |
|
| 415 | - $msg = sprintf( |
|
| 416 | - esc_html__('The %1$sfile located at "%2$s" is not writable.', 'event_espresso'), |
|
| 417 | - $file_type, |
|
| 418 | - $full_file_path |
|
| 419 | - ); |
|
| 420 | - $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_file_path); |
|
| 421 | - throw new EE_Error($msg); |
|
| 422 | - } |
|
| 423 | - return false; |
|
| 424 | - } |
|
| 425 | - // load WP_Filesystem and set file permissions |
|
| 426 | - $wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path); |
|
| 427 | - // write the file |
|
| 428 | - if ( |
|
| 429 | - ! $wp_filesystem->put_contents( |
|
| 430 | - EEH_File::convert_local_filepath_to_remote_filepath($full_file_path), |
|
| 431 | - $file_contents |
|
| 432 | - ) |
|
| 433 | - ) { |
|
| 434 | - if (defined('WP_DEBUG') && WP_DEBUG) { |
|
| 435 | - $msg = sprintf( |
|
| 436 | - esc_html__('The %1$sfile located at "%2$s" could not be written to.', 'event_espresso'), |
|
| 437 | - $file_type, |
|
| 438 | - $full_file_path |
|
| 439 | - ); |
|
| 440 | - $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_file_path, 'f'); |
|
| 441 | - throw new EE_Error($msg); |
|
| 442 | - } |
|
| 443 | - return false; |
|
| 444 | - } |
|
| 445 | - return true; |
|
| 446 | - } |
|
| 447 | - |
|
| 448 | - |
|
| 449 | - /** |
|
| 450 | - * Wrapper for WP_Filesystem_Base::delete |
|
| 451 | - * |
|
| 452 | - * @param string $filepath |
|
| 453 | - * @param boolean $recursive |
|
| 454 | - * @param boolean|string $type 'd' for directory, 'f' for file |
|
| 455 | - * @return boolean |
|
| 456 | - * @throws EE_Error if filesystem credentials are required |
|
| 457 | - */ |
|
| 458 | - public static function delete($filepath, $recursive = false, $type = false) |
|
| 459 | - { |
|
| 460 | - $wp_filesystem = EEH_File::_get_wp_filesystem(); |
|
| 461 | - return $wp_filesystem->delete($filepath, $recursive, $type); |
|
| 462 | - } |
|
| 463 | - |
|
| 464 | - |
|
| 465 | - /** |
|
| 466 | - * exists |
|
| 467 | - * checks if a file exists using the WP filesystem |
|
| 468 | - * |
|
| 469 | - * @param string $full_file_path |
|
| 470 | - * @return bool |
|
| 471 | - * @throws EE_Error if filesystem credentials are required |
|
| 472 | - */ |
|
| 473 | - public static function exists($full_file_path = '') |
|
| 474 | - { |
|
| 475 | - $wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path); |
|
| 476 | - return $wp_filesystem->exists(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path)); |
|
| 477 | - } |
|
| 478 | - |
|
| 479 | - |
|
| 480 | - /** |
|
| 481 | - * is_readable |
|
| 482 | - * checks if a file is_readable using the WP filesystem |
|
| 483 | - * |
|
| 484 | - * @param string $full_file_path |
|
| 485 | - * @return bool |
|
| 486 | - * @throws EE_Error if filesystem credentials are required |
|
| 487 | - */ |
|
| 488 | - public static function is_readable($full_file_path = '') |
|
| 489 | - { |
|
| 490 | - $wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path); |
|
| 491 | - return $wp_filesystem->is_readable(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path)); |
|
| 492 | - } |
|
| 493 | - |
|
| 494 | - |
|
| 495 | - /** |
|
| 496 | - * remove_filename_from_filepath |
|
| 497 | - * given a full path to a file including the filename itself, this removes the filename and returns the path, up |
|
| 498 | - * to, but NOT including the filename OR slash |
|
| 499 | - * |
|
| 500 | - * @param string $full_file_path |
|
| 501 | - * @return string |
|
| 502 | - */ |
|
| 503 | - public static function remove_filename_from_filepath($full_file_path = '') |
|
| 504 | - { |
|
| 505 | - return pathinfo($full_file_path, PATHINFO_DIRNAME); |
|
| 506 | - } |
|
| 507 | - |
|
| 508 | - |
|
| 509 | - /** |
|
| 510 | - * get_filename_from_filepath. Arguably the same as basename() |
|
| 511 | - * |
|
| 512 | - * @param string $full_file_path |
|
| 513 | - * @return string |
|
| 514 | - */ |
|
| 515 | - public static function get_filename_from_filepath($full_file_path = '') |
|
| 516 | - { |
|
| 517 | - return pathinfo($full_file_path, PATHINFO_BASENAME); |
|
| 518 | - } |
|
| 519 | - |
|
| 520 | - |
|
| 521 | - /** |
|
| 522 | - * get_file_extension |
|
| 523 | - * |
|
| 524 | - * @param string $full_file_path |
|
| 525 | - * @return string |
|
| 526 | - */ |
|
| 527 | - public static function get_file_extension($full_file_path = '') |
|
| 528 | - { |
|
| 529 | - return pathinfo($full_file_path, PATHINFO_EXTENSION); |
|
| 530 | - } |
|
| 531 | - |
|
| 532 | - |
|
| 533 | - /** |
|
| 534 | - * add_htaccess_deny_from_all so the webserver cannot access this folder |
|
| 535 | - * |
|
| 536 | - * @param string $folder |
|
| 537 | - * @return bool |
|
| 538 | - * @throws EE_Error if filesystem credentials are required |
|
| 539 | - */ |
|
| 540 | - public static function add_htaccess_deny_from_all($folder = '') |
|
| 541 | - { |
|
| 542 | - $folder = EEH_File::standardise_and_end_with_directory_separator($folder); |
|
| 543 | - if (! EEH_File::exists($folder . '.htaccess')) { |
|
| 544 | - if (! EEH_File::write_to_file($folder . '.htaccess', 'deny from all', '.htaccess')) { |
|
| 545 | - return false; |
|
| 546 | - } |
|
| 547 | - } |
|
| 548 | - return true; |
|
| 549 | - } |
|
| 550 | - |
|
| 551 | - |
|
| 552 | - /** |
|
| 553 | - * Adds an index file to this folder, so folks can't list all the file's contents |
|
| 554 | - * |
|
| 555 | - * @param string $folder |
|
| 556 | - * @return boolean |
|
| 557 | - * @throws EE_Error if filesystem credentials are required |
|
| 558 | - */ |
|
| 559 | - public static function add_index_file($folder) |
|
| 560 | - { |
|
| 561 | - $folder = EEH_File::standardise_and_end_with_directory_separator($folder); |
|
| 562 | - if (! EEH_File::exists($folder . 'index.php')) { |
|
| 563 | - if ( |
|
| 564 | - ! EEH_File::write_to_file( |
|
| 565 | - $folder . 'index.php', |
|
| 566 | - 'You are not permitted to read from this folder', |
|
| 567 | - '.php' |
|
| 568 | - ) |
|
| 569 | - ) { |
|
| 570 | - return false; |
|
| 571 | - } |
|
| 572 | - } |
|
| 573 | - return true; |
|
| 574 | - } |
|
| 575 | - |
|
| 576 | - |
|
| 577 | - /** |
|
| 578 | - * Given that the file in $file_path has the normal name, (ie, CLASSNAME.whatever.php), |
|
| 579 | - * extract that classname. |
|
| 580 | - * |
|
| 581 | - * @param string $file_path |
|
| 582 | - * @return string |
|
| 583 | - */ |
|
| 584 | - public static function get_classname_from_filepath_with_standard_filename($file_path) |
|
| 585 | - { |
|
| 586 | - // extract file from path |
|
| 587 | - $filename = basename($file_path); |
|
| 588 | - // now remove the first period and everything after |
|
| 589 | - $pos_of_first_period = strpos($filename, '.'); |
|
| 590 | - return substr($filename, 0, $pos_of_first_period); |
|
| 591 | - } |
|
| 592 | - |
|
| 593 | - |
|
| 594 | - /** |
|
| 595 | - * standardise_directory_separators |
|
| 596 | - * convert all directory separators in a file path. |
|
| 597 | - * |
|
| 598 | - * @param string $file_path |
|
| 599 | - * @return string |
|
| 600 | - */ |
|
| 601 | - public static function standardise_directory_separators($file_path) |
|
| 602 | - { |
|
| 603 | - return str_replace(['\\', '/'], '/', $file_path); |
|
| 604 | - } |
|
| 605 | - |
|
| 606 | - |
|
| 607 | - /** |
|
| 608 | - * end_with_directory_separator |
|
| 609 | - * ensures that file path ends with '/' |
|
| 610 | - * |
|
| 611 | - * @param string $file_path |
|
| 612 | - * @return string |
|
| 613 | - */ |
|
| 614 | - public static function end_with_directory_separator($file_path) |
|
| 615 | - { |
|
| 616 | - return rtrim($file_path, '/\\') . '/'; |
|
| 617 | - } |
|
| 618 | - |
|
| 619 | - |
|
| 620 | - /** |
|
| 621 | - * shorthand for both EEH_FIle::end_with_directory_separator AND EEH_File::standardise_directory_separators |
|
| 622 | - * |
|
| 623 | - * @param $file_path |
|
| 624 | - * @return string |
|
| 625 | - */ |
|
| 626 | - public static function standardise_and_end_with_directory_separator($file_path) |
|
| 627 | - { |
|
| 628 | - return self::end_with_directory_separator(self::standardise_directory_separators($file_path)); |
|
| 629 | - } |
|
| 630 | - |
|
| 631 | - |
|
| 632 | - /** |
|
| 633 | - * takes the folder name (with or without trailing slash) and finds the files it in, |
|
| 634 | - * and what the class's name inside of each should be. |
|
| 635 | - * |
|
| 636 | - * @param array $folder_paths |
|
| 637 | - * @param boolean $index_numerically if TRUE, the returned array will be indexed numerically; |
|
| 638 | - * if FALSE (Default), returned array will be indexed by the filenames minus |
|
| 639 | - * extensions. Set it TRUE if you know there are files in the directory with the |
|
| 640 | - * same name but different extensions |
|
| 641 | - * @return array if $index_numerically == TRUE keys are numeric , |
|
| 642 | - * if $index_numerically == FALSE (Default) keys are what the class names SHOULD |
|
| 643 | - * be; and values are their filepaths |
|
| 644 | - */ |
|
| 645 | - public static function get_contents_of_folders($folder_paths = [], $index_numerically = false) |
|
| 646 | - { |
|
| 647 | - $class_to_folder_path = []; |
|
| 648 | - foreach ($folder_paths as $folder_path) { |
|
| 649 | - $folder_path = self::standardise_and_end_with_directory_separator($folder_path); |
|
| 650 | - // load WP_Filesystem and set file permissions |
|
| 651 | - $files_in_folder = glob($folder_path . '*.php'); |
|
| 652 | - $class_to_folder_path = []; |
|
| 653 | - if ($files_in_folder) { |
|
| 654 | - foreach ($files_in_folder as $file_path) { |
|
| 655 | - // only add files, not folders |
|
| 656 | - if (! is_dir($file_path)) { |
|
| 657 | - if ($index_numerically) { |
|
| 658 | - $class_to_folder_path[] = $file_path; |
|
| 659 | - } else { |
|
| 660 | - $classname = |
|
| 661 | - self::get_classname_from_filepath_with_standard_filename($file_path); |
|
| 662 | - $class_to_folder_path[ $classname ] = $file_path; |
|
| 663 | - } |
|
| 664 | - } |
|
| 665 | - } |
|
| 666 | - } |
|
| 667 | - } |
|
| 668 | - return $class_to_folder_path; |
|
| 669 | - } |
|
| 670 | - |
|
| 671 | - |
|
| 672 | - /** |
|
| 673 | - * Copies a file. Mostly a wrapper of WP_Filesystem::copy |
|
| 674 | - * |
|
| 675 | - * @param string $source_file |
|
| 676 | - * @param string $destination_file |
|
| 677 | - * @param boolean $overwrite |
|
| 678 | - * @return boolean success |
|
| 679 | - * @throws EE_Error if filesystem credentials are required |
|
| 680 | - */ |
|
| 681 | - public static function copy($source_file, $destination_file, $overwrite = false) |
|
| 682 | - { |
|
| 683 | - $full_source_path = EEH_File::standardise_directory_separators($source_file); |
|
| 684 | - if (! EEH_File::exists($full_source_path)) { |
|
| 685 | - if (defined('WP_DEBUG') && WP_DEBUG) { |
|
| 686 | - $msg = sprintf( |
|
| 687 | - esc_html__('The file located at "%2$s" is not readable or doesn\'t exist.', 'event_espresso'), |
|
| 688 | - $full_source_path |
|
| 689 | - ); |
|
| 690 | - $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_source_path); |
|
| 691 | - throw new EE_Error($msg); |
|
| 692 | - } |
|
| 693 | - return false; |
|
| 694 | - } |
|
| 695 | - |
|
| 696 | - $full_dest_path = EEH_File::standardise_directory_separators($destination_file); |
|
| 697 | - $folder = EEH_File::remove_filename_from_filepath($full_dest_path); |
|
| 698 | - EEH_File::ensure_folder_exists_and_is_writable($folder); |
|
| 699 | - if (! EEH_File::verify_is_writable($folder)) { |
|
| 700 | - if (defined('WP_DEBUG') && WP_DEBUG) { |
|
| 701 | - $msg = sprintf( |
|
| 702 | - esc_html__('The file located at "%2$s" is not writable.', 'event_espresso'), |
|
| 703 | - $full_dest_path |
|
| 704 | - ); |
|
| 705 | - $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_dest_path); |
|
| 706 | - throw new EE_Error($msg); |
|
| 707 | - } |
|
| 708 | - return false; |
|
| 709 | - } |
|
| 710 | - |
|
| 711 | - // load WP_Filesystem and set file permissions |
|
| 712 | - $wp_filesystem = EEH_File::_get_wp_filesystem($destination_file); |
|
| 713 | - // write the file |
|
| 714 | - if ( |
|
| 715 | - ! $wp_filesystem->copy( |
|
| 716 | - EEH_File::convert_local_filepath_to_remote_filepath($full_source_path), |
|
| 717 | - EEH_File::convert_local_filepath_to_remote_filepath($full_dest_path), |
|
| 718 | - $overwrite |
|
| 719 | - ) |
|
| 720 | - ) { |
|
| 721 | - if (defined('WP_DEBUG') && WP_DEBUG) { |
|
| 722 | - $msg = |
|
| 723 | - sprintf( |
|
| 724 | - esc_html__( |
|
| 725 | - 'Attempted writing to file %1$s, but could not, probably because of permissions issues', |
|
| 726 | - 'event_espresso' |
|
| 727 | - ), |
|
| 728 | - $full_source_path |
|
| 729 | - ); |
|
| 730 | - $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_source_path, 'f'); |
|
| 731 | - throw new EE_Error($msg); |
|
| 732 | - } |
|
| 733 | - return false; |
|
| 734 | - } |
|
| 735 | - return true; |
|
| 736 | - } |
|
| 737 | - |
|
| 738 | - |
|
| 739 | - /** |
|
| 740 | - * Reports whether or not the filepath is in the EE uploads folder or not |
|
| 741 | - * |
|
| 742 | - * @param string $filepath |
|
| 743 | - * @return boolean |
|
| 744 | - */ |
|
| 745 | - public static function is_in_uploads_folder($filepath) |
|
| 746 | - { |
|
| 747 | - $uploads = wp_upload_dir(); |
|
| 748 | - return strpos($filepath, $uploads['basedir']) === 0; |
|
| 749 | - } |
|
| 750 | - |
|
| 751 | - |
|
| 752 | - /** |
|
| 753 | - * Given a "local" filepath (what you probably thought was the only filepath), |
|
| 754 | - * converts it into a "remote" filepath (the filepath the currently-in-use |
|
| 755 | - * $wp_filesystem needs to use access the folder or file). |
|
| 756 | - * See http://wordpress.stackexchange.com/questions/124900/using-wp-filesystem-in-plugins |
|
| 757 | - * |
|
| 758 | - * @param string $local_filepath the filepath to the folder/file locally |
|
| 759 | - * @return string the remote filepath (eg the filepath the filesystem method, eg |
|
| 760 | - * ftp or ssh, will use to access the folder |
|
| 761 | - * @throws EE_Error if filesystem credentials are required |
|
| 762 | - */ |
|
| 763 | - public static function convert_local_filepath_to_remote_filepath($local_filepath) |
|
| 764 | - { |
|
| 765 | - $wp_filesystem = EEH_File::_get_wp_filesystem($local_filepath); |
|
| 766 | - return str_replace(WP_CONTENT_DIR . '/', $wp_filesystem->wp_content_dir(), $local_filepath); |
|
| 767 | - } |
|
| 27 | + /** |
|
| 28 | + * @var string $_credentials_form |
|
| 29 | + */ |
|
| 30 | + private static $_credentials_form; |
|
| 31 | + |
|
| 32 | + protected static $_wp_filesystem_direct; |
|
| 33 | + |
|
| 34 | + |
|
| 35 | + /** |
|
| 36 | + * @param string|null $filepath the filepath we want to work in. If its in the |
|
| 37 | + * wp uploads directory, we'll want to just use the filesystem directly. |
|
| 38 | + * If not provided, we have to assume its not in the uploads directory |
|
| 39 | + * @return WP_Filesystem_Base |
|
| 40 | + * @throws EE_Error if filesystem credentials are required |
|
| 41 | + */ |
|
| 42 | + private static function _get_wp_filesystem($filepath = null) |
|
| 43 | + { |
|
| 44 | + if ( |
|
| 45 | + apply_filters( |
|
| 46 | + 'FHEE__EEH_File___get_wp_filesystem__allow_using_filesystem_direct', |
|
| 47 | + $filepath && EEH_File::is_in_uploads_folder($filepath), |
|
| 48 | + $filepath |
|
| 49 | + ) |
|
| 50 | + ) { |
|
| 51 | + if (! EEH_File::$_wp_filesystem_direct instanceof WP_Filesystem_Direct) { |
|
| 52 | + require_once(ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php'); |
|
| 53 | + $method = 'direct'; |
|
| 54 | + $wp_filesystem_direct_file = |
|
| 55 | + apply_filters( |
|
| 56 | + 'filesystem_method_file', |
|
| 57 | + ABSPATH . 'wp-admin/includes/class-wp-filesystem-' . $method . '.php', |
|
| 58 | + $method |
|
| 59 | + ); |
|
| 60 | + // check constants defined, just like in wp-admin/includes/file.php's WP_Filesystem() |
|
| 61 | + if (! defined('FS_CHMOD_DIR')) { |
|
| 62 | + define('FS_CHMOD_DIR', (fileperms(ABSPATH) & 0777 | 0755)); |
|
| 63 | + } |
|
| 64 | + if (! defined('FS_CHMOD_FILE')) { |
|
| 65 | + define('FS_CHMOD_FILE', (fileperms(ABSPATH . 'index.php') & 0777 | 0644)); |
|
| 66 | + } |
|
| 67 | + require_once($wp_filesystem_direct_file); |
|
| 68 | + EEH_File::$_wp_filesystem_direct = new WP_Filesystem_Direct([]); |
|
| 69 | + } |
|
| 70 | + return EEH_File::$_wp_filesystem_direct; |
|
| 71 | + } |
|
| 72 | + global $wp_filesystem; |
|
| 73 | + // no filesystem setup ??? |
|
| 74 | + if (! $wp_filesystem instanceof WP_Filesystem_Base) { |
|
| 75 | + // if some eager beaver's just trying to get in there too early... |
|
| 76 | + // let them do it, because we are one of those eager beavers! :P |
|
| 77 | + /** |
|
| 78 | + * more explanations are probably merited. http://codex.wordpress.org/Filesystem_API#Initializing_WP_Filesystem_Base |
|
| 79 | + * says WP_Filesystem should be used after 'wp_loaded', but currently EE's activation process |
|
| 80 | + * is setup to mostly happen on 'init', and refactoring to have it happen on |
|
| 81 | + * 'wp_loaded' is too much work on a BETA milestone. |
|
| 82 | + * So this fix is expected to work if the WP files are owned by the server user, |
|
| 83 | + * but probably not if the user needs to enter their FTP credentials to modify files |
|
| 84 | + * and there may be troubles if the WP files are owned by a different user |
|
| 85 | + * than the server user. But both of these issues should exist in 4.4 and earlier too |
|
| 86 | + */ |
|
| 87 | + if (false && ! did_action('wp_loaded')) { |
|
| 88 | + $msg = esc_html__( |
|
| 89 | + 'An attempt to access and/or write to a file on the server could not be completed due to a lack of sufficient credentials.', |
|
| 90 | + 'event_espresso' |
|
| 91 | + ); |
|
| 92 | + if (WP_DEBUG) { |
|
| 93 | + $msg .= '<br />'; |
|
| 94 | + $msg .= esc_html__( |
|
| 95 | + 'The WP Filesystem can not be accessed until after the "wp_loaded" hook has run, so it\'s best not to attempt access until the "admin_init" hookpoint.', |
|
| 96 | + 'event_espresso' |
|
| 97 | + ); |
|
| 98 | + } |
|
| 99 | + throw new EE_Error($msg); |
|
| 100 | + } else { |
|
| 101 | + // should be loaded if we are past the wp_loaded hook... |
|
| 102 | + if (! function_exists('WP_Filesystem')) { |
|
| 103 | + require_once(ABSPATH . 'wp-admin/includes/file.php'); |
|
| 104 | + require_once(ABSPATH . 'wp-admin/includes/template.php'); |
|
| 105 | + } |
|
| 106 | + // turn on output buffering so that we can capture the credentials form |
|
| 107 | + ob_start(); |
|
| 108 | + $credentials = request_filesystem_credentials(''); |
|
| 109 | + // store credentials form for the time being |
|
| 110 | + EEH_File::$_credentials_form = ob_get_clean(); |
|
| 111 | + // basically check for direct or previously configured access |
|
| 112 | + if (! WP_Filesystem($credentials)) { |
|
| 113 | + // if credentials do NOT exist |
|
| 114 | + if ($credentials === false) { |
|
| 115 | + add_action('admin_notices', ['EEH_File', 'display_request_filesystem_credentials_form'], 999); |
|
| 116 | + throw new EE_Error( |
|
| 117 | + esc_html__( |
|
| 118 | + 'An attempt to access and/or write to a file on the server could not be completed due to a lack of sufficient credentials.', |
|
| 119 | + 'event_espresso' |
|
| 120 | + ) |
|
| 121 | + ); |
|
| 122 | + } elseif (is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code()) { |
|
| 123 | + add_action('admin_notices', ['EEH_File', 'display_request_filesystem_credentials_form'], 999); |
|
| 124 | + throw new EE_Error( |
|
| 125 | + sprintf( |
|
| 126 | + esc_html__('WP Filesystem Error: $1%s', 'event_espresso'), |
|
| 127 | + $wp_filesystem->errors->get_error_message() |
|
| 128 | + ) |
|
| 129 | + ); |
|
| 130 | + } |
|
| 131 | + } |
|
| 132 | + } |
|
| 133 | + } |
|
| 134 | + return $wp_filesystem; |
|
| 135 | + } |
|
| 136 | + |
|
| 137 | + |
|
| 138 | + /** |
|
| 139 | + * display_request_filesystem_credentials_form |
|
| 140 | + */ |
|
| 141 | + public static function display_request_filesystem_credentials_form() |
|
| 142 | + { |
|
| 143 | + if (! empty(EEH_File::$_credentials_form)) { |
|
| 144 | + echo '<div class="updated espresso-notices-attention"><p>' . EEH_File::$_credentials_form . '</p></div>'; |
|
| 145 | + } |
|
| 146 | + } |
|
| 147 | + |
|
| 148 | + |
|
| 149 | + /** |
|
| 150 | + * verify_filepath_and_permissions |
|
| 151 | + * checks that a file is readable and has sufficient file permissions set to access |
|
| 152 | + * |
|
| 153 | + * @access public |
|
| 154 | + * @param string $full_file_path - full server path to the folder or file |
|
| 155 | + * @param string $file_name - name of file if checking a file |
|
| 156 | + * @param string $file_ext - file extension (ie: "php") if checking a file |
|
| 157 | + * @param string $type_of_file - general type of file (ie: "module"), this is only used to improve error messages |
|
| 158 | + * @return bool |
|
| 159 | + * @throws EE_Error if filesystem credentials are required |
|
| 160 | + */ |
|
| 161 | + public static function verify_filepath_and_permissions( |
|
| 162 | + $full_file_path = '', |
|
| 163 | + $file_name = '', |
|
| 164 | + $file_ext = '', |
|
| 165 | + $type_of_file = '' |
|
| 166 | + ) { |
|
| 167 | + // load WP_Filesystem and set file permissions |
|
| 168 | + $wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path); |
|
| 169 | + $full_file_path = EEH_File::standardise_directory_separators($full_file_path); |
|
| 170 | + if (! $wp_filesystem->is_readable(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path))) { |
|
| 171 | + $file_name = ! empty($type_of_file) ? $file_name . ' ' . $type_of_file : $file_name; |
|
| 172 | + $file_name .= ! empty($file_ext) ? ' file' : ' folder'; |
|
| 173 | + $msg = sprintf( |
|
| 174 | + esc_html__( |
|
| 175 | + 'The requested %1$s could not be found or is not readable, possibly due to an incorrect filepath, or incorrect file permissions.%2$s', |
|
| 176 | + 'event_espresso' |
|
| 177 | + ), |
|
| 178 | + $file_name, |
|
| 179 | + '<br />' |
|
| 180 | + ); |
|
| 181 | + if (EEH_File::exists($full_file_path)) { |
|
| 182 | + $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_file_path, $type_of_file); |
|
| 183 | + } else { |
|
| 184 | + // no file permissions means the file was not found |
|
| 185 | + $msg .= sprintf( |
|
| 186 | + esc_html__('Please ensure the following path is correct: "%s".', 'event_espresso'), |
|
| 187 | + $full_file_path |
|
| 188 | + ); |
|
| 189 | + } |
|
| 190 | + if (defined('WP_DEBUG') && WP_DEBUG) { |
|
| 191 | + throw new EE_Error($msg . '||' . $msg); |
|
| 192 | + } |
|
| 193 | + return false; |
|
| 194 | + } |
|
| 195 | + return true; |
|
| 196 | + } |
|
| 197 | + |
|
| 198 | + |
|
| 199 | + /** |
|
| 200 | + * _permissions_error_for_unreadable_filepath - attempts to determine why permissions are set incorrectly for a |
|
| 201 | + * file or folder |
|
| 202 | + * |
|
| 203 | + * @access private |
|
| 204 | + * @param string $full_file_path - full server path to the folder or file |
|
| 205 | + * @param string $type_of_file - general type of file (ie: "module"), this is only used to improve error messages |
|
| 206 | + * @return string |
|
| 207 | + * @throws EE_Error if filesystem credentials are required |
|
| 208 | + */ |
|
| 209 | + private static function _permissions_error_for_unreadable_filepath($full_file_path = '', $type_of_file = '') |
|
| 210 | + { |
|
| 211 | + // load WP_Filesystem and set file permissions |
|
| 212 | + $wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path); |
|
| 213 | + // check file permissions |
|
| 214 | + $perms = $wp_filesystem->getchmod(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path)); |
|
| 215 | + if ($perms) { |
|
| 216 | + // file permissions exist, but way be set incorrectly |
|
| 217 | + $type_of_file = ! empty($type_of_file) ? $type_of_file . ' ' : ''; |
|
| 218 | + $type_of_file .= ! empty($type_of_file) ? 'file' : 'folder'; |
|
| 219 | + return sprintf( |
|
| 220 | + esc_html__( |
|
| 221 | + 'File permissions for the requested %1$s are currently set at "%2$s". The recommended permissions are 644 for files and 755 for folders.', |
|
| 222 | + 'event_espresso' |
|
| 223 | + ), |
|
| 224 | + $type_of_file, |
|
| 225 | + $perms |
|
| 226 | + ); |
|
| 227 | + } |
|
| 228 | + // file exists but file permissions could not be read ?!?! |
|
| 229 | + return sprintf( |
|
| 230 | + esc_html__( |
|
| 231 | + 'Please ensure that the server and/or PHP configuration allows the current process to access the following file: "%s".', |
|
| 232 | + 'event_espresso' |
|
| 233 | + ), |
|
| 234 | + $full_file_path |
|
| 235 | + ); |
|
| 236 | + } |
|
| 237 | + |
|
| 238 | + |
|
| 239 | + /** |
|
| 240 | + * ensure_folder_exists_and_is_writable |
|
| 241 | + * ensures that a folder exists and is writable, will attempt to create folder if it does not exist |
|
| 242 | + * Also ensures all the parent folders exist, and if not tries to create them. |
|
| 243 | + * Also, if this function creates the folder, adds a .htaccess file and index.html file |
|
| 244 | + * |
|
| 245 | + * @param string $folder |
|
| 246 | + * @return bool false if folder isn't writable; true if it exists and is writeable, |
|
| 247 | + * @throws EE_Error if the folder exists and is writeable, but for some reason we |
|
| 248 | + * can't write to it |
|
| 249 | + */ |
|
| 250 | + public static function ensure_folder_exists_and_is_writable($folder = '') |
|
| 251 | + { |
|
| 252 | + if (empty($folder)) { |
|
| 253 | + return false; |
|
| 254 | + } |
|
| 255 | + // remove ending / |
|
| 256 | + $folder = EEH_File::standardise_directory_separators(rtrim($folder, '/\\')); |
|
| 257 | + $parent_folder = EEH_File::get_parent_folder($folder); |
|
| 258 | + // add / to folder |
|
| 259 | + $folder = EEH_File::end_with_directory_separator($folder); |
|
| 260 | + $wp_filesystem = EEH_File::_get_wp_filesystem($folder); |
|
| 261 | + if (! $wp_filesystem->is_dir(EEH_File::convert_local_filepath_to_remote_filepath($folder))) { |
|
| 262 | + // ok so it doesn't exist. Does its parent? Can we write to it? |
|
| 263 | + if (! EEH_File::ensure_folder_exists_and_is_writable($parent_folder)) { |
|
| 264 | + return false; |
|
| 265 | + } |
|
| 266 | + if (! EEH_File::verify_is_writable($parent_folder)) { |
|
| 267 | + return false; |
|
| 268 | + } else { |
|
| 269 | + if (! $wp_filesystem->mkdir(EEH_File::convert_local_filepath_to_remote_filepath($folder))) { |
|
| 270 | + if (defined('WP_DEBUG') && WP_DEBUG) { |
|
| 271 | + $msg = sprintf(esc_html__('"%s" could not be created.', 'event_espresso'), $folder); |
|
| 272 | + $msg .= EEH_File::_permissions_error_for_unreadable_filepath($folder); |
|
| 273 | + throw new EE_Error($msg); |
|
| 274 | + } |
|
| 275 | + return false; |
|
| 276 | + } |
|
| 277 | + EEH_File::add_index_file($folder); |
|
| 278 | + } |
|
| 279 | + } elseif (! EEH_File::verify_is_writable($folder)) { |
|
| 280 | + return false; |
|
| 281 | + } |
|
| 282 | + return true; |
|
| 283 | + } |
|
| 284 | + |
|
| 285 | + |
|
| 286 | + /** |
|
| 287 | + * verify_is_writable - checks if a file or folder is writable |
|
| 288 | + * |
|
| 289 | + * @param string $full_path - full server path to file or folder |
|
| 290 | + * @param string $file_or_folder - whether checking a file or folder |
|
| 291 | + * @return bool |
|
| 292 | + * @throws EE_Error if filesystem credentials are required |
|
| 293 | + */ |
|
| 294 | + public static function verify_is_writable($full_path = '', $file_or_folder = 'folder') |
|
| 295 | + { |
|
| 296 | + // load WP_Filesystem and set file permissions |
|
| 297 | + $wp_filesystem = EEH_File::_get_wp_filesystem($full_path); |
|
| 298 | + $full_path = EEH_File::standardise_directory_separators($full_path); |
|
| 299 | + if (! $wp_filesystem->is_writable(EEH_File::convert_local_filepath_to_remote_filepath($full_path))) { |
|
| 300 | + if (defined('WP_DEBUG') && WP_DEBUG) { |
|
| 301 | + $msg = sprintf( |
|
| 302 | + esc_html__('The "%1$s" %2$s is not writable.', 'event_espresso'), |
|
| 303 | + $full_path, |
|
| 304 | + $file_or_folder |
|
| 305 | + ); |
|
| 306 | + $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_path); |
|
| 307 | + throw new EE_Error($msg); |
|
| 308 | + } |
|
| 309 | + return false; |
|
| 310 | + } |
|
| 311 | + return true; |
|
| 312 | + } |
|
| 313 | + |
|
| 314 | + |
|
| 315 | + /** |
|
| 316 | + * ensure_file_exists_and_is_writable |
|
| 317 | + * ensures that a file exists and is writable, will attempt to create file if it does not exist. |
|
| 318 | + * Also ensures all the parent folders exist, and if not tries to create them. |
|
| 319 | + * |
|
| 320 | + * @param string $full_file_path |
|
| 321 | + * @return bool |
|
| 322 | + * @throws EE_Error if filesystem credentials are required |
|
| 323 | + */ |
|
| 324 | + public static function ensure_file_exists_and_is_writable($full_file_path = '') |
|
| 325 | + { |
|
| 326 | + // load WP_Filesystem and set file permissions |
|
| 327 | + $wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path); |
|
| 328 | + $full_file_path = EEH_File::standardise_directory_separators($full_file_path); |
|
| 329 | + $parent_folder = EEH_File::get_parent_folder($full_file_path); |
|
| 330 | + if (! EEH_File::exists($full_file_path)) { |
|
| 331 | + if (! EEH_File::ensure_folder_exists_and_is_writable($parent_folder)) { |
|
| 332 | + return false; |
|
| 333 | + } |
|
| 334 | + if (! $wp_filesystem->touch(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path))) { |
|
| 335 | + if (defined('WP_DEBUG') && WP_DEBUG) { |
|
| 336 | + $msg = |
|
| 337 | + sprintf(esc_html__('The "%s" file could not be created.', 'event_espresso'), $full_file_path); |
|
| 338 | + $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_file_path); |
|
| 339 | + throw new EE_Error($msg); |
|
| 340 | + } |
|
| 341 | + return false; |
|
| 342 | + } |
|
| 343 | + } |
|
| 344 | + if (! EEH_File::verify_is_writable($full_file_path, 'file')) { |
|
| 345 | + return false; |
|
| 346 | + } |
|
| 347 | + return true; |
|
| 348 | + } |
|
| 349 | + |
|
| 350 | + |
|
| 351 | + /** |
|
| 352 | + * Gets the parent folder. If provided with file, gets the folder that contains it. |
|
| 353 | + * If provided a folder, gets its parent folder. |
|
| 354 | + * |
|
| 355 | + * @param string $file_or_folder_path |
|
| 356 | + * @return string parent folder, ENDING with a directory separator |
|
| 357 | + */ |
|
| 358 | + public static function get_parent_folder($file_or_folder_path) |
|
| 359 | + { |
|
| 360 | + // find the last /, ignoring a / on the very end |
|
| 361 | + // eg if given "/var/something/somewhere/", we want to get "somewhere"'s |
|
| 362 | + // parent folder, "/var/something/" |
|
| 363 | + $ds = strlen($file_or_folder_path) > 1 |
|
| 364 | + ? strrpos($file_or_folder_path, '/', -2) |
|
| 365 | + : strlen($file_or_folder_path); |
|
| 366 | + return substr($file_or_folder_path, 0, $ds + 1); |
|
| 367 | + } |
|
| 368 | + |
|
| 369 | + // public static function ensure_folder_exists_recursively( $folder ) { |
|
| 370 | + // |
|
| 371 | + // } |
|
| 372 | + |
|
| 373 | + |
|
| 374 | + /** |
|
| 375 | + * get_file_contents |
|
| 376 | + * |
|
| 377 | + * @param string $full_file_path |
|
| 378 | + * @return string |
|
| 379 | + * @throws EE_Error if filesystem credentials are required |
|
| 380 | + */ |
|
| 381 | + public static function get_file_contents($full_file_path = '') |
|
| 382 | + { |
|
| 383 | + $full_file_path = EEH_File::standardise_directory_separators($full_file_path); |
|
| 384 | + if ( |
|
| 385 | + EEH_File::verify_filepath_and_permissions( |
|
| 386 | + $full_file_path, |
|
| 387 | + EEH_File::get_filename_from_filepath($full_file_path), |
|
| 388 | + EEH_File::get_file_extension($full_file_path) |
|
| 389 | + ) |
|
| 390 | + ) { |
|
| 391 | + // load WP_Filesystem and set file permissions |
|
| 392 | + $wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path); |
|
| 393 | + return $wp_filesystem->get_contents(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path)); |
|
| 394 | + } |
|
| 395 | + return ''; |
|
| 396 | + } |
|
| 397 | + |
|
| 398 | + |
|
| 399 | + /** |
|
| 400 | + * write_file |
|
| 401 | + * |
|
| 402 | + * @param string $full_file_path |
|
| 403 | + * @param string $file_contents - the content to be written to the file |
|
| 404 | + * @param string $file_type |
|
| 405 | + * @return bool |
|
| 406 | + * @throws EE_Error if filesystem credentials are required |
|
| 407 | + */ |
|
| 408 | + public static function write_to_file($full_file_path = '', $file_contents = '', $file_type = '') |
|
| 409 | + { |
|
| 410 | + $full_file_path = EEH_File::standardise_directory_separators($full_file_path); |
|
| 411 | + $file_type = ! empty($file_type) ? rtrim($file_type, ' ') . ' ' : ''; |
|
| 412 | + $folder = EEH_File::remove_filename_from_filepath($full_file_path); |
|
| 413 | + if (! EEH_File::verify_is_writable($folder)) { |
|
| 414 | + if (defined('WP_DEBUG') && WP_DEBUG) { |
|
| 415 | + $msg = sprintf( |
|
| 416 | + esc_html__('The %1$sfile located at "%2$s" is not writable.', 'event_espresso'), |
|
| 417 | + $file_type, |
|
| 418 | + $full_file_path |
|
| 419 | + ); |
|
| 420 | + $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_file_path); |
|
| 421 | + throw new EE_Error($msg); |
|
| 422 | + } |
|
| 423 | + return false; |
|
| 424 | + } |
|
| 425 | + // load WP_Filesystem and set file permissions |
|
| 426 | + $wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path); |
|
| 427 | + // write the file |
|
| 428 | + if ( |
|
| 429 | + ! $wp_filesystem->put_contents( |
|
| 430 | + EEH_File::convert_local_filepath_to_remote_filepath($full_file_path), |
|
| 431 | + $file_contents |
|
| 432 | + ) |
|
| 433 | + ) { |
|
| 434 | + if (defined('WP_DEBUG') && WP_DEBUG) { |
|
| 435 | + $msg = sprintf( |
|
| 436 | + esc_html__('The %1$sfile located at "%2$s" could not be written to.', 'event_espresso'), |
|
| 437 | + $file_type, |
|
| 438 | + $full_file_path |
|
| 439 | + ); |
|
| 440 | + $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_file_path, 'f'); |
|
| 441 | + throw new EE_Error($msg); |
|
| 442 | + } |
|
| 443 | + return false; |
|
| 444 | + } |
|
| 445 | + return true; |
|
| 446 | + } |
|
| 447 | + |
|
| 448 | + |
|
| 449 | + /** |
|
| 450 | + * Wrapper for WP_Filesystem_Base::delete |
|
| 451 | + * |
|
| 452 | + * @param string $filepath |
|
| 453 | + * @param boolean $recursive |
|
| 454 | + * @param boolean|string $type 'd' for directory, 'f' for file |
|
| 455 | + * @return boolean |
|
| 456 | + * @throws EE_Error if filesystem credentials are required |
|
| 457 | + */ |
|
| 458 | + public static function delete($filepath, $recursive = false, $type = false) |
|
| 459 | + { |
|
| 460 | + $wp_filesystem = EEH_File::_get_wp_filesystem(); |
|
| 461 | + return $wp_filesystem->delete($filepath, $recursive, $type); |
|
| 462 | + } |
|
| 463 | + |
|
| 464 | + |
|
| 465 | + /** |
|
| 466 | + * exists |
|
| 467 | + * checks if a file exists using the WP filesystem |
|
| 468 | + * |
|
| 469 | + * @param string $full_file_path |
|
| 470 | + * @return bool |
|
| 471 | + * @throws EE_Error if filesystem credentials are required |
|
| 472 | + */ |
|
| 473 | + public static function exists($full_file_path = '') |
|
| 474 | + { |
|
| 475 | + $wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path); |
|
| 476 | + return $wp_filesystem->exists(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path)); |
|
| 477 | + } |
|
| 478 | + |
|
| 479 | + |
|
| 480 | + /** |
|
| 481 | + * is_readable |
|
| 482 | + * checks if a file is_readable using the WP filesystem |
|
| 483 | + * |
|
| 484 | + * @param string $full_file_path |
|
| 485 | + * @return bool |
|
| 486 | + * @throws EE_Error if filesystem credentials are required |
|
| 487 | + */ |
|
| 488 | + public static function is_readable($full_file_path = '') |
|
| 489 | + { |
|
| 490 | + $wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path); |
|
| 491 | + return $wp_filesystem->is_readable(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path)); |
|
| 492 | + } |
|
| 493 | + |
|
| 494 | + |
|
| 495 | + /** |
|
| 496 | + * remove_filename_from_filepath |
|
| 497 | + * given a full path to a file including the filename itself, this removes the filename and returns the path, up |
|
| 498 | + * to, but NOT including the filename OR slash |
|
| 499 | + * |
|
| 500 | + * @param string $full_file_path |
|
| 501 | + * @return string |
|
| 502 | + */ |
|
| 503 | + public static function remove_filename_from_filepath($full_file_path = '') |
|
| 504 | + { |
|
| 505 | + return pathinfo($full_file_path, PATHINFO_DIRNAME); |
|
| 506 | + } |
|
| 507 | + |
|
| 508 | + |
|
| 509 | + /** |
|
| 510 | + * get_filename_from_filepath. Arguably the same as basename() |
|
| 511 | + * |
|
| 512 | + * @param string $full_file_path |
|
| 513 | + * @return string |
|
| 514 | + */ |
|
| 515 | + public static function get_filename_from_filepath($full_file_path = '') |
|
| 516 | + { |
|
| 517 | + return pathinfo($full_file_path, PATHINFO_BASENAME); |
|
| 518 | + } |
|
| 519 | + |
|
| 520 | + |
|
| 521 | + /** |
|
| 522 | + * get_file_extension |
|
| 523 | + * |
|
| 524 | + * @param string $full_file_path |
|
| 525 | + * @return string |
|
| 526 | + */ |
|
| 527 | + public static function get_file_extension($full_file_path = '') |
|
| 528 | + { |
|
| 529 | + return pathinfo($full_file_path, PATHINFO_EXTENSION); |
|
| 530 | + } |
|
| 531 | + |
|
| 532 | + |
|
| 533 | + /** |
|
| 534 | + * add_htaccess_deny_from_all so the webserver cannot access this folder |
|
| 535 | + * |
|
| 536 | + * @param string $folder |
|
| 537 | + * @return bool |
|
| 538 | + * @throws EE_Error if filesystem credentials are required |
|
| 539 | + */ |
|
| 540 | + public static function add_htaccess_deny_from_all($folder = '') |
|
| 541 | + { |
|
| 542 | + $folder = EEH_File::standardise_and_end_with_directory_separator($folder); |
|
| 543 | + if (! EEH_File::exists($folder . '.htaccess')) { |
|
| 544 | + if (! EEH_File::write_to_file($folder . '.htaccess', 'deny from all', '.htaccess')) { |
|
| 545 | + return false; |
|
| 546 | + } |
|
| 547 | + } |
|
| 548 | + return true; |
|
| 549 | + } |
|
| 550 | + |
|
| 551 | + |
|
| 552 | + /** |
|
| 553 | + * Adds an index file to this folder, so folks can't list all the file's contents |
|
| 554 | + * |
|
| 555 | + * @param string $folder |
|
| 556 | + * @return boolean |
|
| 557 | + * @throws EE_Error if filesystem credentials are required |
|
| 558 | + */ |
|
| 559 | + public static function add_index_file($folder) |
|
| 560 | + { |
|
| 561 | + $folder = EEH_File::standardise_and_end_with_directory_separator($folder); |
|
| 562 | + if (! EEH_File::exists($folder . 'index.php')) { |
|
| 563 | + if ( |
|
| 564 | + ! EEH_File::write_to_file( |
|
| 565 | + $folder . 'index.php', |
|
| 566 | + 'You are not permitted to read from this folder', |
|
| 567 | + '.php' |
|
| 568 | + ) |
|
| 569 | + ) { |
|
| 570 | + return false; |
|
| 571 | + } |
|
| 572 | + } |
|
| 573 | + return true; |
|
| 574 | + } |
|
| 575 | + |
|
| 576 | + |
|
| 577 | + /** |
|
| 578 | + * Given that the file in $file_path has the normal name, (ie, CLASSNAME.whatever.php), |
|
| 579 | + * extract that classname. |
|
| 580 | + * |
|
| 581 | + * @param string $file_path |
|
| 582 | + * @return string |
|
| 583 | + */ |
|
| 584 | + public static function get_classname_from_filepath_with_standard_filename($file_path) |
|
| 585 | + { |
|
| 586 | + // extract file from path |
|
| 587 | + $filename = basename($file_path); |
|
| 588 | + // now remove the first period and everything after |
|
| 589 | + $pos_of_first_period = strpos($filename, '.'); |
|
| 590 | + return substr($filename, 0, $pos_of_first_period); |
|
| 591 | + } |
|
| 592 | + |
|
| 593 | + |
|
| 594 | + /** |
|
| 595 | + * standardise_directory_separators |
|
| 596 | + * convert all directory separators in a file path. |
|
| 597 | + * |
|
| 598 | + * @param string $file_path |
|
| 599 | + * @return string |
|
| 600 | + */ |
|
| 601 | + public static function standardise_directory_separators($file_path) |
|
| 602 | + { |
|
| 603 | + return str_replace(['\\', '/'], '/', $file_path); |
|
| 604 | + } |
|
| 605 | + |
|
| 606 | + |
|
| 607 | + /** |
|
| 608 | + * end_with_directory_separator |
|
| 609 | + * ensures that file path ends with '/' |
|
| 610 | + * |
|
| 611 | + * @param string $file_path |
|
| 612 | + * @return string |
|
| 613 | + */ |
|
| 614 | + public static function end_with_directory_separator($file_path) |
|
| 615 | + { |
|
| 616 | + return rtrim($file_path, '/\\') . '/'; |
|
| 617 | + } |
|
| 618 | + |
|
| 619 | + |
|
| 620 | + /** |
|
| 621 | + * shorthand for both EEH_FIle::end_with_directory_separator AND EEH_File::standardise_directory_separators |
|
| 622 | + * |
|
| 623 | + * @param $file_path |
|
| 624 | + * @return string |
|
| 625 | + */ |
|
| 626 | + public static function standardise_and_end_with_directory_separator($file_path) |
|
| 627 | + { |
|
| 628 | + return self::end_with_directory_separator(self::standardise_directory_separators($file_path)); |
|
| 629 | + } |
|
| 630 | + |
|
| 631 | + |
|
| 632 | + /** |
|
| 633 | + * takes the folder name (with or without trailing slash) and finds the files it in, |
|
| 634 | + * and what the class's name inside of each should be. |
|
| 635 | + * |
|
| 636 | + * @param array $folder_paths |
|
| 637 | + * @param boolean $index_numerically if TRUE, the returned array will be indexed numerically; |
|
| 638 | + * if FALSE (Default), returned array will be indexed by the filenames minus |
|
| 639 | + * extensions. Set it TRUE if you know there are files in the directory with the |
|
| 640 | + * same name but different extensions |
|
| 641 | + * @return array if $index_numerically == TRUE keys are numeric , |
|
| 642 | + * if $index_numerically == FALSE (Default) keys are what the class names SHOULD |
|
| 643 | + * be; and values are their filepaths |
|
| 644 | + */ |
|
| 645 | + public static function get_contents_of_folders($folder_paths = [], $index_numerically = false) |
|
| 646 | + { |
|
| 647 | + $class_to_folder_path = []; |
|
| 648 | + foreach ($folder_paths as $folder_path) { |
|
| 649 | + $folder_path = self::standardise_and_end_with_directory_separator($folder_path); |
|
| 650 | + // load WP_Filesystem and set file permissions |
|
| 651 | + $files_in_folder = glob($folder_path . '*.php'); |
|
| 652 | + $class_to_folder_path = []; |
|
| 653 | + if ($files_in_folder) { |
|
| 654 | + foreach ($files_in_folder as $file_path) { |
|
| 655 | + // only add files, not folders |
|
| 656 | + if (! is_dir($file_path)) { |
|
| 657 | + if ($index_numerically) { |
|
| 658 | + $class_to_folder_path[] = $file_path; |
|
| 659 | + } else { |
|
| 660 | + $classname = |
|
| 661 | + self::get_classname_from_filepath_with_standard_filename($file_path); |
|
| 662 | + $class_to_folder_path[ $classname ] = $file_path; |
|
| 663 | + } |
|
| 664 | + } |
|
| 665 | + } |
|
| 666 | + } |
|
| 667 | + } |
|
| 668 | + return $class_to_folder_path; |
|
| 669 | + } |
|
| 670 | + |
|
| 671 | + |
|
| 672 | + /** |
|
| 673 | + * Copies a file. Mostly a wrapper of WP_Filesystem::copy |
|
| 674 | + * |
|
| 675 | + * @param string $source_file |
|
| 676 | + * @param string $destination_file |
|
| 677 | + * @param boolean $overwrite |
|
| 678 | + * @return boolean success |
|
| 679 | + * @throws EE_Error if filesystem credentials are required |
|
| 680 | + */ |
|
| 681 | + public static function copy($source_file, $destination_file, $overwrite = false) |
|
| 682 | + { |
|
| 683 | + $full_source_path = EEH_File::standardise_directory_separators($source_file); |
|
| 684 | + if (! EEH_File::exists($full_source_path)) { |
|
| 685 | + if (defined('WP_DEBUG') && WP_DEBUG) { |
|
| 686 | + $msg = sprintf( |
|
| 687 | + esc_html__('The file located at "%2$s" is not readable or doesn\'t exist.', 'event_espresso'), |
|
| 688 | + $full_source_path |
|
| 689 | + ); |
|
| 690 | + $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_source_path); |
|
| 691 | + throw new EE_Error($msg); |
|
| 692 | + } |
|
| 693 | + return false; |
|
| 694 | + } |
|
| 695 | + |
|
| 696 | + $full_dest_path = EEH_File::standardise_directory_separators($destination_file); |
|
| 697 | + $folder = EEH_File::remove_filename_from_filepath($full_dest_path); |
|
| 698 | + EEH_File::ensure_folder_exists_and_is_writable($folder); |
|
| 699 | + if (! EEH_File::verify_is_writable($folder)) { |
|
| 700 | + if (defined('WP_DEBUG') && WP_DEBUG) { |
|
| 701 | + $msg = sprintf( |
|
| 702 | + esc_html__('The file located at "%2$s" is not writable.', 'event_espresso'), |
|
| 703 | + $full_dest_path |
|
| 704 | + ); |
|
| 705 | + $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_dest_path); |
|
| 706 | + throw new EE_Error($msg); |
|
| 707 | + } |
|
| 708 | + return false; |
|
| 709 | + } |
|
| 710 | + |
|
| 711 | + // load WP_Filesystem and set file permissions |
|
| 712 | + $wp_filesystem = EEH_File::_get_wp_filesystem($destination_file); |
|
| 713 | + // write the file |
|
| 714 | + if ( |
|
| 715 | + ! $wp_filesystem->copy( |
|
| 716 | + EEH_File::convert_local_filepath_to_remote_filepath($full_source_path), |
|
| 717 | + EEH_File::convert_local_filepath_to_remote_filepath($full_dest_path), |
|
| 718 | + $overwrite |
|
| 719 | + ) |
|
| 720 | + ) { |
|
| 721 | + if (defined('WP_DEBUG') && WP_DEBUG) { |
|
| 722 | + $msg = |
|
| 723 | + sprintf( |
|
| 724 | + esc_html__( |
|
| 725 | + 'Attempted writing to file %1$s, but could not, probably because of permissions issues', |
|
| 726 | + 'event_espresso' |
|
| 727 | + ), |
|
| 728 | + $full_source_path |
|
| 729 | + ); |
|
| 730 | + $msg .= EEH_File::_permissions_error_for_unreadable_filepath($full_source_path, 'f'); |
|
| 731 | + throw new EE_Error($msg); |
|
| 732 | + } |
|
| 733 | + return false; |
|
| 734 | + } |
|
| 735 | + return true; |
|
| 736 | + } |
|
| 737 | + |
|
| 738 | + |
|
| 739 | + /** |
|
| 740 | + * Reports whether or not the filepath is in the EE uploads folder or not |
|
| 741 | + * |
|
| 742 | + * @param string $filepath |
|
| 743 | + * @return boolean |
|
| 744 | + */ |
|
| 745 | + public static function is_in_uploads_folder($filepath) |
|
| 746 | + { |
|
| 747 | + $uploads = wp_upload_dir(); |
|
| 748 | + return strpos($filepath, $uploads['basedir']) === 0; |
|
| 749 | + } |
|
| 750 | + |
|
| 751 | + |
|
| 752 | + /** |
|
| 753 | + * Given a "local" filepath (what you probably thought was the only filepath), |
|
| 754 | + * converts it into a "remote" filepath (the filepath the currently-in-use |
|
| 755 | + * $wp_filesystem needs to use access the folder or file). |
|
| 756 | + * See http://wordpress.stackexchange.com/questions/124900/using-wp-filesystem-in-plugins |
|
| 757 | + * |
|
| 758 | + * @param string $local_filepath the filepath to the folder/file locally |
|
| 759 | + * @return string the remote filepath (eg the filepath the filesystem method, eg |
|
| 760 | + * ftp or ssh, will use to access the folder |
|
| 761 | + * @throws EE_Error if filesystem credentials are required |
|
| 762 | + */ |
|
| 763 | + public static function convert_local_filepath_to_remote_filepath($local_filepath) |
|
| 764 | + { |
|
| 765 | + $wp_filesystem = EEH_File::_get_wp_filesystem($local_filepath); |
|
| 766 | + return str_replace(WP_CONTENT_DIR . '/', $wp_filesystem->wp_content_dir(), $local_filepath); |
|
| 767 | + } |
|
| 768 | 768 | } |
@@ -48,21 +48,21 @@ discard block |
||
| 48 | 48 | $filepath |
| 49 | 49 | ) |
| 50 | 50 | ) { |
| 51 | - if (! EEH_File::$_wp_filesystem_direct instanceof WP_Filesystem_Direct) { |
|
| 52 | - require_once(ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php'); |
|
| 51 | + if ( ! EEH_File::$_wp_filesystem_direct instanceof WP_Filesystem_Direct) { |
|
| 52 | + require_once(ABSPATH.'wp-admin/includes/class-wp-filesystem-base.php'); |
|
| 53 | 53 | $method = 'direct'; |
| 54 | 54 | $wp_filesystem_direct_file = |
| 55 | 55 | apply_filters( |
| 56 | 56 | 'filesystem_method_file', |
| 57 | - ABSPATH . 'wp-admin/includes/class-wp-filesystem-' . $method . '.php', |
|
| 57 | + ABSPATH.'wp-admin/includes/class-wp-filesystem-'.$method.'.php', |
|
| 58 | 58 | $method |
| 59 | 59 | ); |
| 60 | 60 | // check constants defined, just like in wp-admin/includes/file.php's WP_Filesystem() |
| 61 | - if (! defined('FS_CHMOD_DIR')) { |
|
| 61 | + if ( ! defined('FS_CHMOD_DIR')) { |
|
| 62 | 62 | define('FS_CHMOD_DIR', (fileperms(ABSPATH) & 0777 | 0755)); |
| 63 | 63 | } |
| 64 | - if (! defined('FS_CHMOD_FILE')) { |
|
| 65 | - define('FS_CHMOD_FILE', (fileperms(ABSPATH . 'index.php') & 0777 | 0644)); |
|
| 64 | + if ( ! defined('FS_CHMOD_FILE')) { |
|
| 65 | + define('FS_CHMOD_FILE', (fileperms(ABSPATH.'index.php') & 0777 | 0644)); |
|
| 66 | 66 | } |
| 67 | 67 | require_once($wp_filesystem_direct_file); |
| 68 | 68 | EEH_File::$_wp_filesystem_direct = new WP_Filesystem_Direct([]); |
@@ -71,7 +71,7 @@ discard block |
||
| 71 | 71 | } |
| 72 | 72 | global $wp_filesystem; |
| 73 | 73 | // no filesystem setup ??? |
| 74 | - if (! $wp_filesystem instanceof WP_Filesystem_Base) { |
|
| 74 | + if ( ! $wp_filesystem instanceof WP_Filesystem_Base) { |
|
| 75 | 75 | // if some eager beaver's just trying to get in there too early... |
| 76 | 76 | // let them do it, because we are one of those eager beavers! :P |
| 77 | 77 | /** |
@@ -99,9 +99,9 @@ discard block |
||
| 99 | 99 | throw new EE_Error($msg); |
| 100 | 100 | } else { |
| 101 | 101 | // should be loaded if we are past the wp_loaded hook... |
| 102 | - if (! function_exists('WP_Filesystem')) { |
|
| 103 | - require_once(ABSPATH . 'wp-admin/includes/file.php'); |
|
| 104 | - require_once(ABSPATH . 'wp-admin/includes/template.php'); |
|
| 102 | + if ( ! function_exists('WP_Filesystem')) { |
|
| 103 | + require_once(ABSPATH.'wp-admin/includes/file.php'); |
|
| 104 | + require_once(ABSPATH.'wp-admin/includes/template.php'); |
|
| 105 | 105 | } |
| 106 | 106 | // turn on output buffering so that we can capture the credentials form |
| 107 | 107 | ob_start(); |
@@ -109,7 +109,7 @@ discard block |
||
| 109 | 109 | // store credentials form for the time being |
| 110 | 110 | EEH_File::$_credentials_form = ob_get_clean(); |
| 111 | 111 | // basically check for direct or previously configured access |
| 112 | - if (! WP_Filesystem($credentials)) { |
|
| 112 | + if ( ! WP_Filesystem($credentials)) { |
|
| 113 | 113 | // if credentials do NOT exist |
| 114 | 114 | if ($credentials === false) { |
| 115 | 115 | add_action('admin_notices', ['EEH_File', 'display_request_filesystem_credentials_form'], 999); |
@@ -140,8 +140,8 @@ discard block |
||
| 140 | 140 | */ |
| 141 | 141 | public static function display_request_filesystem_credentials_form() |
| 142 | 142 | { |
| 143 | - if (! empty(EEH_File::$_credentials_form)) { |
|
| 144 | - echo '<div class="updated espresso-notices-attention"><p>' . EEH_File::$_credentials_form . '</p></div>'; |
|
| 143 | + if ( ! empty(EEH_File::$_credentials_form)) { |
|
| 144 | + echo '<div class="updated espresso-notices-attention"><p>'.EEH_File::$_credentials_form.'</p></div>'; |
|
| 145 | 145 | } |
| 146 | 146 | } |
| 147 | 147 | |
@@ -167,8 +167,8 @@ discard block |
||
| 167 | 167 | // load WP_Filesystem and set file permissions |
| 168 | 168 | $wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path); |
| 169 | 169 | $full_file_path = EEH_File::standardise_directory_separators($full_file_path); |
| 170 | - if (! $wp_filesystem->is_readable(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path))) { |
|
| 171 | - $file_name = ! empty($type_of_file) ? $file_name . ' ' . $type_of_file : $file_name; |
|
| 170 | + if ( ! $wp_filesystem->is_readable(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path))) { |
|
| 171 | + $file_name = ! empty($type_of_file) ? $file_name.' '.$type_of_file : $file_name; |
|
| 172 | 172 | $file_name .= ! empty($file_ext) ? ' file' : ' folder'; |
| 173 | 173 | $msg = sprintf( |
| 174 | 174 | esc_html__( |
@@ -188,7 +188,7 @@ discard block |
||
| 188 | 188 | ); |
| 189 | 189 | } |
| 190 | 190 | if (defined('WP_DEBUG') && WP_DEBUG) { |
| 191 | - throw new EE_Error($msg . '||' . $msg); |
|
| 191 | + throw new EE_Error($msg.'||'.$msg); |
|
| 192 | 192 | } |
| 193 | 193 | return false; |
| 194 | 194 | } |
@@ -214,7 +214,7 @@ discard block |
||
| 214 | 214 | $perms = $wp_filesystem->getchmod(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path)); |
| 215 | 215 | if ($perms) { |
| 216 | 216 | // file permissions exist, but way be set incorrectly |
| 217 | - $type_of_file = ! empty($type_of_file) ? $type_of_file . ' ' : ''; |
|
| 217 | + $type_of_file = ! empty($type_of_file) ? $type_of_file.' ' : ''; |
|
| 218 | 218 | $type_of_file .= ! empty($type_of_file) ? 'file' : 'folder'; |
| 219 | 219 | return sprintf( |
| 220 | 220 | esc_html__( |
@@ -258,15 +258,15 @@ discard block |
||
| 258 | 258 | // add / to folder |
| 259 | 259 | $folder = EEH_File::end_with_directory_separator($folder); |
| 260 | 260 | $wp_filesystem = EEH_File::_get_wp_filesystem($folder); |
| 261 | - if (! $wp_filesystem->is_dir(EEH_File::convert_local_filepath_to_remote_filepath($folder))) { |
|
| 261 | + if ( ! $wp_filesystem->is_dir(EEH_File::convert_local_filepath_to_remote_filepath($folder))) { |
|
| 262 | 262 | // ok so it doesn't exist. Does its parent? Can we write to it? |
| 263 | - if (! EEH_File::ensure_folder_exists_and_is_writable($parent_folder)) { |
|
| 263 | + if ( ! EEH_File::ensure_folder_exists_and_is_writable($parent_folder)) { |
|
| 264 | 264 | return false; |
| 265 | 265 | } |
| 266 | - if (! EEH_File::verify_is_writable($parent_folder)) { |
|
| 266 | + if ( ! EEH_File::verify_is_writable($parent_folder)) { |
|
| 267 | 267 | return false; |
| 268 | 268 | } else { |
| 269 | - if (! $wp_filesystem->mkdir(EEH_File::convert_local_filepath_to_remote_filepath($folder))) { |
|
| 269 | + if ( ! $wp_filesystem->mkdir(EEH_File::convert_local_filepath_to_remote_filepath($folder))) { |
|
| 270 | 270 | if (defined('WP_DEBUG') && WP_DEBUG) { |
| 271 | 271 | $msg = sprintf(esc_html__('"%s" could not be created.', 'event_espresso'), $folder); |
| 272 | 272 | $msg .= EEH_File::_permissions_error_for_unreadable_filepath($folder); |
@@ -276,7 +276,7 @@ discard block |
||
| 276 | 276 | } |
| 277 | 277 | EEH_File::add_index_file($folder); |
| 278 | 278 | } |
| 279 | - } elseif (! EEH_File::verify_is_writable($folder)) { |
|
| 279 | + } elseif ( ! EEH_File::verify_is_writable($folder)) { |
|
| 280 | 280 | return false; |
| 281 | 281 | } |
| 282 | 282 | return true; |
@@ -296,7 +296,7 @@ discard block |
||
| 296 | 296 | // load WP_Filesystem and set file permissions |
| 297 | 297 | $wp_filesystem = EEH_File::_get_wp_filesystem($full_path); |
| 298 | 298 | $full_path = EEH_File::standardise_directory_separators($full_path); |
| 299 | - if (! $wp_filesystem->is_writable(EEH_File::convert_local_filepath_to_remote_filepath($full_path))) { |
|
| 299 | + if ( ! $wp_filesystem->is_writable(EEH_File::convert_local_filepath_to_remote_filepath($full_path))) { |
|
| 300 | 300 | if (defined('WP_DEBUG') && WP_DEBUG) { |
| 301 | 301 | $msg = sprintf( |
| 302 | 302 | esc_html__('The "%1$s" %2$s is not writable.', 'event_espresso'), |
@@ -327,11 +327,11 @@ discard block |
||
| 327 | 327 | $wp_filesystem = EEH_File::_get_wp_filesystem($full_file_path); |
| 328 | 328 | $full_file_path = EEH_File::standardise_directory_separators($full_file_path); |
| 329 | 329 | $parent_folder = EEH_File::get_parent_folder($full_file_path); |
| 330 | - if (! EEH_File::exists($full_file_path)) { |
|
| 331 | - if (! EEH_File::ensure_folder_exists_and_is_writable($parent_folder)) { |
|
| 330 | + if ( ! EEH_File::exists($full_file_path)) { |
|
| 331 | + if ( ! EEH_File::ensure_folder_exists_and_is_writable($parent_folder)) { |
|
| 332 | 332 | return false; |
| 333 | 333 | } |
| 334 | - if (! $wp_filesystem->touch(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path))) { |
|
| 334 | + if ( ! $wp_filesystem->touch(EEH_File::convert_local_filepath_to_remote_filepath($full_file_path))) { |
|
| 335 | 335 | if (defined('WP_DEBUG') && WP_DEBUG) { |
| 336 | 336 | $msg = |
| 337 | 337 | sprintf(esc_html__('The "%s" file could not be created.', 'event_espresso'), $full_file_path); |
@@ -341,7 +341,7 @@ discard block |
||
| 341 | 341 | return false; |
| 342 | 342 | } |
| 343 | 343 | } |
| 344 | - if (! EEH_File::verify_is_writable($full_file_path, 'file')) { |
|
| 344 | + if ( ! EEH_File::verify_is_writable($full_file_path, 'file')) { |
|
| 345 | 345 | return false; |
| 346 | 346 | } |
| 347 | 347 | return true; |
@@ -408,9 +408,9 @@ discard block |
||
| 408 | 408 | public static function write_to_file($full_file_path = '', $file_contents = '', $file_type = '') |
| 409 | 409 | { |
| 410 | 410 | $full_file_path = EEH_File::standardise_directory_separators($full_file_path); |
| 411 | - $file_type = ! empty($file_type) ? rtrim($file_type, ' ') . ' ' : ''; |
|
| 411 | + $file_type = ! empty($file_type) ? rtrim($file_type, ' ').' ' : ''; |
|
| 412 | 412 | $folder = EEH_File::remove_filename_from_filepath($full_file_path); |
| 413 | - if (! EEH_File::verify_is_writable($folder)) { |
|
| 413 | + if ( ! EEH_File::verify_is_writable($folder)) { |
|
| 414 | 414 | if (defined('WP_DEBUG') && WP_DEBUG) { |
| 415 | 415 | $msg = sprintf( |
| 416 | 416 | esc_html__('The %1$sfile located at "%2$s" is not writable.', 'event_espresso'), |
@@ -540,8 +540,8 @@ discard block |
||
| 540 | 540 | public static function add_htaccess_deny_from_all($folder = '') |
| 541 | 541 | { |
| 542 | 542 | $folder = EEH_File::standardise_and_end_with_directory_separator($folder); |
| 543 | - if (! EEH_File::exists($folder . '.htaccess')) { |
|
| 544 | - if (! EEH_File::write_to_file($folder . '.htaccess', 'deny from all', '.htaccess')) { |
|
| 543 | + if ( ! EEH_File::exists($folder.'.htaccess')) { |
|
| 544 | + if ( ! EEH_File::write_to_file($folder.'.htaccess', 'deny from all', '.htaccess')) { |
|
| 545 | 545 | return false; |
| 546 | 546 | } |
| 547 | 547 | } |
@@ -559,10 +559,10 @@ discard block |
||
| 559 | 559 | public static function add_index_file($folder) |
| 560 | 560 | { |
| 561 | 561 | $folder = EEH_File::standardise_and_end_with_directory_separator($folder); |
| 562 | - if (! EEH_File::exists($folder . 'index.php')) { |
|
| 562 | + if ( ! EEH_File::exists($folder.'index.php')) { |
|
| 563 | 563 | if ( |
| 564 | 564 | ! EEH_File::write_to_file( |
| 565 | - $folder . 'index.php', |
|
| 565 | + $folder.'index.php', |
|
| 566 | 566 | 'You are not permitted to read from this folder', |
| 567 | 567 | '.php' |
| 568 | 568 | ) |
@@ -613,7 +613,7 @@ discard block |
||
| 613 | 613 | */ |
| 614 | 614 | public static function end_with_directory_separator($file_path) |
| 615 | 615 | { |
| 616 | - return rtrim($file_path, '/\\') . '/'; |
|
| 616 | + return rtrim($file_path, '/\\').'/'; |
|
| 617 | 617 | } |
| 618 | 618 | |
| 619 | 619 | |
@@ -648,18 +648,18 @@ discard block |
||
| 648 | 648 | foreach ($folder_paths as $folder_path) { |
| 649 | 649 | $folder_path = self::standardise_and_end_with_directory_separator($folder_path); |
| 650 | 650 | // load WP_Filesystem and set file permissions |
| 651 | - $files_in_folder = glob($folder_path . '*.php'); |
|
| 651 | + $files_in_folder = glob($folder_path.'*.php'); |
|
| 652 | 652 | $class_to_folder_path = []; |
| 653 | 653 | if ($files_in_folder) { |
| 654 | 654 | foreach ($files_in_folder as $file_path) { |
| 655 | 655 | // only add files, not folders |
| 656 | - if (! is_dir($file_path)) { |
|
| 656 | + if ( ! is_dir($file_path)) { |
|
| 657 | 657 | if ($index_numerically) { |
| 658 | 658 | $class_to_folder_path[] = $file_path; |
| 659 | 659 | } else { |
| 660 | 660 | $classname = |
| 661 | 661 | self::get_classname_from_filepath_with_standard_filename($file_path); |
| 662 | - $class_to_folder_path[ $classname ] = $file_path; |
|
| 662 | + $class_to_folder_path[$classname] = $file_path; |
|
| 663 | 663 | } |
| 664 | 664 | } |
| 665 | 665 | } |
@@ -681,7 +681,7 @@ discard block |
||
| 681 | 681 | public static function copy($source_file, $destination_file, $overwrite = false) |
| 682 | 682 | { |
| 683 | 683 | $full_source_path = EEH_File::standardise_directory_separators($source_file); |
| 684 | - if (! EEH_File::exists($full_source_path)) { |
|
| 684 | + if ( ! EEH_File::exists($full_source_path)) { |
|
| 685 | 685 | if (defined('WP_DEBUG') && WP_DEBUG) { |
| 686 | 686 | $msg = sprintf( |
| 687 | 687 | esc_html__('The file located at "%2$s" is not readable or doesn\'t exist.', 'event_espresso'), |
@@ -696,7 +696,7 @@ discard block |
||
| 696 | 696 | $full_dest_path = EEH_File::standardise_directory_separators($destination_file); |
| 697 | 697 | $folder = EEH_File::remove_filename_from_filepath($full_dest_path); |
| 698 | 698 | EEH_File::ensure_folder_exists_and_is_writable($folder); |
| 699 | - if (! EEH_File::verify_is_writable($folder)) { |
|
| 699 | + if ( ! EEH_File::verify_is_writable($folder)) { |
|
| 700 | 700 | if (defined('WP_DEBUG') && WP_DEBUG) { |
| 701 | 701 | $msg = sprintf( |
| 702 | 702 | esc_html__('The file located at "%2$s" is not writable.', 'event_espresso'), |
@@ -763,6 +763,6 @@ discard block |
||
| 763 | 763 | public static function convert_local_filepath_to_remote_filepath($local_filepath) |
| 764 | 764 | { |
| 765 | 765 | $wp_filesystem = EEH_File::_get_wp_filesystem($local_filepath); |
| 766 | - return str_replace(WP_CONTENT_DIR . '/', $wp_filesystem->wp_content_dir(), $local_filepath); |
|
| 766 | + return str_replace(WP_CONTENT_DIR.'/', $wp_filesystem->wp_content_dir(), $local_filepath); |
|
| 767 | 767 | } |
| 768 | 768 | } |