/** * Generate the exports page contents. * * @since 0.2.0 * @return void */ public function generateExportPage() { wp_enqueue_script('pikaday'); wp_enqueue_style('pikaday'); wp_enqueue_style('wprsrv-export'); $exportTemplate = [\Wprsrv\wprsrv()->pluginDirectory, 'includes', 'templates', 'admin', 'export.php']; $exportTemplate = implode(RDS, $exportTemplate); require_once $exportTemplate; }
/** * Handle a requested export. Takes the request params and dumps the export data. * * @since 0.2.0 * @return void */ public function handleExport() { if (!$this->validateExportRequest()) { return; } $args = $_POST['wprsrv']; try { $exportParams = $this->getExportParameters($args); $exporter = ExporterFactory::create($exportParams['format'], $exportParams['reservable'], $exportParams['date_range']); $exporter->dumpExport(); } catch (\OutOfBoundsException $oobe) { wp_die(__('The selected date range has no reservation data to export.', 'wprsrv')); } catch (\Exception $e) { \Wprsrv\wprsrv()->logger->alert('Could not generate export: {msg}', ['msg' => $e->getMessage()]); wp_die(__('There was an error with the export, please try again.', 'wprsrv')); } }
/** * Create a new instance of Reservation from given data. * * @fixme Validate single day mode works. * @todo Extract some functionality elsewhere to make this simpler. * @since 0.1.0 * * @param mixed[] $postData Post data values to use. Passed to wp_insert_post. * @param mixed[] $metaData Metadata values to use. * @param \Wprsrv\PostTypes\Objects\Reservable|null $reservable The reservable * being reserved. * * @return \Wprsrv\PostTypes\Objects\Reservation|Boolean */ public static function create(array $postData, array $metaData, Reservable $reservable = null) { // Force post type and status. $postData['post_type'] = 'reservation'; $postData['post_status'] = 'reservation_pending'; if (!isset($metaData['reservable_id']) && $reservable === null) { throw new \InvalidArgumentException('Could not create a new reservation with the given post data.'); } elseif ($reservable !== null) { $metaData['reservable_id'] = $reservable->ID; } $metaErrors = static::validateMeta($metaData); if (!empty($metaErrors)) { $_POST['reservation_notice'] = sprintf('<ul><li>%s</li></ul>', implode('</li><li>', $metaErrors)); return false; } $id = wp_insert_post($postData); if (!$id || is_wp_error($id)) { if (is_wp_error($id)) { \Wprsrv\wprsrv()->logger->alert('Could not create a new reservation: {msg}', ['msg' => array_shift($id->get_error_messages())]); } throw new \InvalidArgumentException('Could not create a new reservation with the given post data.'); } $self = new static($id); $date = new \DateTime('now'); $year = new \DateInterval('P1Y'); $pruneDate = $date->add($year); $metaData['prune_date'] = $pruneDate->format('Y-m-d H:i:s'); // Save meta values. foreach ($metaData as $key => $data) { if ($key == 'reservation_date_start') { $data = preg_replace('%[0-9][0-9]:[0-9][0-9]:[0-9][0-9]$%', '00:00:00', $data); } elseif ($key == 'reservation_date_end') { $data = preg_replace('%[0-9][0-9]:[0-9][0-9]:[0-9][0-9]$%', '23:59:59', $data); } $self->setMeta($key, $data); } $self->getReservable()->flushCache(); do_action('wprsrv/reservation/created', $self->ID, $self); return $self; }
/** * Get the data for all disabled days for a reservable item. This includes * blocked time which has been already reserved. * * @since 0.1.0 * * @param Boolean $forFrontendCalendar Optional. Get disabled days for the front-end * calendar? * * @return mixed */ public function getDisabledDaysData($forFrontendCalendar = false) { // Get cached disabled days data. $ranges = get_transient($this->cachePrefix . 'disdays_' . strval($forFrontendCalendar)); if ($ranges !== false) { return $ranges; } $disabledRanges = $this->getDisabledDaysAdminData(); $disabledWeekdays = $this->getDisabledWeekdaysData(); $reservations = $this->getReservations(); $weekInterval = new \DateInterval('P1W'); $dayInterval = new \DateInterval('P1D'); if (!empty($disabledWeekdays)) { foreach ($disabledWeekdays as $weekday) { $wdayDate = new \DateTime('now'); $wdayDate->modify($weekday); // Pad to two years. for ($i = 0; $i < 105; $i++) { $range = ['start' => $wdayDate->format('Y-m-d'), 'end' => $wdayDate->format('Y-m-d')]; $disabledRanges[] = $range; $wdayDate->add($weekInterval); } } } $overlapAdjusted = []; foreach ($reservations as $reservation) { if (!$reservation->isPending() && !$reservation->isAccepted()) { continue; } try { $startDate = $reservation->getStartDate('object'); $endDate = $reservation->getEndDate('object'); // If we allow overlapping reservations, allow users to pick dates // Where a reservation either starts or ends. if ($this->allowsOverlappingReservations() && $forFrontendCalendar) { $overlapAdjusted[] = $startDate->format('Y-m-d'); $overlapAdjusted[] = $endDate->format('Y-m-d'); $startDate->add($dayInterval); $endDate->sub($dayInterval); } $start = $startDate->format('Y-m-d'); $end = $endDate->format('Y-m-d'); // Don't get "expired" dates. if ($end < date('Y-m-d')) { continue; } $range = ['start' => $start, 'end' => $end, 'reservation_id' => $reservation->ID]; $disabledRanges[] = $range; } catch (\Exception $e) { \Wprsrv\wprsrv()->logger->alert('Invalid date data for reservation {id}', ['id' => $reservation->ID]); } } $overlapValCounts = array_count_values($overlapAdjusted); // See which dates have two reservations overlapping and disable them too. foreach ($overlapValCounts as $value => $count) { if ($count < 2) { continue; } $range = ['start' => $value, 'end' => $value, 'reservation_id' => 0]; $disabledRanges[] = $range; } set_transient($this->cachePrefix . 'disdays_' . strval($forFrontendCalendar), $disabledRanges, HOUR_IN_SECONDS * 4); return $disabledRanges; }
/** * Create a new reservation from the form. * * @access protected * @since 0.1.0 * @todo Pickle this up to smaller methods and pass some control over to the * reservation object class. * * @param mixed[] $data Data for reservation. * * @return void */ protected function createReservation($data) { $reservable = $this->reservable; $reservation_meta_data = ['reservable_id' => $reservable->ID, 'reserver_email' => $data['wprsrv-reserver-email'], 'reserver_name' => $data['wprsrv-reserver-name']]; if ($reservable->isSingleDay()) { $reservation_meta_data['start_date'] = $data['wprsrv-reservation-date']; $reservation_meta_data['end_date'] = $data['wprsrv-reservation-date']; $reservation_meta_data['reservation_date'] = $data['wprsrv-reservation-date']; } else { $reservation_meta_data['start_date'] = $data['wprsrv-reservation-date-start']; $reservation_meta_data['end_date'] = $data['wprsrv-reservation-date-end']; } if ($reservable->isSingleDay()) { $reservationTitle = [$reservable->post_title, ': ', $reservation_meta_data['start_date'], ', by ', $reservation_meta_data['reserver_email']]; } else { $reservationTitle = [$reservable->post_title, ': ', $reservation_meta_data['start_date'], ' to ', $reservation_meta_data['end_date'], ', by ', $reservation_meta_data['reserver_email']]; } $reservationTitle = implode('', $reservationTitle); $reservation_post_data = ['post_type' => 'reservation', 'post_title' => $reservationTitle, 'post_status' => 'reservation_pending', 'post_content' => $data['wprsrv-reservation-description']]; try { $reservation = Reservation::create($reservation_post_data, $reservation_meta_data, $reservable); } catch (\InvalidArgumentException $iae) { $_POST['reservation_notice'] = _x('The data you gave looked invalid, please check your fields and try again.', 'reservation form error', 'wprsrv'); return; } catch (\Exception $e) { \Wprsrv\wprsrv()->logger->critical('Could not create new reservation: {msg}', ['msg' => print_r($e, true)]); $_POST['reservation_notice'] = _x('Sorry, something went wrong in the reservation system, please try again.', 'reservation form error', 'wprsrv'); return; } if ($reservation instanceof Reservation) { $this->reservation = $reservation; } else { return; } $_POST['reservation_success'] = _x('Thank you for your reservation. You will get a confirmation email in a few moments.', 'reservation form success', 'wprsrv'); }
/** * Prune all reservations that have expired according to their `prune_date` meta * value. * * @since 0.1.0 * @see self::pruning() * @return void */ public function pruneReservations() { global $wpdb; // Posts statuses to prune. $pruneStatuses = ['"reservation_pending"', '"reservation_declined"']; // Query for reservations which have `prune_date` set and where `prune_date` is less than today. $query = vsprintf('SELECT ID FROM %s p LEFT JOIN %s pm ON (p.ID = pm.post_id) WHERE p.post_type = "%s" AND p.post_status IN (%s) AND pm.meta_key = "%s" AND pm.meta_value < "%s"', [$wpdb->posts, $wpdb->postmeta, 'reservation', implode(', ', $pruneStatuses), '_wprsrv_prune_date', date('Y-m-d H:i:s')]); $reservationIds = $wpdb->get_col($query); foreach ($reservationIds as $id) { /** * Should a reservation be pruned although prunde date has passed. * * Allows skipping pruning for single reservations. * * @since 0.1.0 * * @param Boolean $shouldBePruned True to allow pruning, false to * disallow. * @param Integer $id The reservation post ID. */ $shouldBePruned = (bool) apply_filters('wprsrv/reservation/prune_reservation', true, $id); /** * Set the method of deletion for pruned reservations. * * If returns true, pruned reservations will be trashed instead of fully * deleted. Returning false (default) will delete pruned reservations * completely. * * @since 0.1.0 * * @param Boolean $trashInstead Trash it on true, delete on false. * @param Integer $id Reservation post ID. */ $trashInstead = (bool) apply_filters('wprsrv/reservation/prune_to_trash', false, $id); if ($shouldBePruned) { // Force delete the reservation, skipping trash unless set to trash. $deleted = wp_delete_post($id, !$trashInstead); if ($deleted === false) { \Wprsrv\wprsrv()->logger->error('Could not prune reservation {id}: wp_delete_post failed.', ['id' => $id]); } } } }
/** * Spawn metaboxes for the post type. * * @since 0.1.0 * @access protected * * @param $string * @param $reservable * * @return void */ protected function metaBoxCallback($string, $reservable) { $tmplBase = \Wprsrv\wprsrv()->templateDirectory . RDS . 'admin' . RDS . 'reservables' . RDS; $templateFiles = ['settings' => $tmplBase . 'settings-metabox.php', 'calendars' => $tmplBase . 'calendars-metabox.php', 'actions' => $tmplBase . 'actions-metabox.php']; include $templateFiles[$string]; }