<commit_before><?php
/**
 * An extension to allow a user to book resources for an event time.
 *
 * @package silverstripe-eventresources
 */
class CalendarDateTimeResourcesExtension extends DataObjectDecorator {

	public function extraStatics() {
		return array(
			'many_many' => array(
				'Resources' => 'EventResource'),
			'many_many_extraFields' => array(
				'Resources' => array(
					'BookingQuantity' => 'Int'))
		);
	}

	public function updateDateTimeCMSFields(FieldSet $fields) {
		if (!$this->owner->isInDB()) {
			$fields->addFieldToTab('Root.Resources', new LiteralField(
				'BookResourcesNote',
				'<p>You can book resources after you save for the first time.</p>'
			));
			return;
		}

		$fields->addFieldToTab('Root.Resources', $res = new ManyManyPickerField(
			$this->owner,
			'Resources',
			'Booked Resources For This Event',
			array(
				'ExtraFields'        => 'getCmsExtraFields',
				'ShowPickedInSearch' => false
			))
		);
		$res->getSearchField()->setOption('FilterCallback', array(
			$this->owner, 'filterEventResource'
		));
	}

	public function updateDateTimeTable($table) {
		$table->setPopupSize(560, 650);
	}

	/**
	 * If the event times have changed, it checks to make sure that the resources
	 * are still available. If not, it throws a validation exception.
	 *
	 * @throws ValiationException
	 */
	public function onBeforeWrite() {
		$changed = $this->owner->getChangedFields();
		$check   = array('StartDate', 'StartTime', 'EndDate', 'EndTime', 'is_all_day');

		if (!array_intersect_key(array_flip($check), $changed)) return;

		foreach ($this->owner->Resources() as $resource) {
			if ($resource->Type == 'Limited') {
				$avail = $resource->getAvailableForEvent($this->owner);

				if ($resource->BookingQuantity > $avail) {
					throw new ValidationException(new ValidationResult(false, sprintf(
						'Changing the date of this event means there is only %d '     .
						'of the "%s" resource available, whereas you have requested ' .
						'%d. Please either select fewer of this resource from the '   .
						'"Resources" tab, or change the date to one where there are ' .
						'more available.',
						$avail, $resource->Title, $resource->BookingQuantity
					)));
				}
			} elseif (!$resource->getAvailableForEvent($this->owner)) {
				throw new ValidationException(new ValidationResult(false, sprintf(
					'Changing the date of this event means the "%s" resource ' .
					'is no longer available. Please either remove this '       .
					'resource from the "Resources" tab, or change the date '   .
					'to one where the resource has not already been booked.',
					$resource->Title
				)));
			}
		}
	}

	/**
	 * @return bool
	 */
	public function filterEventResource($resource) {
		return (bool) $resource->getAvailableForEvent($this->owner);
	}

}<commit_msg>Added basic validation to recurring events - any non-recurring events that also use the same resource as the recurring event are checked to see if they conflict on save.<commit_after><?php
/**
 * An extension to allow a user to book resources for an event time.
 *
 * @package silverstripe-eventresources
 */
class CalendarDateTimeResourcesExtension extends DataObjectDecorator {

	public function extraStatics() {
		return array(
			'many_many' => array(
				'Resources' => 'EventResource'),
			'many_many_extraFields' => array(
				'Resources' => array(
					'BookingQuantity' => 'Int'))
		);
	}

	public function updateDateTimeCMSFields(FieldSet $fields) {
		if (!$this->owner->isInDB()) {
			$fields->addFieldToTab('Root.Resources', new LiteralField(
				'BookResourcesNote',
				'<p>You can book resources after you save for the first time.</p>'
			));
			return;
		}

		$fields->addFieldToTab('Root.Resources', $res = new ManyManyPickerField(
			$this->owner,
			'Resources',
			'Booked Resources For This Event',
			array(
				'ExtraFields'        => 'getCmsExtraFields',
				'ShowPickedInSearch' => false
			))
		);
		$res->getSearchField()->setOption('FilterCallback', array(
			$this->owner, 'filterEventResource'
		));
	}

	public function updateDateTimeTable($table) {
		$table->setPopupSize(560, 650);
	}

	/**
	 * If the event times have changed, it checks to make sure that the resources
	 * are still available. If not, it throws a validation exception.
	 *
	 * @throws ValiationException
	 */
	public function onBeforeWrite() {
		$changed = $this->owner->getChangedFields();
		$check   = array('StartDate', 'StartTime', 'EndDate', 'EndTime', 'is_all_day');

		if (!array_intersect_key(array_flip($check), $changed)) return;

		foreach ($this->owner->Resources() as $resource) {
			if ($resource->Type == 'Unlimited') {
				continue;
			}

			// If this is a recurring event, then check all other non-recurring
			// bookings for this resource to see if any conflict.
			if ($this->owner->Event()->Recursion) {
				$bookings = $resource->Events(
					sprintf(
						'"CalendarEvent"."Recursion" = 0 AND "CalendarDateTime"."ID" <> %d',
						$this->owner->ID),
					null,
					'INNER JOIN "CalendarEvent" ON "CalendarEvent"."ID" = "CalendarDateTime"."EventID"');

				foreach ($bookings as $booking) {
					$counter = $booking->getStartTimestamp();
					$end     = $booking->getEndTimestamp();

					// Loop through each day the other booking falls on, to see
					// if it could cause a conflict.
					while ($counter < $end) {
						if ($this->owner->Event()->recursionHappensOn($counter)) {
							$this->checkResourceAvailability($resource, $counter);
						}
						$counter = sfTime::add($counter, 1, sfTime::DAY);
					}
				}
			} else {
				$this->checkResourceAvailability($resource);
			}
		}
	}

	/**
	 * @throws ValidationException
	 */
	protected function checkResourceAvailability($resource, $ts = null) {
		$datetime = $this->owner;

		if ($ts) {
			$datetime = clone $datetime;
			$datetime->StartDate = date('Y-m-d', $ts);
			$datetime->EndDate   = date('Y-m-d', $ts);
		}

		if ($resource->Type == 'Limited') {
			$avail = $resource->getAvailableForEvent($datetime);

			if ($resource->BookingQuantity > $avail) {
				throw new ValidationException(new ValidationResult(false, sprintf(
					'Changing the date of this event means there is only %d '     .
					'of the "%s" resource available, whereas you have requested ' .
					'%d. Please either select fewer of this resource from the '   .
					'"Resources" tab, or change the date to one where there are ' .
					'more available.',
					$avail, $resource->Title, $resource->BookingQuantity
				)));
			}
		} elseif (!$resource->getAvailableForEvent($datetime)) {
			throw new ValidationException(new ValidationResult(false, sprintf(
				'Changing the date of this event means the "%s" resource ' .
				'is no longer available. Please either remove this '       .
				'resource from the "Resources" tab, or change the date '   .
				'to one where the resource has not already been booked.',
				$resource->Title
			)));
		}
	}

	/**
	 * @return bool
	 */
	public function filterEventResource($resource) {
		return (bool) $resource->getAvailableForEvent($this->owner);
	}

}
{'fraction_non_alphanumeric': 0.13742937853107345, 'fraction_numerical': 0.0019774011299435027, 'mean_word_length': 3.5246006389776356, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 87, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '672802', 'n_tokens_mistral': 2496, 'n_tokens_neox': 2062, 'n_words': 605}