Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
vote.php
1<?php
8namespace Bitrix\Vote;
9use \Bitrix\Main\AccessDeniedException;
11use \Bitrix\Main\Entity;
13use \Bitrix\Main\ORM\Data\AddResult;
14use \Bitrix\Main\ORM\Data\Result;
15use \Bitrix\Main\Application;
16use \Bitrix\Main\Error;
17use \Bitrix\Main\Localization\Loc;
18use \Bitrix\Main\ArgumentException;
19use \Bitrix\Main\ORM\Data\UpdateResult;
20use \Bitrix\Main\ORM\Event;
21use \Bitrix\Main\ORM\Fields\DatetimeField;
22use \Bitrix\Main\ORM\Fields\ExpressionField;
23use \Bitrix\Main\ORM\Fields\TextField;
24use \Bitrix\Main\ORM\Fields\Validators\DateValidator;
25use \Bitrix\Main\ORM\Fields\IntegerField;
26use \Bitrix\Main\ORM\Fields\EnumField;
27use \Bitrix\Main\ORM\Fields\StringField;
28use \Bitrix\Main\ORM\Fields\BooleanField;
29use \Bitrix\Main\ORM\Fields\Relations\Reference;
30use \Bitrix\Main\ORM\Fields\FieldError;
31use \Bitrix\Main\ORM\EntityError;
32use \Bitrix\Main\ORM\Query\Join;
33use \Bitrix\Main\Text\HtmlFilter;
35use \Bitrix\Vote\Base\BaseObject;
36use \Bitrix\Vote\Vote\Option;
37use \Bitrix\Vote\Vote\EventLimits;
38use \Bitrix\Vote\Vote\Anonymity;
39use \Bitrix\Main\Type\DateTime;
40use \Bitrix\Main\FileTable;
41
42
43Loc::loadMessages(__FILE__);
44
94class VoteTable extends Entity\DataManager
95{
101 public static function getTableName()
102 {
103 return "b_vote";
104 }
105
112 public static function getMap()
113 {
114 $now = Application::getInstance()->getConnection()->getSqlHelper()->getCurrentDateTimeFunction();
115 return array(
116 (new IntegerField("ID", ["primary" => true, "autocomplete" => true])),
117 (new IntegerField("CHANNEL_ID", ["required" => true])),
118 (new IntegerField("C_SORT", ["default_value" => 100])),
119 (new BooleanField("ACTIVE", ["values" => ["N", "Y"], "default_value" => "Y"])),
120 (new IntegerField("ANONYMITY", ["default_value" => Anonymity::PUBLICLY])),
121 (new EnumField("NOTIFY", ["values" => ["N", "Y", "I"], "default_value" => "N"])),
122 (new IntegerField("AUTHOR_ID")),
123 (new Reference("AUTHOR", \Bitrix\Main\UserTable::class, Join::on("this.AUTHOR_ID", "ref.ID"))),
124 (new DatetimeField("TIMESTAMP_X", ["default_value" => function(){return new DateTime();}])),
125 (new DatetimeField("DATE_START", ["default_value" => function(){return new DateTime();}, "required" => true, "validation" => function() {
126 return [
127 new DateValidator,
128 [__CLASS__, "validateActivityDate"]
129 ];
130 }])),
131 (new DatetimeField("DATE_END", ["default_value" => function(){
132 $time = new DateTime();
133 $time->add("1D");
134 return $time;
135 }, "required" => true, "validation" => function() {
136 return [
137 new DateValidator,
138 [__CLASS__, "validateActivityDate"]
139 ];
140 }])),
141 (new StringField("URL", ["size" => 255])),
142 (new IntegerField("COUNTER")),
143 (new StringField("TITLE", ["size" => 255])),
144 (new TextField("DESCRIPTION")),
145 (new EnumField("DESCRIPTION_TYPE", ["values" => ["text", "html"], "default_value" => "text"])),
146 (new IntegerField("IMAGE_ID")),
147 (new Reference("IMAGE", FileTable::class, Join::on("this.IMAGE_ID", "ref.ID"))),
148 (new StringField("EVENT1", ["size" => 255])),
149 (new StringField("EVENT2", ["size" => 255])),
150 (new StringField("EVENT3", ["size" => 255])),
151 (new IntegerField("UNIQUE_TYPE", ["default_value" => EventLimits::BY_IP|EventLimits::BY_USER_ID])),
152 (new IntegerField("KEEP_IP_SEC", ["default_value" => 604800])), // one week
153 (new IntegerField("OPTIONS", ["default_value" => Option::ALLOW_REVOTE])),
154 (new ExpressionField("LAMP",
155 "CASE ".
156 "WHEN (%s='Y' AND %s='Y' AND %s <= {$now} AND {$now} <= %s AND %s='Y') THEN 'yellow' ".
157 "WHEN (%s='Y' AND %s='Y' AND %s <= {$now} AND {$now} <= %s AND %s!='Y') THEN 'green' ".
158 "ELSE 'red' ".
159 "END",
160 ["CHANNEL.ACTIVE", "ACTIVE", "DATE_START", "DATE_END", "CHANNEL.VOTE_SINGLE",
161 "CHANNEL.ACTIVE", "ACTIVE", "DATE_START", "DATE_END", "CHANNEL.VOTE_SINGLE"])),
162 (new Reference("CHANNEL", ChannelTable::class, Join::on("this.CHANNEL_ID", "ref.ID"))),
163 (new Reference("QUESTION", QuestionTable::class, Join::on("this.ID", "ref.VOTE_ID"))),
164 (new Reference("USER", \Bitrix\Main\UserTable::class, Join::on("this.AUTHOR_ID", "ref.ID"))),
165 );
166 }
174 public static function validateActivityDate($value, $primary, $row, $field)
175 {
177 if (empty($value))
178 return new FieldError(
179 $field, Loc::getMessage("VOTE_ERROR_DATE_VOTE_IS_EMPTY"), $field->getName()
180 );
181
182 return true;
183 }
184
190 public static function onBeforeAdd(\Bitrix\Main\ORM\Event $event)
191 {
192 $result = new \Bitrix\Main\ORM\EventResult();
193 if (($events = GetModuleEvents("vote", "onBeforeVoteAdd", true)) && !empty($events))
194 {
196 $data = $event->getParameter("fields");
197 foreach ($events as $ev)
198 {
199 if (ExecuteModuleEventEx($ev, array(&$data)) === false)
200 {
201 $result->addError(new EntityError("Error: ".serialize($ev), "event"));
202 return $result;
203 }
204 }
205 if ($data != $event->getParameter("fields"))
206 {
207 $result->modifyFields($data);
208 }
209 }
210 return self::modifyData($event, $result);
211 }
216 public static function onAfterAdd(\Bitrix\Main\ORM\Event $event)
217 {
218 $id = $event->getParameter("id");
219 $id = is_array($id) && array_key_exists("ID", $id) ? $id["ID"] : $id;
220 $fields = $event->getParameter("fields");
221 /***************** Event onAfterVoteAdd ****************************/
222 foreach (GetModuleEvents("vote", "onAfterVoteAdd", true) as $event)
223 ExecuteModuleEventEx($event, [$id, $fields]);
224 /***************** /Event ******************************************/
225 }
231 public static function onBeforeUpdate(\Bitrix\Main\ORM\Event $event)
232 {
233 $result = new \Bitrix\Main\ORM\EventResult();
234 if (($events = GetModuleEvents("vote", "onBeforeVoteUpdate", true)) && !empty($events))
235 {
237 $data = $event->getParameter("fields");
238 $id = $event->getParameter("id");
239 $id = is_array($id) && array_key_exists("ID", $id) ? $id["ID"] : $id;
240 foreach ($events as $ev)
241 {
242 if (ExecuteModuleEventEx($ev, array($id, &$data)) === false)
243 {
244 $result->addError(new EntityError("Error: ".serialize($ev), "event"));
245 return $result;
246 }
247 }
248 if ($data != $event->getParameter("fields"))
249 {
250 $result->modifyFields($data);
251 }
252 }
253 return self::modifyData($event, $result);
254 }
255
260 public static function onAfterUpdate(\Bitrix\Main\ORM\Event $event)
261 {
262 $id = $event->getParameter("id");
263 $id = is_array($id) && array_key_exists("ID", $id) ? $id["ID"] : $id;
264 $fields = $event->getParameter("fields");
265 /***************** Event onAfterVoteAdd ****************************/
266 foreach (GetModuleEvents("vote", "onAfterVoteUpdate", true) as $event)
267 ExecuteModuleEventEx($event, [$id, $fields]);
268 /***************** /Event ******************************************/
269 }
276 private static function modifyData(\Bitrix\Main\ORM\Event $event, \Bitrix\Main\ORM\EventResult $result)
277 {
278 $data = array_merge($event->getParameter("fields"), $result->getModified());
279 $fields = [];
280
281 if (isset($data["UNIQUE_TYPE"]) && (
282 !($data["UNIQUE_TYPE"] & \Bitrix\Vote\Vote\EventLimits::BY_USER_AUTH) &&
283 ($data["UNIQUE_TYPE"] & \Bitrix\Vote\Vote\EventLimits::BY_USER_DATE_REGISTER ||
284 $data["UNIQUE_TYPE"] & \Bitrix\Vote\Vote\EventLimits::BY_USER_ID)
285 ))
286 $fields["UNIQUE_TYPE"] = $data["UNIQUE_TYPE"] | \Bitrix\Vote\Vote\EventLimits::BY_USER_AUTH;
287
288 foreach (["TIMESTAMP_X", "DATE_START", "DATE_END"] as $key)
289 {
290 if (isset($data[$key]) && !($data[$key] instanceof DateTime))
291 $fields[$key] = DateTime::createFromUserTime($data[$key]);
292 }
293
294 //region check image
295 if (array_key_exists("IMAGE_ID", $data))
296 {
297 if ($str = \CFile::CheckImageFile($data["IMAGE_ID"]))
298 {
299 $result->addError(new FieldError(static::getEntity()->getField("IMAGE_ID"), $str));
300 }
301 else
302 {
303 $fields["IMAGE_ID"] = $data["IMAGE_ID"];
304 $fields["IMAGE_ID"]["MODULE_ID"] = "vote";
305 if ($id = $event->getParameter("id"))
306 {
307 $id = is_integer($id) ? $id : $id["ID"];
308 if ($id > 0 && ($vote = VoteTable::getById($id)->fetch()) && ($vote["IMAGE_ID"] > 0))
309 {
310 $fields["IMAGE_ID"]["old_file"] = $vote["IMAGE_ID"];
311 }
312 }
313 if (\CFile::SaveForDB($fields, "IMAGE_ID", "") === false)
314 {
315 $result->unsetField("IMAGE_ID");
316 }
317 }
318 }
319 //endregion
320 if (!empty($fields))
321 {
322 $result->modifyFields(array_merge($result->getModified(), $fields));
323 }
324 return $result;
325 }
333 public static function checkFields(Result $result, $primary, array $data)
334 {
335 parent::checkFields($result, $primary, $data);
336 if ($result->isSuccess())
337 {
338 try
339 {
340 $vote = null;
341 //region check activity dates
344 $params = null;
345 if ($result instanceof AddResult)
346 {
347 $params = [
348 "ID" => null,
349 "ACTIVE" => (array_key_exists("ACTIVE", $data) ? $data["ACTIVE"] : "Y"),
350 "CHANNEL_ID" => $data["CHANNEL_ID"],
351 "DATE_START" => $data["DATE_START"],
352 "DATE_END" => $data["DATE_END"]
353 ];
354 }
355 else if (array_key_exists("CHANNEL_ID", $data) ||
356 array_key_exists("ACTIVE", $data) && $data["ACTIVE"] == "Y" ||
357 array_key_exists("DATE_START", $data) ||
358 array_key_exists("DATE_END", $data)
359 )
360 {
361 // if it is need to move to other channel or activate or change the dates
362 $vote = Vote::loadFromId($primary["ID"]);
363 $params = [
364 "ID" => $primary["ID"],
365 "ACTIVE" => (array_key_exists("ACTIVE", $data) ? $data["ACTIVE"] : $vote["ACTIVE"]),
366 "CHANNEL_ID" => (array_key_exists("CHANNEL_ID", $data) ? $data["CHANNEL_ID"] : $vote["CHANNEL_ID"]),
367 "DATE_START" => (array_key_exists("DATE_START", $data) ? $data["DATE_START"] : $vote["DATE_START"]),
368 "DATE_END" => (array_key_exists("DATE_END", $data) ? $data["DATE_END"] : $vote["DATE_END"])
369 ];
370 }
371 if (!is_null($params))
372 {
373 $params["DATE_START"] = static::getEntity()->getField("DATE_START")->cast($params["DATE_START"]);
374 $params["DATE_END"] = static::getEntity()->getField("DATE_END")->cast($params["DATE_END"]);
375 if (array_key_exists("DATE_END", $data))
376 $data["DATE_END"] = $params["DATE_END"];
377 if (!($params["DATE_START"] instanceof DateTime) || !($params["DATE_END"] instanceof DateTime))
378 $result->addError(new FieldError(
379 static::getEntity()->getField("DATE_START"),
380 Loc::getMessage("VOTE_ERROR_DATE_VOTE_IS_WRONG")
381 ));
382 else if ($params["DATE_START"]->getTimestamp() > $params["DATE_END"]->getTimeStamp())
383 {
384 $result->addError(new FieldError(
385 static::getEntity()->getField("DATE_START"),
386 Loc::getMessage("VOTE_ERROR_DATE_START_LATER_THAN_END")
387 ));
388 }
389 else if ($params["ACTIVE"] == "Y")
390 {
392 $channel = Channel::loadFromId($params["CHANNEL_ID"]);
393 if ($channel["VOTE_SINGLE"] == "Y")
394 {
395 $dbRes = VoteTable::getList([
396 "select" => ["ID", "TITLE", "DATE_START", "DATE_END"],
397 "filter" => (is_null($params["ID"]) ? [] : [
398 "!ID" => $params["ID"]]) + [
399 "CHANNEL_ID" => $channel["ID"],
400 [
401 "LOGIC" => "OR",
402 "><DATE_START" => [$params["DATE_START"], $params["DATE_END"]],
403 "><DATE_END" => [$params["DATE_START"], $params["DATE_END"]],
404 [
405 "<=DATE_START" => $params["DATE_START"],
406 ">=DATE_END" => $params["DATE_END"]
407 ]
408 ]
409 ]
410 ]);
411 if ($res = $dbRes->fetch())
412 {
413 $field = static::getEntity()->getField("DATE_START");
414 $result->addError(new FieldError(
415 $field,
416 Loc::getMessage("VOTE_ERROR_SAME_DATE_VOTE_IS_ALREADY_EXISTS", ["#VOTE#" => $res["TITLE"]." [".$res["ID"]."]"])
417 ));
418 }
419 }
420 }
421 }
422 //endregion
423 }
424 catch (\Exception $e)
425 {
426 $result->addError(new Error(
427 $e->getMessage()
428 ));
429 }
430 }
431 }
432
438 public static function setCounter(array $id, $increment = true)
439 {
440 if (empty($id))
441 return;
442 $connection = \Bitrix\Main\Application::getInstance()->getConnection();
443
444 $sql = intval($increment);
445 if ($increment === true)
446 $sql = "COUNTER+1";
447 else if ($increment === false)
448 $sql = "COUNTER-1";
449 $connection->queryExecute("UPDATE ".self::getTableName()." SET COUNTER=".$sql." WHERE ID IN (".implode(", ", $id).")");
450 }
451}
452
453class Vote extends BaseObject implements \ArrayAccess
454{
455 protected $vote = array();
456 protected $questions = array();
457 protected $channel = null;
459 protected static $canVoteStorage = [];
460 public static $storage = array();
461 public static $statStorage = array();
462
463 public function __construct($id)
464 {
465 if (!($id > 0))
466 throw new \Bitrix\Main\ArgumentNullException("vote id");
467 parent::__construct($id);
468 }
469
470 public function init()
471 {
472 $data = self::getData($this->id);
473 if ($data === null)
474 throw new ArgumentException("Wrong vote id!");
475 $this->vote = array_diff_key($data, array("QUESTIONS" => ""));
476 foreach ($data["QUESTIONS"] as $q)
477 {
478 $this->questions[$q["ID"]] = $q;
479 }
480 $eventManager = \Bitrix\Main\EventManager::getInstance();
481 $eventManager->addEventHandler("vote", "onAfterVoteQuestionAdd", array($this, "clearCache"));
482 $eventManager->addEventHandler("vote", "onAfterVoteQuestionUpdate", array($this, "clearCache"));
483 $eventManager->addEventHandler("vote", "onAfterVoteQuestionDelete", array($this, "clearCache"));
484 $eventManager->addEventHandler("vote", "onVoteQuestionActivate", array($this, "clearCache"));
485 $eventManager->addEventHandler("vote", "onVoteReset", array($this, "clearCache"));
486 }
487
492 public static function getData($id)
493 {
494 if (!array_key_exists($id, self::$storage))
495 {
496 self::$storage[$id] = null;
498 $dbRes = VoteTable::getList(array(
499 "select" => array(
500 "V_" => "*",
501 "V_LAMP" => "LAMP",
502 "Q_" => "QUESTION.*",
503 "A_" => "QUESTION.ANSWER",
504 ),
505 "order" => array(
506 "QUESTION.C_SORT" => "ASC",
507 "QUESTION.ID" => "ASC",
508 "QUESTION.ANSWER.C_SORT" => "ASC",
509 "QUESTION.ANSWER.ID" => "ASC",
510 ),
511 "filter" => array(
512 "ID" => $id
513 )
514 ));
515 // TODO Remake to a \Bitrix\Main\ORM\Objectify\Collection and its method ->fill()
516 if (($row = $dbRes->fetch()) && $row)
517 {
518 $images = array();
519 $vote = array();
520 foreach ($row as $key => $val)
521 if (mb_strpos($key, "V_") === 0)
522 $vote[mb_substr($key, 2)] = $val;
523 $vote += array(
524 "IMAGE" => null,
525 "FIELD_NAME" => \Bitrix\Vote\Event::getExtrasFieldName($vote["ID"], "#ENTITY_ID#"),
526 "QUESTIONS" => array());
527 if ($vote["IMAGE_ID"] > 0)
528 $images[$vote["IMAGE_ID"]] = &$vote["IMAGE"];
529 $question = array("ID" => null);
530 do
531 {
532 $answer = array();
533 foreach ($row as $key => $val)
534 {
535 if (mb_strpos($key, "A_") === 0)
536 $answer[mb_substr($key, 2)] = $val;
537 }
538 if ($answer["IMAGE_ID"] > 0)
539 $images[$answer["IMAGE_ID"]] = &$answer["IMAGE"];
540 if ($answer["QUESTION_ID"] != $question["ID"])
541 {
542 unset($question);
543 $question = array();
544 foreach ($row as $key => $val)
545 {
546 if (mb_strpos($key, "Q_") === 0)
547 $question[mb_substr($key, 2)] = $val;
548 }
549 $question += array(
550 "IMAGE" => null,
551 "FIELD_NAME" => \Bitrix\Vote\Event::getFieldName($vote["ID"], $question["ID"]),
552 "ANSWERS" => array()
553 );
554 if ($question["IMAGE_ID"] > 0)
555 $images[$question["IMAGE_ID"]] = &$question["IMAGE"];
556 $vote["QUESTIONS"][$question["ID"]] = &$question;
557 }
558 $answer["FIELD_NAME"] = $answer["~FIELD_NAME"] = \Bitrix\Vote\Event::getFieldName($vote["ID"], $question["ID"]);
559 $answer["MESSAGE_FIELD_NAME"] = \Bitrix\Vote\Event::getMessageFieldName($vote["ID"], $question["ID"], $answer["ID"]);
560 if (
561 $answer["FIELD_TYPE"] == \Bitrix\Vote\AnswerTypes::TEXT ||
562 $answer["FIELD_TYPE"] == \Bitrix\Vote\AnswerTypes::TEXTAREA
563 )
564 {
565 if ($question["FIELD_TYPE"] == \Bitrix\Vote\QuestionTypes::COMPATIBILITY)
566 $answer["FIELD_NAME"] = $answer["MESSAGE_FIELD_NAME"];
567 }
568 else if ($question["FIELD_TYPE"] != \Bitrix\Vote\QuestionTypes::COMPATIBILITY)
569 {
570 $answer["FIELD_TYPE"] = $question["FIELD_TYPE"];
571 }
572 $answer["~PERCENT"] = ($question["COUNTER"] > 0 ? $answer["COUNTER"] * 100 / $question["COUNTER"] : 0);
573 $answer["PERCENT"] = round($answer["~PERCENT"], 2);
574 $question["ANSWERS"][$answer["ID"]] = &$answer;
575 unset($answer);
576 } while (($row = $dbRes->fetch()) && $row);
577 unset($question);
578 //region Getting images
579 if (count($images) > 0)
580 {
581 $dbRes = \Bitrix\Main\FileTable::getList(array("select" => array("*"), "filter" => array("ID" => array_keys($images))));
582 while ($res = $dbRes->fetch())
583 {
584 $images[$res["ID"]] = $res + array("SRC" => \CFile::GetFileSRC($res));
585 }
586 }
587 //endregion
588 //region Setting data into local storages
589 foreach ($vote["QUESTIONS"] as $question)
590 {
591 $questionId = strval($question["ID"]);
592 if (!array_key_exists($questionId, Question::$storage))
593 Question::$storage[$questionId] = $question;
594 foreach ($question["ANSWERS"] as $answer)
595 {
596 if (!array_key_exists($answer["ID"], Answer::$storage))
597 Answer::$storage[$answer["ID"]] = $answer;
598 }
599 }
600 self::$storage[$id] = $vote;
601 //endregion
602 }
603 }
604 return self::$storage[$id];
605 }
606
638 public static function checkData(array &$data, $voteId = 0)
639 {
640 $result = new AddResult();
641 $questionsToRevise = [];
642 if ($voteId > 0)
643 {
644 $result = new UpdateResult();
645 $vote = static::getData($voteId);
646 if (is_null($vote))
647 throw new ArgumentException(Loc::getMessage("VOTE_VOTE_NOT_FOUND", array("#ID#", $voteId)));
648 $questionsToRevise = ($vote["QUESTIONS"] ?: []);
649 }
650 $questionsToSave = isset($data["QUESTIONS"]) && is_array($data["QUESTIONS"]) ? $data["QUESTIONS"] : [];
651 unset($data["QUESTIONS"]);
652 VoteTable::checkFields($result, ["ID" => $voteId], $data);
653 if (!$result->isSuccess())
654 throw new ArgumentException(implode("", $result->getErrorMessages()));
655 /************** Check Data *****************************************/
656 $questions = array();
657 foreach ($questionsToSave as $key => $question)
658 {
659 if (($question["DEL"] ?? null) == "Y")
660 continue;
661
662 $question["ID"] = intval($question["ID"] ?? null);
663 $question = array(
664 "ID" => (array_key_exists($question["ID"], $questionsToRevise) ? $question["ID"] : null),
665 "QUESTION" => trim($question["QUESTION"]),
666 "QUESTION_TYPE" => trim($question["QUESTION_TYPE"]),
667 "FIELD_TYPE" => $question["FIELD_TYPE"],
668 "ANSWERS" => (is_array($question["ANSWERS"]) ? $question["ANSWERS"] : array()));
669
670 $savedAnswers = ($question["ID"] > 0 ? $questionsToRevise[$question["ID"]]["ANSWERS"] : array());
671 $newAnswers = array();
672 foreach ($question["ANSWERS"] as $keya => $answer)
673 {
674 $answer["ID"] = intval($answer["ID"] ?? null);
675 $answer["MESSAGE"] = trim($answer["MESSAGE"]);
676 if (($answer["DEL"] ?? null) != "Y" && $answer["MESSAGE"] !== "")
677 {
678 $answer = array(
679 "ID" => $answer["ID"],
680 "MESSAGE" => $answer["MESSAGE"],
681 "MESSAGE_TYPE" => $answer["MESSAGE_TYPE"],
682 "FIELD_TYPE" => $answer["FIELD_TYPE"]);
683 if (!array_key_exists($answer["ID"], $savedAnswers))
684 unset($answer["ID"]);
685 else
686 unset($savedAnswers[$answer["ID"]]);
687 $newAnswers[] = $answer;
688 }
689 }
690 $question["ANSWERS"] = $newAnswers;
691
692 if ($question["QUESTION"] == "" && empty($question["ANSWERS"]))
693 continue;
694 else if ($question["QUESTION"] == "")
695 {
696 $result->addError(new Error(Loc::getMessage("VOTE_QUESTION_EMPTY", array("#NUMBER#" => $key)), "QUESTION_".$key));
697 }
698 else if (empty($question["ANSWERS"]))
699 {
700 $result->addError(new Error(Loc::getMessage("VOTE_ANSWERS_EMPTY", array("#QUESTION#" => HtmlFilter::encode($question["QUESTION"]))), "QUESTION_".$key));
701 }
702 else
703 {
704 foreach ($savedAnswers as $answer)
705 {
706 $question["ANSWERS"][] = $answer + array("DEL" => "Y");
707 }
708 $questions[] = $question;
709 unset($questionsToRevise[$question["ID"]]);
710 }
711 }
712 if (!$result->isSuccess())
713 {
714 throw new ArgumentException(implode("", $result->getErrorMessages()));
715 }
716 foreach ($questionsToRevise as $question)
717 {
718 $questions[] = $question + array("DEL" => "Y");
719 }
720 $data += array("QUESTIONS" => $questions);
721 return true;
722 }
723
730 public static function saveData($voteId, array $data)
731 {
732 if (!($voteId > 0) && empty($data["QUESTIONS"]))
733 {
734 return 0;
735 }
736 if ($voteId)
737 {
738 $result = VoteTable::update($voteId, $data);
739 }
740 else
741 {
742 unset($data['ID']);
743 $result = VoteTable::add($data);
744 if ($result->isSuccess())
745 $voteId = $result->getId();
746 }
747 if (!$result->isSuccess())
748 throw new ArgumentException(implode("", $result->getErrorMessages()));
749 else if ($result instanceof UpdateResult)
750 $vote = static::getData($voteId);
751 else
752 $vote = \Bitrix\Vote\VoteTable::getById($voteId)->fetch();
753 $vote += ["QUESTIONS" => []];
754 /************** Check Data *****************************************/
755 $iQuestions = 0;
756 foreach ($data["QUESTIONS"] as $question)
757 {
758 $savedAnswers = array();
759 if ($question["ID"] > 0 && array_key_exists($question["ID"], $vote["QUESTIONS"]))
760 {
761 $savedAnswers = $vote["QUESTIONS"][$question["ID"]]["ANSWERS"];
762 unset($vote["QUESTIONS"][$question["ID"]]);
763 if (isset($question["DEL"]) && $question["DEL"] === "Y")
764 {
765 \CVoteQuestion::Delete($question["ID"]);
766 continue;
767 }
768 $question["C_SORT"] = (++$iQuestions) * 10;
769 \CVoteQuestion::Update($question["ID"], $question);
770 }
771 else
772 {
773 $question["C_SORT"] = (++$iQuestions) * 10;
774 $question["VOTE_ID"] = $vote["ID"];
775 $question["ID"] = \CVoteQuestion::Add($question);
776 if ($question["ID"] <= 0)
777 continue;
778 }
779 $iAnswers = 0;
780 foreach ($question["ANSWERS"] as $answer)
781 {
782 if (!empty($answer["ID"]) && array_key_exists($answer["ID"], $savedAnswers))
783 {
784 unset($savedAnswers[$answer["ID"]]);
785 if ($answer["DEL"] == "Y")
786 {
787 \CVoteAnswer::Delete($answer["ID"]);
788 continue;
789 }
790 $answer["C_SORT"] = (++$iAnswers)* 10;
791 \CVoteAnswer::Update($answer["ID"], $answer);
792 }
793 else
794 {
795 $answer["QUESTION_ID"] = $question["ID"];
796 $answer["C_SORT"] = (++$iAnswers) * 10;
797 $answer["ID"] = intval(\CVoteAnswer::Add($answer));
798 if ($answer["ID"] <= 0)
799 continue;
800 }
801 }
802 if ($iAnswers <= 0)
803 {
804 \CVoteQuestion::Delete($question["ID"]);
805 $iQuestions--;
806 }
807 else if (!empty($savedAnswers))
808 {
809 while ($answer = array_pop($savedAnswers))
810 \CVoteAnswer::Delete($answer["ID"]);
811 }
812 }
813 if ($iQuestions <= 0)
814 {
815 Vote::delete($vote["ID"]);
816 $vote["ID"] = 0;
817 }
818 return $vote["ID"];
819 }
820
828 public function sendVotingMessage(array $event, $vote, $type = "im")
829 {
830 if ($type == "im" && \Bitrix\Main\Loader::includeModule("im"))
831 {
832 $url = "";
833 if (!empty($vote["URL"]))
834 {
835 if (defined("SITE_SERVER_NAME"))
836 $url = SITE_SERVER_NAME;
837 $url = (!empty($url) ? $url : \COption::GetOptionString("main", "server_name"));
838 if (!empty($url))
839 $url = (\CMain::IsHTTPS() ? "https" : "http") . "://" . $url . $vote["URL"];
840 }
841
842 // send notification
843 $gender = "";
844 if ($event["VISIBLE"] == "Y" && $this->getUser()->getParam("PERSONAL_GENDER") == "F")
845 $gender = "_F";
846 $res = array(
847 "MESSAGE_TYPE" => IM_MESSAGE_SYSTEM,
848 "TO_USER_ID" => $vote["AUTHOR_ID"],
849 "FROM_USER_ID" => ( $event["VISIBLE"] == "Y" ? $this->getUser()->getId() : 0),
850 "NOTIFY_TYPE" => IM_NOTIFY_FROM,
851 "NOTIFY_MODULE" => "vote",
852 "NOTIFY_EVENT" => "voting",
853 "NOTIFY_TAG" => "VOTING|" . $vote["ID"],
854 "NOTIFY_MESSAGE" => (!empty($vote["URL"]) ?
855 Loc::getMessage("V_NOTIFY_MESSAGE_HREF" . $gender, array("#VOTE_TITLE#" => $vote["TITLE"], "#VOTE_URL#" => $vote["URL"])) :
856 Loc::getMessage("V_NOTIFY_MESSAGE" . $gender, array("#VOTE_TITLE#" => $vote["TITLE"]))),
857 "NOTIFY_MESSAGE_OUT" => (!empty($url) ?
858 Loc::getMessage("V_NOTIFY_MESSAGE_OUT_HREF" . $gender, array("#VOTE_TITLE#" => $vote["TITLE"], "#VOTE_URL#" => $url)) :
859 Loc::getMessage("V_NOTIFY_MESSAGE" . $gender, array("#VOTE_TITLE#" => $vote["TITLE"])))
860 );
861 \CIMNotify::Add($res);
862 }
863 else
864 {
865 $channel = $this->getChannel();
866 // send e-mail
867 $dbUser = \CUser::getById($vote["AUTHOR_ID"]);
868 if ($dbUser && ($u = $dbUser->Fetch()) && !empty($u["EMAIL"]))
869 {
870 $eventFields = array(
871 "EMAIL_TO" => $u["EMAIL"],
872 "VOTE_STATISTIC" => "",
873 "ID" => $event["EVENT_ID"],
874 "TIME" => GetTime(time(), "FULL"),
875 "VOTE_TITLE" => $vote["TITLE"],
876 "VOTE_DESCRIPTION" => $vote["DESCRIPTION"],
877 "VOTE_ID" => $vote["ID"],
878 "VOTE_COUNTER" => $vote["COUNTER"],
879 "URL" => $vote["URL"],
880 "CHANNEL" => $channel["TITLE"],
881 "CHANNEL_ID" => $channel["ID"],
882 "VOTER_ID" => $event["VOTE_USER_ID"],
883 "USER_NAME" => ($event["VISIBLE"] == "Y" ? $this->getUser()->getFullName() : "Hidden"),
884 "LOGIN" => ($event["VISIBLE"] == "Y" ? $this->getUser()->getLogin() : "hidden"),
885 "USER_ID" => ($event["VISIBLE"] == "Y" ? $this->getUser()->getID() : 0),
886 "STAT_GUEST_ID" => intval($_SESSION["SESS_GUEST_ID"]),
887 "SESSION_ID" => intval($_SESSION["SESS_SESSION_ID"]),
888 "IP" => \Bitrix\Main\Context::getCurrent()->getServer()->get("REMOTE_ADDR")
889 );
890 $eventFields["USER_NAME"] = (!!$eventFields["USER_NAME"] && $event["VISIBLE"] == "Y" ? $eventFields["USER_NAME"] : $eventFields["LOGIN"]);
891 // VOTE_STATISTIC
892 $text = array();
893 foreach ($this["QUESTIONS"] as $question)
894 {
895 if (array_key_exists($question["ID"], $event["BALLOT"]))
896 {
897 $text[$question["ID"]] = array();
898 foreach ($question["ANSWERS"] as $answer)
899 {
900 if (array_key_exists($answer["ID"], $event["BALLOT"][$question["ID"]]["ANSWERS"]))
901 {
902 if (($answer["FIELD_TYPE"] == \Bitrix\Vote\AnswerTypes::TEXT || $answer["FIELD_TYPE"] == \Bitrix\Vote\AnswerTypes::TEXTAREA) &&
903 $event["BALLOT"][$question["ID"]]["ANSWERS"][$answer["ID"]]["MESSAGE"] !== "")
904 {
905 $text[$question["ID"]][] = $event["BALLOT"][$question["ID"]]["ANSWERS"][$answer["ID"]]["MESSAGE"];
906 }
907 else
908 {
909 $text[$question["ID"]][] = $answer["MESSAGE"];
910 }
911 }
912 }
913 if (!empty($text[$question["ID"]]))
914 {
915 $text[$question["ID"]] = " - " . $question["QUESTION"] . "\n - " . implode(", ", $text[$question["ID"]]);
916 }
917 else
918 {
919 $text[$question["ID"]] = " - " . $question["QUESTION"] . "\n - ...\n";
920 }
921 }
922 }
923 $eventFields["VOTE_STATISTIC"] = "\n" . implode("\n\n", $text);
924 $arrSites = \CVoteChannel::GetSiteArray($channel["ID"]);
925 \CEvent::Send("VOTE_FOR", $arrSites, $eventFields, "N");
926 }
927 }
928
929 return true;
930 }
931
936 public function fillStatistic()
937 {
938 foreach ($this->questions as &$qs)
939 foreach ($qs["ANSWERS"] as &$as)
940 $as["STAT"] = array();
941
942 $dbRes = \Bitrix\Vote\EventTable::getList(array(
943 "select" => array(
944 "V_" => "*",
945 "Q_" => "QUESTION.*",
946 "A_" => "QUESTION.ANSWER.*",
947 "U_ID" => "USER.USER.ID",
948 "U_NAME" => "USER.USER.NAME",
949 "U_LAST_NAME" => "USER.USER.LAST_NAME",
950 "U_SECOND_NAME" => "USER.USER.SECOND_NAME",
951 "U_LOGIN" => "USER.USER.LOGIN",
952 "U_PERSONAL_PHOTO" => "USER.USER.PERSONAL_PHOTO",
953 ),
954 "filter" => array("VOTE_ID" => $this->id, "VALID" => "Y"),
955 "order" => array(
956 "USER.USER.LAST_NAME" => "ASC",
957 "USER.USER.NAME" => "ASC",
958 "USER.USER.LOGIN" => "ASC"
959 )
960 ));
961 while ($dbRes && ($res = $dbRes->fetch()))
962 {
963 if (array_key_exists($res["Q_QUESTION_ID"], $this->questions) &&
964 array_key_exists($res["A_ANSWER_ID"], $this->questions[$res["Q_QUESTION_ID"]]["ANSWERS"]))
965 {
966 $stat = &$this->questions[$res["Q_QUESTION_ID"]]["ANSWERS"][$res["A_ANSWER_ID"]]["STAT"];
967 $result = array(
968 "USER" => array(
969 "ID" => 0,
970 ),
971 "MESSAGE" => $res["A_MESSAGE"]
972 );
973 if ($this["ANONYMITY"] !== \Bitrix\Vote\Vote\Anonymity::ANONYMOUSLY &&
974 $res["V_VISIBLE"] == "Y" && $res["U_ID"] > 0)
975 {
976 $result["USER"] = array(
977 "ID" => $res["U_ID"],
978 "NAME" => $res["U_NAME"],
979 "LAST_NAME" => $res["U_LAST_NAME"],
980 "SECOND_NAME" => $res["U_SECOND_NAME"],
981 "LOGIN" => $res["U_LOGIN"],
982 "PERSONAL_PHOTO" => $res["U_PERSONAL_PHOTO"],
983 );
984 }
985 $stat[$res["A_ID"]] = $result;
986 }
987 }
988 }
989
990 public function getStatistic() {
991 $dbRes = \Bitrix\Vote\EventTable::getList(array(
992 "select" => array(
993 "V_" => "*",
994 "Q_" => "QUESTION.*",
995 "A_" => "QUESTION.ANSWER.*",
996 "U_ID" => "USER.USER.ID",
997 "U_NAME" => "USER.USER.NAME",
998 "U_LAST_NAME" => "USER.USER.LAST_NAME",
999 "U_SECOND_NAME" => "USER.USER.SECOND_NAME",
1000 "U_PERSONAL_PHOTO" => "USER.USER.PERSONAL_PHOTO",
1001 ),
1002 "filter" => array("VOTE_ID" => $this->id, "VALID" => "Y"),
1003 "order" => array(
1004 "USER.USER.LAST_NAME" => "ASC",
1005 "USER.USER.NAME" => "ASC",
1006 "USER.USER.LOGIN" => "ASC"
1007 )
1008 ));
1009 $result = [];
1010 while ($dbRes && ($res = $dbRes->fetch()))
1011 {
1012 if (!array_key_exists($res["V_ID"], $result))
1013 {
1014 $result[$res["V_ID"]] = [
1015 "ID" => $res["V_ID"],
1016 "DATE" => $res["V_DATE_VOTE"],
1017 "VISIBLE" => ($this["ANONYMITY"] !== \Bitrix\Vote\Vote\Anonymity::ANONYMOUSLY &&
1018 $res["V_VISIBLE"] == "Y" ? "Y" : "N"),
1019 "BALLOT" => [],
1020 "USER" => ["ID" => 0]
1021 ];
1022 if ($result[$res["V_ID"]]["VISIBLE"] == "Y" && $res["U_ID"] > 0)
1023 {
1024 $result[$res["V_ID"]]["USER"] = array(
1025 "ID" => $res["U_ID"],
1026 "NAME" => $res["U_NAME"],
1027 "LAST_NAME" => $res["U_LAST_NAME"],
1028 "SECOND_NAME" => $res["U_SECOND_NAME"],
1029 "LOGIN" => $res["U_LOGIN"],
1030 "PERSONAL_PHOTO" => $res["U_PERSONAL_PHOTO"],
1031 );
1032 }
1033 }
1034 $ballot = &$result[$res["V_ID"]]["BALLOT"];
1035 if (!array_key_exists($res["Q_QUESTION_ID"], $ballot))
1036 {
1037 $ballot[$res["Q_QUESTION_ID"]] = [];
1038 }
1039 $ballot[$res["Q_QUESTION_ID"]][$res["A_ANSWER_ID"]] = trim($res["A_MESSAGE"]);
1040 }
1041 return $result;
1042 }
1046 public function getChannel()
1047 {
1048 if ($this->channel === null)
1049 {
1050 $this->channel = array();
1051 $db = Channel::getList(array());
1052 while (($res = $db->fetch()) && $res)
1053 {
1054 if ($this->vote["CHANNEL_ID"] == $res["ID"])
1055 {
1056 $this->channel = $res;
1057 break;
1058 }
1059 }
1060 }
1061 return $this->channel;
1062 }
1063
1068 public function get($key)
1069 {
1070 return $this->vote[$key];
1071 }
1072
1078 public function getQuestion(int $id)
1079 {
1080 if (array_key_exists($id, $this->questions))
1081 return $this->questions[$id];
1082 return null;
1083 }
1087 public function getQuestions()
1088 {
1089 return $this->questions;
1090 }
1091
1096 public function resume()
1097 {
1098 VoteTable::update($this->id, ["DATE_END" => (new DateTime())->add("1Y")]);
1099 $this->clearCache();
1100 }
1101
1106 public function stop()
1107 {
1108 VoteTable::update($this->id, ["DATE_END" => new DateTime()]);
1109 $this->clearCache();
1110 }
1111
1117 public static function delete($id)
1118 {
1119 // @todo delete all attaches
1120 return \CVote::Delete($id);
1121 }
1122
1127 public function clearCache()
1128 {
1129 global $VOTE_CACHE;
1130 unset($VOTE_CACHE["VOTE"][$this->id]);
1131 unset(self::$storage[$this->id]);
1132 unset(self::$canVoteStorage[$this->id]);
1133 }
1138 private function clearVotingCache()
1139 {
1140 global $VOTE_CACHE;
1141 unset($VOTE_CACHE["VOTE_CACHE_VOTING"][$this->id]);
1142 unset(self::$canVoteStorage[$this->id]);
1143 }
1144
1150 public function exportExcel($type = "html")
1151 {
1152 global $APPLICATION;
1153 $nameTemplate = Context::getCurrent()->getCulture()->getFormatName();
1154 $dateTemplate = \Bitrix\Main\Type\DateTime::getFormat();
1155
1156 $APPLICATION->restartBuffer();
1157 while(ob_get_clean());
1158 header("Content-Transfer-Encoding: binary");
1159
1160 $statistic = $this->getStatistic();
1161 $table1 = ["body" => []];
1162 $table2 = [
1163 "head" => [Loc::getMessage("V_EXPORT_DATE"), Loc::getMessage("V_EXPORT_NAME")],
1164 "body" => []
1165 ];
1166
1167 foreach ($statistic as $event)
1168 {
1169 $user = Loc::getMessage("VOTE_GUEST");
1170 if ($event["VISIBLE"] !== "Y")
1171 {
1172 $user = Loc::getMessage("VOTE_ANONYMOUSLY");
1173 }
1174 else if ($event["USER"]["ID"] > 0)
1175 {
1176 $user = \CUser::formatName($nameTemplate, $event["USER"], true, false);
1177 }
1178 /*@var \Bitrix\Main\Type\DateTime $event["DATE"] */
1179 $row = [
1180 "DATE" => $event["DATE"]->toUserTime()->format($dateTemplate),
1181 "USER" => $user
1182 ];
1183
1184 foreach ($this->questions as $questionId => $question)
1185 {
1186 $answerMessage = [];
1187 if (array_key_exists($questionId, $event["BALLOT"]))
1188 {
1189 foreach ($question["ANSWERS"] as $answerId => $answer)
1190 {
1191 if (array_key_exists($answerId, $event["BALLOT"][$questionId]))
1192 {
1193 if (!array_key_exists("STAT", $this->questions[$questionId]["ANSWERS"][$answerId]))
1194 $this->questions[$questionId]["ANSWERS"][$answerId]["STAT"] = [];
1195 $stat = &$this->questions[$questionId]["ANSWERS"][$answerId]["STAT"];
1196 if ($event["BALLOT"][$questionId][$answerId] <> '')
1197 {
1198 $stat[$event["ID"]] = $row["USER"]." (".$event["BALLOT"][$questionId][$answerId].")";
1199 $answerMessage[] = $event["BALLOT"][$questionId][$answerId];
1200 }
1201 else
1202 {
1203 $answerMessage[] = $answer["MESSAGE"];
1204 $stat[$event["ID"]] = $row["USER"];
1205 }
1206 }
1207 }
1208 }
1209 $row[] = implode(", ", $answerMessage);
1210 }
1211 $table2["body"][] = array_values($row);
1212 }
1213 foreach ($this->questions as $questionId => $question)
1214 {
1215 $table1["body"][] = [$question["QUESTION"], "", "", ""];
1216 foreach ($question["ANSWERS"] as $answerId => $answer)
1217 {
1218 $table1["body"][] = ["", $answer["MESSAGE"], $answer["COUNTER"], (array_key_exists("STAT", $answer) ? implode(", ", $answer["STAT"]) : "")];
1219 }
1220 $table2["head"][] = $question["QUESTION"];
1221 }
1222
1223 if ($type === "csv")
1224 {
1225 Header("Content-Type: ". MimeType::getByFileExtension("csv"));
1226 header("Content-Disposition: attachment;filename=vote".$this->id.".csv");
1227
1228 $f = fopen("php://output", "w");
1229 fputcsv($f, $table2["head"], ';');
1230 foreach ($table2["body"] as $row) {
1231 fputcsv($f, $row, ';');
1232 }
1233 fclose($f);
1234 }
1235 else
1236 {
1237 $mess = [
1238 "GENERAL_INFO" => Loc::getMessage("V_EXPORT_GENERAL_INFO"),
1239 "STATISTIC" => Loc::getMessage("V_EXPORT_STATISTIC")
1240 ];
1241 if ($type === "xls")
1242 {
1243 $bodyRows = [];
1244 foreach ($table1["body"] as $row)
1245 {
1246 $bodyRows[] = implode("</Data></Cell><Cell ss:StyleID=\"bold\"><Data ss:Type=\"String\">", $row);
1247 }
1248 $table1["body"] = implode("</Data></Cell></Row><Row><Cell><Data ss:Type=\"String\">", $bodyRows);
1249
1250 $table2["head"] = implode("</Data></Cell><Cell ss:StyleID=\"bold\"><Data ss:Type=\"String\">", $table2["head"]);
1251 $bodyRows = [];
1252 foreach ($table2["body"] as $row)
1253 {
1254 $bodyRows[] = implode("</Data></Cell><Cell ss:StyleID=\"bold\"><Data ss:Type=\"String\">", $row);
1255 }
1256 $table2["body"] = implode("</Data></Cell></Row><Row><Cell><Data ss:Type=\"String\">", $bodyRows);
1257 $LANG_CHARSET = LANG_CHARSET;
1258
1259 $res = <<<XML
1260<?xml version="1.0" charset="{$LANG_CHARSET}"?>
1261<?mso-application progid="Excel.Sheet"?>
1262<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:html="http://www.w3.org/TR/REC-html40">
1263 <Styles>
1264 <Style ss:ID="bold">
1265 <Font ss:Bold="1"/>
1266 </Style>
1267 </Styles>
1268 <Worksheet ss:Name="{$mess["GENERAL_INFO"]}">
1269 <Table>
1270 <Row>
1271 <Cell><Data ss:Type="String">{$table1["body"]}</Data></Cell>
1272 </Row>
1273 </Table>
1274 </Worksheet>
1275 <Worksheet ss:Name="{$mess["STATISTIC"]}">
1276 <Table>
1277 <Row>
1278 <Cell ss:StyleID="bold"><Data ss:Type="String">{$table2["head"]}</Data></Cell>
1279 </Row>
1280 <Row>
1281 <Cell><Data ss:Type="String">{$table2["body"]}</Data></Cell>
1282 </Row>
1283 </Table>
1284 </Worksheet>
1285</Workbook>
1286XML;
1287 }
1288 else
1289 {
1290 $LANG_CHARSET = LANG_CHARSET;
1291
1292 $bodyRows = [];
1293 foreach ($table1["body"] as $row)
1294 {
1295 $bodyRows[] = implode("</td><td>", $row);
1296 }
1297 $table1["body"] = implode("</td></tr><tr><td>", $bodyRows);
1298
1299 $table2["head"] = implode("</th><th>", $table2["head"]);
1300 $bodyRows = [];
1301 foreach ($table2["body"] as $row)
1302 {
1303 $bodyRows[] = implode("</td><td>", $row);
1304 }
1305 $table2["body"] = implode("</td></tr><tr><td>", $bodyRows);
1306
1307 $res = <<<HTML
1308<meta http-equiv="Content-type" content="text/html;charset={$LANG_CHARSET}" />
1309<p>
1310<table border="1">
1311 <tbody>
1312 <tr><td>{$table1["body"]}</td></tr>
1313 </tbody>
1314</table>
1315</p>
1316<p>
1317<table border="1">
1318 <thead><tr><th>{$table2["head"]}</th></tr></thead>
1319 <tbody><tr><td>{$table2["body"]}</td></tr></tbody>
1320</table>
1321</p>
1322HTML;
1323 }
1324 header("Content-Type: ". MimeType::getByFileExtension("xls"));
1325 header("Content-Disposition: attachment;filename=vote".$this->id.".xls");
1326 echo $res;
1327 }
1328 \CMain::finalActions();
1329 die();
1330 }
1331
1332 private function getDataFromRequest(array $request)
1333 {
1334 $res = \Bitrix\Vote\Event::getDataFromRequest($this->getId(), $request);
1335 if ($res !== null)
1336 {
1337 $data = $res;
1338 }
1339 else
1340 {
1341 $questions = $this->getQuestions();
1342 $data = ["EXTRAS" => [], "BALLOT" => [], "MESSAGE" => []];
1343
1344 foreach ($questions as $question)
1345 {
1346 $data["BALLOT"][$question["ID"]] = array();
1347 foreach ($question["ANSWERS"] as $answer)
1348 {
1349 $fieldType = (
1350 $question["FIELD_TYPE"] == \Bitrix\Vote\QuestionTypes::COMPATIBILITY ||
1351 $answer["FIELD_TYPE"] == AnswerTypes::TEXTAREA || $answer["FIELD_TYPE"] == AnswerTypes::TEXT ?
1352 $answer["FIELD_TYPE"] : $question["FIELD_TYPE"]);
1353
1354 switch ($fieldType)
1355 {
1356 case AnswerTypes::RADIO :
1358 $fieldName = ($fieldType == AnswerTypes::RADIO ? "vote_radio_" : "vote_dropdown_").$question["ID"];
1359 if ($request[$fieldName] == $answer["ID"])
1360 $data["BALLOT"][$question["ID"]][$answer["ID"]] = true;
1361 break;
1364 $fieldName = ($fieldType == AnswerTypes::CHECKBOX ? "vote_checkbox_" : "vote_multiselect_").$question["ID"];
1365 if (array_key_exists($fieldName, $request) && is_array($request[$fieldName]) && in_array($answer["ID"], $request[$fieldName]))
1366 $data["BALLOT"][$question["ID"]][$answer["ID"]] = true;
1367 break;
1368 default :
1369 $fieldName = ($answer["FIELD_TYPE"] == AnswerTypes::TEXT ? "vote_field_" : "vote_memo_") . $answer["ID"];
1370 $value = trim($request[$fieldName]);
1371 if ($value <> '')
1372 {
1373 if (!array_key_exists($question["ID"], $data["MESSAGE"]))
1374 $data["MESSAGE"][$question["ID"]] = [];
1375 $data["MESSAGE"][$question["ID"]][$answer["ID"]] = $value;
1376 $data["BALLOT"][$question["ID"]][$answer["ID"]] = true;
1377 }
1378 break;
1379 }
1380 }
1381 }
1382 }
1383 return $data;
1384 }
1403 public function voteFor(array $request, $params = [])
1404 {
1405 return $this->registerEvent($this->getDataFromRequest($request), $params, User::getCurrent());
1406 }
1407
1408 public function registerEvent(array $data, array $params, \Bitrix\Vote\User $user)
1409 {
1410 if ($this["LAMP"] == "red")
1411 {
1412 throw new AccessDeniedException(Loc::getMessage("VOTE_IS_NOT_ACTIVE"));
1413 }
1414 $voteId = $this->getId();
1415 if ($user->lock($voteId) !== true)
1416 {
1417 throw new AccessDeniedException(Loc::getMessage("VOTE_IS_OCCUPIED"));
1418 }
1419
1420 $userId = $user->getId();
1421
1422 $this->errorCollection->clear();
1423
1425 if ($params["revote"] != true)
1426 {
1427 $result = $this->canVote($user);
1428 }
1429 //region Delete event If It is possible
1430 else if (
1431 ($result = $this->canRevote($user))
1432 && $result->isSuccess()
1433 && !empty($result->getData())
1434 && ($eventIdsToDelete = array_column($result->getData(), 'ID'))
1435 && !empty($eventIdsToDelete)
1436 )
1437 {
1438 $dbRes = \Bitrix\Vote\EventTable::getList([
1439 "select" => [
1440 "V_" => "*",
1441 "Q_" => "QUESTION.*",
1442 "A_" => "QUESTION.ANSWER.*"],
1443 "filter" => [
1444 "VOTE_ID" => $voteId,
1445 "ID" => $eventIdsToDelete],
1446 "order" => [
1447 "ID" => "ASC",
1448 "QUESTION.ID" => "ASC",
1449 "QUESTION.ANSWER.ID" => "ASC"]
1450 ]);
1451 if ($dbRes && ($res = $dbRes->fetch()))
1452 {
1453 if (\Bitrix\Main\Loader::includeModule("im"))
1454 {
1455 \CIMNotify::DeleteByTag("VOTING|".$voteId, $userId);
1456 }
1457 $vEId = 0;
1458 $qEId = 0;
1459 do
1460 {
1461 if ($vEId < $res["V_ID"])
1462 {
1463 $vEId = $res["V_ID"];
1464 \Bitrix\Vote\Event::deleteEvent(intval($res["V_ID"]));
1465 $this->vote["COUNTER"] = max($this->vote["COUNTER"] - 1, 0);
1466 }
1467 if (array_key_exists($res["Q_QUESTION_ID"], $this->questions) &&
1468 array_key_exists($res["A_ANSWER_ID"], $this->questions[$res["Q_QUESTION_ID"]]["ANSWERS"]))
1469 {
1470 if ($qEId < $res["Q_ID"])
1471 {
1472 $qEId = $res["Q_ID"];
1473 $this->questions[$res["Q_QUESTION_ID"]]["COUNTER"] = max($this->questions[$res["Q_QUESTION_ID"]]["COUNTER"] - 1, 0);
1474 }
1475
1476 $this->questions[$res["Q_QUESTION_ID"]]["ANSWERS"][$res["A_ANSWER_ID"]]["COUNTER"] = max(
1477 $this->questions[$res["Q_QUESTION_ID"]]["ANSWERS"][$res["A_ANSWER_ID"]]["COUNTER"] - 1,
1478 0);
1479 if ($this->questions[$res["Q_QUESTION_ID"]]["COUNTER"] > 0)
1480 {
1481 $this->questions[$res["Q_QUESTION_ID"]]["ANSWERS"][$res["A_ANSWER_ID"]]["~PERCENT"] =
1482 $this->questions[$res["Q_QUESTION_ID"]]["ANSWERS"][$res["A_ANSWER_ID"]]["COUNTER"] * 100 /
1483 $this->questions[$res["Q_QUESTION_ID"]]["COUNTER"];
1484 $this->questions[$res["Q_QUESTION_ID"]]["ANSWERS"][$res["A_ANSWER_ID"]]["PERCENT"] = round($this->questions[$res["Q_QUESTION_ID"]]["ANSWERS"][$res["A_ANSWER_ID"]]["~PERCENT"], 2);
1485 }
1486 else
1487 {
1488 $this->questions[$res["Q_QUESTION_ID"]]["ANSWERS"][$res["A_ANSWER_ID"]]["~PERCENT"] = 0;
1489 $this->questions[$res["Q_QUESTION_ID"]]["ANSWERS"][$res["A_ANSWER_ID"]]["PERCENT"] = 0;
1490 }
1491 }
1492 } while ($dbRes && ($res = $dbRes->fetch()));
1493 $this->clearCache();
1494 $this->clearVotingCache();
1495 }
1496 $result = $this->canVote($user);
1497 }
1498 //endregion
1499
1500 if (!$result->isSuccess())
1501 {
1502 $this->errorCollection->add($result->getErrors());
1503 }
1504 else
1505 {
1506 $event = new \Bitrix\Vote\Event($this);
1510 $eventFields = array(
1511 "VOTE_USER_ID" => \Bitrix\Vote\User::getCurrent()->setVotedUserId(true),
1512 "DATE_VOTE" => (new DateTime()),
1513 "STAT_SESSION_ID" => $_SESSION["SESS_SESSION_ID"],
1514 "IP" => \Bitrix\Main\Context::getCurrent()->getServer()->get("REMOTE_ADDR"),
1515 "VALID" => "Y",
1516 "VISIBLE" => ($this["ANONYMITY"] == \Bitrix\Vote\Vote\Anonymity::ANONYMOUSLY ? "N" : "Y") // can be replaced from $data array ["EXTRAS"]["HIDDEN"] = "Y"
1517 );
1518 if (!$event->check($data)
1519 || !($eventResult = $event->add($eventFields, $data))
1520 )
1521 {
1522 $this->errorCollection->add($event->getErrors());
1523 }
1524 else
1525 {
1526 $this->vote["COUNTER"]++;
1527 foreach ($eventResult->get("BALLOT") as $questionId => $question)
1528 {
1529 $this->questions[$questionId]["COUNTER"]++;
1530 foreach ($question["ANSWERS"] as $answerId => $answerEventParams)
1531 {
1532 $this->questions[$questionId]["ANSWERS"][$answerId]["COUNTER"]++;
1533 }
1534 }
1535 foreach ($this->questions as $questionId => $question)
1536 {
1537 foreach ($question["ANSWERS"] as $answerId => $answerEventParams)
1538 {
1539 if ($this->questions[$questionId]["ANSWERS"][$answerId]["COUNTER"] > 0)
1540 {
1541 $this->questions[$questionId]["ANSWERS"][$answerId]["~PERCENT"] =
1542 $this->questions[$questionId]["ANSWERS"][$answerId]["COUNTER"] * 100 /
1543 $this->questions[$questionId]["COUNTER"];
1544 $this->questions[$questionId]["ANSWERS"][$answerId]["PERCENT"] = round($this->questions[$questionId]["ANSWERS"][$answerId]["~PERCENT"], 2);
1545 }
1546 else
1547 {
1548 $this->questions[$questionId]["ANSWERS"][$answerId]["~PERCENT"] = 0;
1549 $this->questions[$questionId]["ANSWERS"][$answerId]["PERCENT"] = 0;
1550 }
1551 }
1552 }
1553 self::$statStorage[] = $voteId;
1554 $_SESSION["VOTE"]["VOTES"][$voteId] = $eventResult->get("EVENT_ID");
1555 // statistic module
1556 if (\Bitrix\Main\Loader::includeModule("statistic"))
1557 {
1558 $event3 = $this["EVENT3"];
1559 if (empty($event3))
1560 {
1561 $event3 = (\Bitrix\Main\Context::getCurrent()->getRequest()->isHttps() ? "https://" : "http://") .
1562 \Bitrix\Main\Context::getCurrent()->getServer()->getHttpHost() .
1563 "/bitrix/admin/vote_user_results.php?EVENT_ID=" . $eventResult->get("EVENT_ID") . "&lang=" . LANGUAGE_ID;
1564 }
1565 \CStatEvent::AddCurrent($this["EVENT1"], $this["EVENT2"], $event3);
1566 }
1567 // notification TODO replace this functional into other function
1568 if ($this["NOTIFY"] !== "N" && $this["AUTHOR_ID"] > 0 && $this["AUTHOR_ID"] != $userId)
1569 {
1570 self::sendVotingMessage($eventResult->toArray(), $this, ($this["NOTIFY"] == "I" ? "im" : "mail"));
1571 }
1572
1573 /***************** Event onAfterVoting *****************************/
1574 foreach (GetModuleEvents("vote", "onAfterVoting", true) as $ev)
1575 {
1576 ExecuteModuleEventEx($ev, array($voteId, $eventResult->get("EVENT_ID"), $userId));
1577 }
1578 /***************** /Event ******************************************/
1579 }
1580 }
1581 $user->unlock($voteId);
1582 return $this->errorCollection->isEmpty();
1583 }
1584
1590 public function isVotedFor($userId)
1591 {
1592 $result = false;
1593 $user = ($userId instanceof User ? $userId : ($userId == $this->getUser()->getId() ? User::getCurrent() : User::loadFromId($userId)));
1594 $canVoteResult = $this->canVote($user);
1595 if (!$canVoteResult->isSuccess())
1596 {
1597 $result = 0;
1598 for (
1599 $canVoteResult->getErrorCollection()->rewind();
1600 $canVoteResult->getErrorCollection()->valid();
1601 $canVoteResult->getErrorCollection()->next()
1602 )
1603 {
1605 $error = $canVoteResult->getErrorCollection()->current();
1606 $result |= $error->getCode();
1607 }
1608 }
1609 return $result;
1610 }
1616 public function canRead($userId)
1617 {
1618 if (parent::canEdit($userId))
1619 return true;
1620 else if (parent::canRead($userId))
1621 {
1622 $groups = parent::loadUserGroups($userId);
1623 $dbRes = Channel::getList(array(
1624 "select" => array("*"),
1625 "filter" => array(
1626 "ACTIVE" => "Y",
1627 "HIDDEN" => "N",
1628 ">=PERMISSION.PERMISSION" => 1,
1629 "PERMISSION.GROUP_ID" => $groups
1630 ),
1631 "order" => array(
1632 "TITLE" => "ASC"
1633 ),
1634 "group" => array("ID")
1635 ));
1636 while ($res = $dbRes->fetch())
1637 {
1638 if ($res["ID"] == $this->get("CHANNEL_ID"))
1639 return true;
1640 }
1641 }
1642 return false;
1643 }
1644
1650 public function canEdit($userId)
1651 {
1652 if (parent::canEdit($userId))
1653 return true;
1654 else if (parent::canRead($userId))
1655 {
1656 $groups = parent::loadUserGroups($userId);
1657 $dbRes = Channel::getList(array(
1658 "select" => array("*"),
1659 "filter" => array(
1660 "ACTIVE" => "Y",
1661 "HIDDEN" => "N",
1662 ">=PERMISSION.PERMISSION" => 4,
1663 "PERMISSION.GROUP_ID" => $groups
1664 ),
1665 "order" => array(
1666 "TITLE" => "ASC"
1667 ),
1668 "group" => array("ID")
1669 ));
1670 while ($res = $dbRes->fetch())
1671 {
1672 if ($res["ID"] == $this->get("CHANNEL_ID"))
1673 return true;
1674 }
1675 }
1676 return false;
1677 }
1678
1679 public function canParticipate($userId)
1680 {
1681 return $this->canRead($userId) && $this->vote["LAMP"] == "green";
1682 }
1683
1692 public function canVote($user)
1693 {
1694 $vote = $this;
1695 $voteId = intval($vote["ID"]);
1696 if (!($user instanceof \Bitrix\Vote\User))
1697 {
1698 $user = \Bitrix\Vote\User::loadFromId($user);
1699 }
1700 if (!array_key_exists($voteId, self::$canVoteStorage))
1701 {
1702 self::$canVoteStorage[$voteId] = [];
1703 }
1704 if (array_key_exists($user->getId(), self::$canVoteStorage[$voteId]))
1705 {
1706 return self::$canVoteStorage[$voteId][$user->getId()];
1707 }
1708
1709 $uniqueType = intval($vote["UNIQUE_TYPE"]);
1710 $filterCard = 0;
1711
1712 $filter = ["LOGIC" => "OR"];
1713
1714 $result = new \Bitrix\Main\Result();
1715
1716 if ($uniqueType & \Bitrix\Vote\Vote\EventLimits::BY_SESSION && is_array($_SESSION["VOTE"]["VOTES"]) && array_key_exists($voteId, $_SESSION["VOTE"]["VOTES"]))
1717 {
1718 $filter["ID"] = $_SESSION["VOTE"]["VOTES"][$voteId];
1720 $result->addError(new \Bitrix\Main\Error(Loc::getMessage("VOTE_ERROR_BY_SESSION"), \Bitrix\Vote\Vote\EventLimits::BY_SESSION));
1721 }
1722 if (($uniqueType & \Bitrix\Vote\Vote\EventLimits::BY_COOKIE) && $user->getCookieId() > 0)
1723 {
1724 $filter["USER.COOKIE_ID"] = $user->getCookieId();
1726 }
1727 if ($uniqueType & \Bitrix\Vote\Vote\EventLimits::BY_IP)
1728 {
1729 $delay = intval($vote["KEEP_IP_SEC"]);
1730 $filter[] = ([
1731 "IP" => \Bitrix\Main\Context::getCurrent()->getRequest()->getRemoteAddress()] +
1732 ($delay > 0 ? [
1733 ">=DATE_VOTE" => (new \Bitrix\Main\Type\DateTime())->add("-T".$delay."S")] : []));
1735 }
1736 if (
1737 $uniqueType & \Bitrix\Vote\Vote\EventLimits::BY_USER_AUTH ||
1738 $uniqueType & \Bitrix\Vote\Vote\EventLimits::BY_USER_DATE_REGISTER ||
1739 $uniqueType & \Bitrix\Vote\Vote\EventLimits::BY_USER_ID)
1740 {
1741 if (!$user->getUser()->IsAuthorized())
1742 {
1744 $result->addError(new \Bitrix\Main\Error(Loc::getMessage("VOTE_ERROR_BY_USER_AUTH"), \Bitrix\Vote\Vote\EventLimits::BY_USER_AUTH));
1745 }
1746 else
1747 {
1748 if ($uniqueType & \Bitrix\Vote\Vote\EventLimits::BY_USER_DATE_REGISTER)
1749 {
1750 $us = \CUser::GetByID($user->getId())->fetch();
1751 if (MakeTimeStamp($vote["DATE_START"]) < MakeTimeStamp($us["DATE_REGISTER"]))
1752 {
1753 $result->addError(new \Bitrix\Main\Error(Loc::getMessage("VOTE_ERROR_BY_USER_DATE_REGISTER"), \Bitrix\Vote\Vote\EventLimits::BY_USER_DATE_REGISTER));
1754 }
1755 }
1756 if ($uniqueType & \Bitrix\Vote\Vote\EventLimits::BY_USER_ID)
1757 {
1758 $filter["USER.AUTH_USER_ID"] = $user->getId();
1760 }
1761 }
1762 }
1763
1764 if ($filterCard > 0)
1765 {
1766 $dbRes = \Bitrix\Vote\EventTable::getList([
1767 "select" => [
1768 "*",
1769 "USER_COOKIE_ID" => "USER.COOKIE_ID",
1770 "USER_AUTH_USER_ID" => "USER.AUTH_USER_ID",
1771 ],
1772 "filter" => [
1773 "VOTE_ID" => $voteId,
1774 $filter
1775 ]
1776 ]);
1777 $data = $dbRes->fetchAll();
1778 $result->setData($data);
1779 foreach ($data as $res)
1780 {
1781 if (($filterCard & \Bitrix\Vote\Vote\EventLimits::BY_COOKIE) && $res["USER_COOKIE_ID"] == $user->getCookieId())
1782 {
1783 $result->addError(new \Bitrix\Main\Error(Loc::getMessage("VOTE_ERROR_BY_COOKIE"), \Bitrix\Vote\Vote\EventLimits::BY_COOKIE));
1785 }
1786 if (($filterCard & \Bitrix\Vote\Vote\EventLimits::BY_IP) && ($res["IP"] == \Bitrix\Main\Context::getCurrent()->getRequest()->getRemoteAddress()))
1787 {
1788 if ($vote["KEEP_IP_SEC"] > 0)
1789 {
1791 $res["DATE_VOTE"]->add("T".$vote["KEEP_IP_SEC"]."S");
1792 $result->addError(new \Bitrix\Main\Error(Loc::getMessage("VOTE_ERROR_BY_IP_2", ["#DATE#" => $res["DATE_VOTE"]->toString()]), \Bitrix\Vote\Vote\EventLimits::BY_IP));
1793 }
1794 else
1795 {
1796 $result->addError(new \Bitrix\Main\Error(Loc::getMessage("VOTE_ERROR_BY_IP"), \Bitrix\Vote\Vote\EventLimits::BY_IP));
1797 }
1798 $filterCard &= ~\Bitrix\Vote\Vote\EventLimits::BY_IP;
1799 }
1800 if (($filterCard & \Bitrix\Vote\Vote\EventLimits::BY_USER_ID) && ($res["USER_AUTH_USER_ID"] == $user->getId()))
1801 {
1802 $result->addError(new \Bitrix\Main\Error(Loc::getMessage("VOTE_ERROR_BY_USER_ID"), \Bitrix\Vote\Vote\EventLimits::BY_USER_ID));
1804 }
1805 if ($filterCard <= 0)
1806 break;
1807 }
1808 }
1809 self::$canVoteStorage[$voteId][$user->getId()] = $result;
1810 return $result;
1811 }
1812
1813 public function canRevote($user)
1814 {
1815 $canVoteResult = $this->canVote($user);
1816 $result = new \Bitrix\Main\Result();
1817 if ($canVoteResult->isSuccess() || (
1818 ($this["OPTIONS"] & Vote\Option::ALLOW_REVOTE) &&
1819 $canVoteResult->getErrorCollection()->getErrorByCode(\Bitrix\Vote\Vote\EventLimits::BY_USER_ID) &&
1820 $canVoteResult->getErrorCollection()->count() == 1
1821 ))
1822 {
1823 $result->setData($canVoteResult->getData());
1824 return $result;
1825 }
1826 return $canVoteResult;
1827 }
1828
1829 public function canReadResult($user)
1830 {
1831 $result = new \Bitrix\Main\Result();
1832
1833 if (!($user instanceof \Bitrix\Vote\User))
1834 {
1835 $user = \Bitrix\Vote\User::loadFromId($user);
1836 }
1837
1838 if ($this["AUTHOR_ID"] != $user->getId())
1839 {
1840 if ($this["OPTIONS"] & Vote\Option::HIDE_RESULT)
1841 {
1842 $result->addError(new Error("Access denied.", "Hidden results"));
1843 }
1844 else if ($this["LAMP"] == "green")
1845 {
1846 $canVoteResult = $this->canVote($user);
1847 if ($canVoteResult->isSuccess())
1848 $result->addError(new Error("Access denied.", "Hidden results"));
1849 }
1850 }
1851 return $result;
1852 }
1853
1858 public function offsetExists($offset)
1859 {
1860 if ($offset == "QUESTIONS")
1861 return true;
1862 return array_key_exists($offset, $this->vote);
1863 }
1868 public function offsetGet($offset)
1869 {
1870 if (array_key_exists($offset, $this->vote))
1871 return $this->vote[$offset];
1872 else if ($offset == "QUESTIONS")
1873 return $this->questions;
1874 return null;
1875 }
1883 public function offsetSet($offset, $value)
1884 {
1885 throw new \Bitrix\Main\NotSupportedException("Model provide ArrayAccess only for reading");
1886 }
1893 public function offsetUnset($offset)
1894 {
1895 throw new \Bitrix\Main\NotSupportedException("Model provide ArrayAccess only for reading");
1896 }
1897}
static getCurrent()
Definition context.php:241
static includeModule($moduleName)
Definition loader.php:69
static loadMessages($file)
Definition loc.php:64
static getMessage($code, $replace=null, $language=null)
Definition loc.php:29
static createFromUserTime($timeString)
Definition datetime.php:180
static loadFromId($id, $shouldBeNewIfIdIsNull=false)
static getList(array $parameters)
Definition channel.php:255
static getExtrasFieldName($id, $name)
Definition event.php:530
static getFieldName($id, $questionId)
Definition event.php:522
static getCurrent()
Definition user.php:312
canEdit($userId)
Definition vote.php:1650
exportExcel($type="html")
Definition vote.php:1150
__construct($id)
Definition vote.php:463
static $storage
Definition vote.php:460
offsetUnset($offset)
Definition vote.php:1893
offsetExists($offset)
Definition vote.php:1858
sendVotingMessage(array $event, $vote, $type="im")
Definition vote.php:828
getQuestion(int $id)
Definition vote.php:1078
static $canVoteStorage
Definition vote.php:459
static $statStorage
Definition vote.php:461
offsetGet($offset)
Definition vote.php:1868
canRead($userId)
Definition vote.php:1616
canReadResult($user)
Definition vote.php:1829
static checkData(array &$data, $voteId=0)
Definition vote.php:638
offsetSet($offset, $value)
Definition vote.php:1883
static saveData($voteId, array $data)
Definition vote.php:730
canParticipate($userId)
Definition vote.php:1679
canRevote($user)
Definition vote.php:1813
voteFor(array $request, $params=[])
Definition vote.php:1403
static onAfterAdd(\Bitrix\Main\ORM\Event $event)
Definition vote.php:216
static setCounter(array $id, $increment=true)
Definition vote.php:438
static onAfterUpdate(\Bitrix\Main\ORM\Event $event)
Definition vote.php:260
static getTableName()
Definition vote.php:101