bool value, ...] */ public static function getCartItemCapabilities() { return []; } public function setErrors(array $errors) { $this->_errors = $errors; return $this; } public function addErrors(array $errors) { $this->_errors = $this->_errors + $errors; return $this; } /** * @param array $data ['id' =>, 'cartId' =>, 'productId' =>, ...] * * @return $this|mixed */ public function setCartItemData(array $data) { $this->_cartItemData = $data; return $this; } /** * @return array */ public function prepareCartItemData() { if ($this->_cartItemData) { $this->_cartItemData['typeTitle'] = Entity::CART_ITEM_TYPE_TITLE; $eventObj = new EventObj(); $eventObj->setPrimaryKey($this->_cartItemData['productCategoryId']); if ($eventObj->getData()) { $this->_cartItemData['url'] = EventView::getViewUrl($eventObj->getData('alias')); } $this->_cartItemData['allowChangeQuantity'] = in_array(App_ECommerce_Cart_ItemObjFactory::CAPABILITY_CHANGE_QTY, $this->getCartItemCapabilities()) ? 'y' : 'n'; $this->_cartItemData['showDescription'] = 'y'; } return $this->_cartItemData; } public function beforeValidateCart() { $row = $this->_cartItemData; $eventId = $row['productCategoryId']; $this->setPrimaryKey($eventId); self::$_eventData[$eventId] = $this->getData(); } /** * Validate temporary attendees (because they can be already added via admin end) * * $item [ * 'id' => int, * 'cartId' => int, * 'cartItemType' => string, // "Event\" * 'productId' => int, // attendeeId * 'quantity' => int, // 1 * 'price' => float, * ] * * @throws Exception * @return array|bool */ public function validateCartItem() { $row = $this->_cartItemData; $eventId = $row['productCategoryId']; $attendeeId = $row['productId']; $this->setPrimaryKey($eventId); $attendee = $this->getAttendeeObj(['primaryKey' => $attendeeId])->getData(); if (empty($row)) { $this->_addError($this->_renderMessage(static::MSG_EVENT_MISSING, [])); return false; } if (empty($attendee)) { $this->_addError($this->_renderMessage(static::MSG_ATTENDEE_MISSING, [])); return false; } if (empty($attendee['userAvailable'])) { $this->_addError($this->_renderMessage(static::MSG_USER_UNAVAILABLE, [])); return false; } if (1 > $row['quantity']) { $this->_addError($this->_renderMessage(static::MSG_REGISTRATION_NOT_ALLOWED, [])); } // self::$_eventData[$eventId]: should operate "approved" attendees // use static var to store changes of registrationSpots $eventData = &self::$_eventData[$eventId]; if (false == $eventData['registrationAllowed']) { $this->_addError($this->_renderMessage(static::MSG_REGISTRATION_NOT_ALLOWED, [$row['title']])); return false; } if (isset($eventData['registrationSpots'])) { if (1 > $eventData['registrationSpots']) { $this->_addError($this->_renderMessage(static::MSG_REGISTRATION_LIMIT_EXCEEDED, [$row['title']])); return false; } $eventData['registrationSpots'] = ($eventData['registrationSpots'] > 1) ? $eventData['registrationSpots'] - 1 : 0; } $errors = []; if ($attendee['userId']) { $attendeePrice = (float)$eventData['memberPrice']; $attendeeName = $attendee['firstName'] . ' ' . $attendee['lastName']; $this->_validateMemberRegistered($errors, $eventData, $attendee); } elseif ($attendee['name']) { $attendeePrice = (float)$eventData['nonmemberPrice']; $attendeeName = $attendee['name']; $this->_validateNonmemberRegistered($errors, $eventData, $attendee); } else { throw new Exception('Wrong data format'); } if ($errors) { $this->addErrors($errors); return false; } if ((float) $attendee['amount'] !== $attendeePrice) { $this->_addError($this->_renderMessage(static::MSG_WRONG_USER_AMOUNT, [$attendeeName, $row['title']])); return false; } return true; } protected function _validateMemberRegistered(array &$errors, array $event, array $attendee) { $registered = $this->getAttendeeObj()->getBoughtAttendees(); $id2user = Qs_Array::indexToAssoc($registered, 'userId'); if (array_key_exists($attendee['userId'], $id2user)) { $errors[] = $this->_renderMessage( static::MSG_USER_ALREADY_REGISTERED, [$id2user[$attendee['userId']]['name'], $event['title']] ); } return $this; } protected function _validateNonmemberRegistered(array &$errors, array $event, array $attendee) { $registered = $this->getAttendeeObj()->getBoughtAttendees(); $name2user = Qs_Array::indexToAssoc($registered, 'name'); if (array_key_exists($attendee['name'], $name2user)) { $errors[] = $this->_renderMessage( static::MSG_USER_ALREADY_REGISTERED, [$attendee['name'], $event['title']] ); } return $this; } protected function _renderMessage($messageKey, array $args) { $template = $this->getConfig('messageTemplates')->toArray()[$messageKey]; return vsprintf($template, $args); } protected static function _getEventAttendees(array $attendeeIds) { $select = Qs_Db::getSelect(); $select->from(Qs_Db::getPair('Event', 'e'), []); $select->join( Qs_Db::getPair('EventAttendee', 'ea'), 'ea.eventId = e.id', ['eventId', '*', 'name' => EventModel::getAttendeeNameExpr()] ); $select->where('ea.id IN(?)', $attendeeIds, Qs_Db::INT_TYPE); $result = []; if (($groupedList = Qs_Db::getInstance()->fetchAll($select, null, Qs_Db::FETCH_GROUP))) { $eventSelect = Qs_Db::getSelect(); $eventSelect->from(Qs_Db::getPair('Event'), ['*']); $eventSelect->where('id IN (?)', array_keys($groupedList)); if (($events = Qs_Db::getInstance()->fetchAll($eventSelect))) { $timeRanges = Model::readEventTimeRanges(Qs_Array::fetchColAll($events, 'id')); foreach ($events as &$event) { $event['attendees'] = $groupedList[$event['id']]; if (array_key_exists($event['id'], $timeRanges)) { $event['timeRanges'] = $timeRanges[$event['id']]; } } $result = $events; } } return $result; } public function completeItemList(array $itemList) { if (($cartIds = Qs_Array::fetchColAll($itemList, 'cartId'))) { $infoList = $this->_getPaymentInfo($cartIds); $registrationDate = date('Y-m-d H:i:s'); foreach ($itemList as $item) { $data = ['registrationDate' => $registrationDate, 'bought' => 'y']; if (isset($infoList[$item['cartId']])) { $data['paymentType'] = $infoList[$item['cartId']]['paymentType']; $data['paymentDate'] = $infoList[$item['cartId']]['paymentDate']; } $this->_getTable('EventAttendee')->update($data, ['id = ?' => $item['productId']]); } } if (($attendeeIds = Qs_Array::fetchColAll($itemList, 'productId'))) { self::sendSignupNotification($attendeeIds); } return $this; } protected function _getPaymentInfo(array $cartIds) { if (empty($cartIds)) { return []; } $select = $this->_db->select(); $select->from($this->_getPair('Cart'), ['cartId' => 'id']); $select->join( $this->_getPair('Transaction'), 'Transaction.id = Cart.transactionId', ['paymentType', 'paymentDate' => 'added'] ); $select->where('Cart.id IN (?)', $cartIds); return $this->_db->fetchAll($select, null, PDO::FETCH_GROUP | PDO::FETCH_UNIQUE); } public function afterCartItemRemove() { $eventAttendee = Qs_Db::getTableName('EventAttendee'); $_cartId = (int) $this->_cartItemData['cartId']; $_eventId = (int) $this->_cartItemData['productId']; $sql = "DELETE ea FROM {$eventAttendee} AS ea " . "WHERE ea.cartId = {$_cartId} AND ea.eventId = {$_eventId} AND ea.bought IS NULL"; return Qs_Db::getInstance()->query($sql); } /** * @param $event * * @return $this */ protected static function _sendEventNotifications($event) { $attendees = $event['attendees']; $mailData = self::_getMailData($event, $attendees); $settingsPrefix = 'eventNotification'; $groups = [ 'Author' => [ 'to' => '', ], 'Attendee' => [ 'to' => Qs_Array::fetchColAll($attendees, 'email'), ], 'Admin' => [ 'to' => App_Settings_Obj::getAdminEmails(), 'link' => AttendeeAdminView::getPage('url') . '/' . $event['id'] ], ]; $auth = UserAuth::getInstance(); if ($auth->isLoggedIn() && ($authData = $auth->getData())) { $groups['Author']['to'][] = $authData['email']; if ($groups['Attendee']['to'] && false !== ($idx = array_search($authData['email'], $groups['Attendee']['to'])) ) { unset($groups['Attendee']['to'][$idx]); } } foreach ($groups as $group => $options) { $data = $mailData; $prefix = $settingsPrefix . $group; $from = App_Settings_Obj::getEmailFrom($prefix . 'From'); $subject = App_Settings_Obj::get($prefix . 'Subject'); $body = App_Settings_Obj::get($prefix . 'Body'); if (empty($options['to']) || empty($body) || empty($subject) || empty($from)) { continue; } if (isset($options['link'])) { $data['{link}'] = htmlentities($options['link']); } $body = str_replace(array_keys($data), array_values($data), $body); $mail = new Qs_Mail(); $mail->setFrom($from); $mail->setSubject($subject); Qs_Mail::cutImageBaseUrl($body, constant('BASE_URL')); $mail->setHtml($body, null, constant('WWW_PATH')); foreach ((array) $options['to'] as $email) { $userMail = clone $mail; if ($email) { $userMail->addTo($email); $userMail->send(); } } } } public static function sendSignupNotification(array $attendeeIds) { $events = self::_getEventAttendees($attendeeIds); foreach ($events as $event) { self::_sendEventNotifications($event); } } protected static function _getMailData($event, $attendees) { /** @var \Qs_Doc $doc */ $doc = Zend_Registry::get('doc'); $smarty = $doc->getSmarty(); require_once 'lib/Smarty/app_plugins/function.date_range.php'; require_once 'lib/Smarty/app_plugins/function.time_range.php'; $dateRangeParams = ['start' => $event['startDate'], 'end' => $event['endDate'], 'monthFormat' => '%B']; $dateHtml = smarty_function_date_range($dateRangeParams, $smarty); $attendeeParts = []; foreach ($attendees as $attendee) { $attendeeParts[] = $attendee['name']; } $timeHtml = $doc->assign('item', new Qs_Doc_Item(['timeRanges' => $event['timeRanges']])) ->fetchTemplate(Qs_SiteMap::getTemplate('Event/notification/time.tpl')); $data = [ '{title}' => htmlspecialchars($event['title']), '{location}' => htmlspecialchars($event['location']), '{date}' => $dateHtml, '{time}' => $timeHtml, '{attendees}' => $attendeeParts ? '