1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
history.php
См. документацию.
1<?php
2
3namespace Bitrix\Landing;
4
5use Bitrix\Landing\History\ActionFactory;
6use Bitrix\Landing\History\Action\BaseAction;
7use Bitrix\Landing\Internals\BlockTable;
8use Bitrix\Landing\Internals\HistoryTable;
9use Bitrix\Landing\Internals\HistoryStepTable;
10use Bitrix\Landing\Internals\LandingTable;
11use Bitrix\Main\Type\DateTime;
12
17{
21 public const ENTITY_TYPE_LANDING = 'L';
22
26 public const ENTITY_TYPE_DESIGNER_BLOCK = 'D';
27
28 public const AVAILABLE_TYPES = [
29 self::ENTITY_TYPE_LANDING,
30 self::ENTITY_TYPE_DESIGNER_BLOCK,
31 ];
32
37 protected static bool $isActive = false;
38
43 protected static bool $multiplyMode = false;
44
49 protected static ?int $multiplyId = null;
50
51 // todo: $multiplyId and $multiplyStep - is no static. But need getInstance method and like a singletone style
56 protected static ?int $multiplyStep = null;
57
58 protected int $entityId;
59 protected string $entityType = self::ENTITY_TYPE_LANDING;
64 protected ?int $stepRowId = null;
69 protected array $stack = [];
70 protected int $step = 0;
71 protected array $actions = [];
72
77 public static function activate(): void
78 {
79 self::$isActive = true;
80 }
81
86 public static function deactivate(): void
87 {
88 self::$isActive = false;
89 }
90
91 public static function setMultiplyMode(): void
92 {
93 self::$multiplyMode = true;
94 }
95
96 public static function unsetMultiplyMode(): void
97 {
98 self::$multiplyMode = false;
99 }
100
105 public static function isActive(): bool
106 {
107 return self::$isActive;
108 }
109
114 public function __construct(int $entityId, string $entityType)
115 {
116 if (!in_array($entityType, self::AVAILABLE_TYPES, true))
117 {
118 // todo :err or null
119 return;
120 }
121
122 $this->entityId = $entityId;
123 $this->entityType = $entityType;
124
125 $this->loadStack();
126 $this->loadStep();
127
128 if ($this->step > $this->getStackCount())
129 {
130 $this->saveStep($this->getStackCount());
131 }
132 }
133
134 protected function loadStack(): void
135 {
136 // todo: maybe cache
137 $this->stack = [];
138
139 $res = HistoryTable::query()
140 ->addSelect('*')
141 ->where('ENTITY_TYPE', '=', $this->entityType)
142 ->where('ENTITY_ID', '=', $this->entityId)
143 ->setOrder(['ID' => 'ASC'])
144 ->exec()
145 ;
146 $step = 1;
147 $multyId = null;
148 while ($row = $res->fetch())
149 {
150 $row['ID'] = (int)$row['ID'];
151 if (!is_array($row['ACTION_PARAMS']))
152 {
153 $this->fixBrokenStep($step, $row['ID']);
154 continue;
155 }
156
157 $row['STEP'] = $step;
158 $row['ENTITY_ID'] = (int)$row['ENTITY_ID'];
159 $row['MULTIPLY_ID'] = (int)$row['MULTIPLY_ID'];
160
161 if ($row['MULTIPLY_ID'])
162 {
163 if ($multyId && $multyId !== $row['MULTIPLY_ID'])
164 {
165 $multyId = null;
166 }
167
168 if (!$multyId)
169 {
170 // first multiply step
171 $row['ACTION_PARAMS'] = [
172 [
173 'ACTION' => $row['ACTION'],
174 'ACTION_PARAMS' => $row['ACTION_PARAMS'],
175 ],
176 ];
178 $multyId = $row['MULTIPLY_ID'];
179 $row['MULTIPLY'] = [$row['MULTIPLY_ID']];
180 unset($row['MULTIPLY_ID']);
181 $this->stack[$step] = $row;
182 }
183 else
184 {
185 $this->stack[$step - 1]['ACTION_PARAMS'][] = [
186 'ACTION' => $row['ACTION'],
187 'ACTION_PARAMS' => $row['ACTION_PARAMS'],
188 ];
189 $this->stack[$step - 1]['MULTIPLY'][] = $row['ID'];
190 }
191 }
192 else
193 {
194 $multyId = null;
195 $this->stack[$step] = $row;
196 }
197
198 $step++;
199 }
200 }
201
209 protected function fixBrokenStep(int $step, int $id): bool
210 {
211 $resDelete = HistoryTable::delete($id);
212 if ($resDelete->isSuccess())
213 {
214 $currentStep = $this->loadStep();
215 if ($step > $currentStep)
216 {
217 return true;
218 }
219
220 return $this->saveStep(max(--$currentStep, 0));
221 }
222
223 return false;
224 }
225
231 protected function clearBefore(int $step): bool
232 {
233 if (!isset($this->stack[$step]))
234 {
235 return false;
236 }
237
238 // if first step - can't delete nothing
239 if ($this->step <= 1)
240 {
241 return true;
242 }
243
244 // delete only before current step
245 if ($step >= $this->step)
246 {
247 $step = $this->step - 1;
248 }
249
250 for ($i = 1; $i <= $step; $i++)
251 {
252 if (!$this->deleteStep(1))
253 {
254 return false;
255 }
256 }
257
258 return true;
259 }
260
266 protected function clearAfter(int $step): bool
267 {
268 if ($step >= $this->getStackCount())
269 {
270 return true;
271 }
272
273 // if last step - can't delete nothing
274 $stackCount = $this->getStackCount();
275 if ($this->step >= $stackCount)
276 {
277 return true;
278 }
279
280 // delete only after current step
281 if ($step <= $this->step)
282 {
283 $step = $this->step + 1;
284 }
285
286 for ($i = $step; $i <= $stackCount; $i++)
287 {
288 if (!$this->deleteStep($step))
289 {
290 return false;
291 }
292 }
293
294 return true;
295 }
296
301 public function clearFuture(): bool
302 {
303 return $this->clearAfter($this->step);
304 }
305
310 public function clear(): bool
311 {
312 $count = $this->getStackCount();
313 for ($i = 0; $i < $count; $i++)
314 {
315 if (!$this->deleteStep(1))
316 {
317 return false;
318 }
319 }
320
321 $this->stack = [];
322
323 return true;
324 }
325
331 protected function deleteStep(int $step): bool
332 {
333 if (!isset($this->stack[$step]))
334 {
335 return false;
336 }
337
338 $item = $this->stack[$step];
339 $action = $this->getActionForStep($item['STEP'], false);
340 if (!$action || !$action->delete())
341 {
342 return false;
343 }
344
345 if (isset($item['MULTIPLY']) && is_array($item['MULTIPLY']) && !empty($item['MULTIPLY']))
346 {
347 foreach ($item['MULTIPLY'] as $multyId)
348 {
349 $resDelete = HistoryTable::delete($multyId);
350 if (!$resDelete->isSuccess())
351 {
352 return false;
353 }
354 }
355 }
356 else
357 {
358 $resDelete = HistoryTable::delete($item['ID']);
359 if (!$resDelete->isSuccess())
360 {
361 return false;
362 }
363 }
364
365 // update stack and step
366 unset($this->stack[$step]);
367 $this->resetStackSteps();
368 if ($step <= $this->step)
369 {
370 return $this->saveStep($this->step - 1);
371 }
372
373 return true;
374 }
375
380 protected function resetStackSteps(): void
381 {
382 $newStack = [];
383 $step = 1;
384 foreach ($this->stack as $item)
385 {
386 $item['STEP'] = $step;
387 $newStack[$step] = $item;
388 $step++;
389 }
390
391 // todo: what about multiply step?
392
393 $this->stack = $newStack;
394 }
395
396
402 public function clearOld(int $days): bool
403 {
404 if ($days > 0)
405 {
406 $dateEnd = new DateTime();
407 $dateEnd->add('-' . $days . ' days');
408
409 $deleteBeforeStep = 0;
410 foreach ($this->stack as $stackItem)
411 {
412 $dateCurrent = DateTime::createFromUserTime($stackItem['DATE_CREATE']);
413 if ($dateEnd < $dateCurrent)
414 {
415 break;
416 }
417 $deleteBeforeStep = $stackItem['STEP'];
418 }
419
420 return $this->clearBefore($deleteBeforeStep);
421 }
422
423 return false;
424 }
425
426 public function getStackCount(): int
427 {
428 return count($this->stack);
429 }
430
435 protected function loadStep(): int
436 {
437 $this->step = 0;
438
439 $step = HistoryStepTable::query()
440 ->addSelect('ID')
441 ->addSelect('STEP')
442 ->where('ENTITY_ID', '=', $this->entityId)
443 ->where('ENTITY_TYPE', '=', $this->entityType)
444 ->exec()
445 ->fetch()
446 ;
447 // todo: del other entities row if exists
448 if ($step)
449 {
450 $this->stepRowId = $step['ID'];
451 $this->step = $step['STEP'];
452 }
453 else
454 {
455 $this->migrateStep();
456 }
457
458 return $this->step;
459 }
460
466 protected function saveStep(int $step): bool
467 {
468 $this->step = $step;
469
470 if ($this->stepRowId)
471 {
472 $res = HistoryStepTable::update($this->stepRowId, ['STEP' => $step]);
473 }
474 else
475 {
476 $res = HistoryStepTable::add([
477 'ENTITY_ID' => $this->entityId,
478 'ENTITY_TYPE' => $this->entityType,
479 'STEP' => $step,
480 ]);
481 }
482
483 if ($res->isSuccess())
484 {
485 $this->stepRowId = $res->getId();
486 $this->step = $step;
487
488 return true;
489 }
490
491 return false;
492 }
493
499 private function migrateStep(): void
500 {
501 $oldStep = null;
502
503 if ($this->entityType === self::ENTITY_TYPE_LANDING)
504 {
505 if (!array_key_exists('HISTORY_STEP', LandingTable::getMap()))
506 {
507 return;
508 }
509
510 $landing = LandingTable::query()
511 ->addSelect('HISTORY_STEP')
512 ->where('ID', '=', $this->entityId)
513 ->exec()
514 ->fetch()
515 ;
516 $oldStep = $landing ? $landing['HISTORY_STEP'] : null;
517 }
518
519 if ($this->entityType === self::ENTITY_TYPE_DESIGNER_BLOCK)
520 {
521 if (!array_key_exists('HISTORY_STEP_DESIGNER', BlockTable::getMap()))
522 {
523 return;
524 }
525
526 $block = BlockTable::query()
527 ->addSelect('HISTORY_STEP_DESIGNER')
528 ->where('ID', '=', $this->entityId)
529 ->exec()
530 ->fetch()
531 ;
532 $oldStep = $block ? $block['HISTORY_STEP_DESIGNER'] : null;
533 }
534
535 $isNewStepExists = HistoryStepTable::query()
536 ->addSelect('ID')
537 ->addSelect('STEP')
538 ->where('ENTITY_ID', '=', $this->entityId)
539 ->where('ENTITY_TYPE', '=', $this->entityType)
540 ->exec()
541 ->fetch()
542 ;
543
544 if ($oldStep && !$isNewStepExists)
545 {
546 $this->saveStep((int)$oldStep);
547 }
548 }
549
554 public function getJsStack(): array
555 {
556 $result = [];
557 foreach ($this->stack as $step => $stackItem)
558 {
559 $actionClass = ActionFactory::getActionClass($stackItem['ACTION']);
560 $result[] = [
561 'id' => $stackItem['ID'],
562 'current' => $step === $this->step,
563 'command' => (is_callable([$actionClass, 'getJsCommandName']))
564 ? call_user_func([$actionClass, 'getJsCommandName'])
565 : ''
566 ,
567 'entityId' => $this->entityId,
568 'entityType' => $this->entityType,
569 ];
570 }
571
572 return $result;
573 }
574
579 public function getStep(): int
580 {
581 return $this->step;
582 }
583
584 public function push(string $actionName, array $params): bool
585 {
586 $actionName = strtoupper($actionName);
587
588 $action = ActionFactory::getAction($actionName);
589 if (!$action)
590 {
591 return false;
592 // todo: or err
593 }
594 $action->setParams($params);
595
596 $fields = [
597 'ENTITY_TYPE' => $this->entityType,
598 'ENTITY_ID' => $this->entityId,
599 'ACTION' => $actionName,
600 'ACTION_PARAMS' => $action->getParams(),
601 'CREATED_BY_ID' => Manager::getUserId() ?: 1,
602 'DATE_CREATE' => new DateTime,
603 ];
604
605 // check duplicates
606 if (
607 !empty($this->stack[$this->step])
608 && ActionFactory::compareSteps($this->stack[$this->step], $fields)
609 )
610 {
611 return false;
612 }
613
614 if (!$action->isNeedPush())
615 {
616 return true;
617 }
618
619 $stackCount = $this->getStackCount();
620 if ($this->step < $stackCount)
621 {
622 if (!$this->clearFuture())
623 {
624 return false;
625 }
626 }
627
628 $nextStep =
629 (self::$multiplyMode && self::$multiplyStep !== null)
630 ? self::$multiplyStep
631 : $this->step + 1
632 ;
633
634 if (!$this->saveStep($nextStep))
635 {
636 return false;
637 }
638
639 self::$multiplyStep = $nextStep;
640 // todo: drop $multiplyStep after last element (when set multiply mode off)
641
642 if (self::$multiplyMode && self::$multiplyId !== null)
643 {
644 $fields['MULTIPLY_ID'] = self::$multiplyId;
645 }
646
647 $resAdd = HistoryTable::add($fields);
648
649 // save MULTIPLY_ID for first element in group
650 if (self::$multiplyMode && self::$multiplyId === null)
651 {
652 self::$multiplyId = $resAdd->getId();
653 HistoryTable::update(self::$multiplyId, [
654 'MULTIPLY_ID' => self::$multiplyId,
655 ]);
656 }
657
658 return $resAdd->isSuccess();
659 }
660
661 public function undo(): bool
662 {
663 if ($this->canUndo())
664 {
665 self::deactivate();
666 $action = $this->getActionForStep($this->step, true);
667 if ($action && $action->execute())
668 {
669 return $this->saveStep($this->step - 1);
670 }
671 }
672
673 return false;
674 }
675
676 protected function canUndo(): bool
677 {
678 return
679 $this->step > 0
680 && $this->getStackCount() > 0
681 && $this->step <= $this->getStackCount()
682 ;
683 }
684
685 public function redo(): bool
686 {
687 if ($this->canRedo())
688 {
689 self::deactivate();
690 $action = $this->getActionForStep($this->step + 1, false);
691 if ($action && $action->execute(false))
692 {
693 return $this->saveStep($this->step + 1);
694 }
695 }
696
697 return false;
698 }
699
700 protected function canRedo(): bool
701 {
702 return
703 $this->step >= 0
704 && $this->getStackCount() > 0
705 && $this->step < $this->getStackCount()
706 ;
707 }
708
714 public function getJsCommand(bool $undo = true): array
715 {
716 $action = $this->getActionForStep(
717 $undo ? $this->step : ($this->step + 1),
718 $undo
719 );
720
721 return $action ? $action->getJsCommand($undo) : [];
722 }
723
730 protected function getActionForStep(int $step, bool $undo): ?BaseAction
731 {
732 if (!isset($this->stack[$step]))
733 {
734 return null;
735 }
736
737 $stepItem = $this->stack[$step];
738 $stepId = $stepItem['ID'];
740 if (isset($this->actions[$stepId][$direction]))
741 {
742 return $this->actions[$stepId][$direction];
743 }
744
745 $params = $stepItem['ACTION_PARAMS'];
746 if ($this->entityType === self::ENTITY_TYPE_LANDING)
747 {
748 $params['lid'] = $this->entityId;
749 }
750 if ($this->entityType === self::ENTITY_TYPE_DESIGNER_BLOCK)
751 {
752 $params['blockId'] = $this->entityId;
753 }
754
755 $action = ActionFactory::getAction($stepItem['ACTION'], $undo);
756 if (!$action)
757 {
758 return null;
759 }
760
761 $action->setParams($params, true);
762 $this->actions[$stepId][$direction] = $action;
763
764 return $action;
765 }
766
767
768}
$count
Определения admin_tab.php:4
static getAction(string $actionName, ?bool $undo=false)
Определения ActionFactory.php:134
static getActionClass(string $actionName, ?bool $undo=false)
Определения ActionFactory.php:165
static compareSteps(array $prevStep, array $nextStep)
Определения ActionFactory.php:194
static getDirectionName(?bool $undo=false)
Определения ActionFactory.php:183
clearBefore(int $step)
Определения history.php:231
undo()
Определения history.php:661
array $actions
Определения history.php:71
static int $multiplyStep
Определения history.php:56
getStackCount()
Определения history.php:426
getActionForStep(int $step, bool $undo)
Определения history.php:730
static bool $isActive
Определения history.php:37
getJsCommand(bool $undo=true)
Определения history.php:714
static int $multiplyId
Определения history.php:49
canUndo()
Определения history.php:676
fixBrokenStep(int $step, int $id)
Определения history.php:209
static setMultiplyMode()
Определения history.php:91
getJsStack()
Определения history.php:554
int $step
Определения history.php:70
clearAfter(int $step)
Определения history.php:266
redo()
Определения history.php:685
static deactivate()
Определения history.php:86
canRedo()
Определения history.php:700
string $entityType
Определения history.php:59
const ENTITY_TYPE_DESIGNER_BLOCK
Определения history.php:26
const AVAILABLE_TYPES
Определения history.php:28
static bool $multiplyMode
Определения history.php:43
static activate()
Определения history.php:77
array $stack
Определения history.php:69
static unsetMultiplyMode()
Определения history.php:96
__construct(int $entityId, string $entityType)
Определения history.php:114
deleteStep(int $step)
Определения history.php:331
clear()
Определения history.php:310
getStep()
Определения history.php:579
int $stepRowId
Определения history.php:64
int $entityId
Определения history.php:58
loadStep()
Определения history.php:435
static isActive()
Определения history.php:105
saveStep(int $step)
Определения history.php:466
loadStack()
Определения history.php:134
resetStackSteps()
Определения history.php:380
clearFuture()
Определения history.php:301
const ENTITY_TYPE_LANDING
Определения history.php:21
push(string $actionName, array $params)
Определения history.php:584
clearOld(int $days)
Определения history.php:402
static getUserId()
Определения manager.php:107
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$res
Определения filter_act.php:7
$result
Определения get_property_values.php:14
$entityId
Определения payment.php:4
$direction
Определения prolog_auth_admin.php:25
$i
Определения factura.php:643
</p ></td >< td valign=top style='border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 2.0pt 0cm 2.0pt;height:9.0pt'>< p class=Normal align=center style='margin:0cm;margin-bottom:.0001pt;text-align:center;line-height:normal'>< a name=ТекстовоеПоле54 ></a ><?=($taxRate > count( $arTaxList) > 0) ? $taxRate."%"
Определения waybill.php:936
if($inWords) echo htmlspecialcharsbx(Number2Word_Rus(roundEx($totalVatSum $params['CURRENCY']
Определения template.php:799
$action
Определения file_dialog.php:21
$fields
Определения yandex_run.php:501