diff --git a/composer.json b/composer.json index 3dfd363..681a500 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,8 @@ "psr/container": "^1.0 || ^2.0", "psr/log": "^1.0 || ^2.0 || ^3.0", "react/event-loop": "^1.5", - "symfony/process": "^6.0 || ^7.0" + "symfony/process": "^6.0 || ^7.0", + "alexanderpas/http-enum": "^1.0" }, "replace": { "panlatent/craft-action-abstract": "self.version" diff --git a/src/actions/HttpRequest.php b/src/actions/HttpRequest.php index f08b9ec..65d3f2c 100644 --- a/src/actions/HttpRequest.php +++ b/src/actions/HttpRequest.php @@ -2,6 +2,8 @@ namespace panlatent\schedule\actions; +use Alexanderpas\Common\HTTP\Method; +use Craft; use craft\helpers\Json; use GuzzleHttp\Client; use GuzzleHttp\ClientInterface; @@ -14,10 +16,7 @@ class HttpRequest extends Action { - /** - * @var string - */ - public string $method = 'get'; + public string $method = Method::GET->value; /** * @var string Request URL default is echo api. @@ -44,11 +43,6 @@ class HttpRequest extends Action */ public string $body = ''; - public static function run() - { - - } - public function execute(ContextInterface $context): bool { try { @@ -71,6 +65,44 @@ public function executeWithClient(ContextInterface $context, ClientInterface $cl return $statusCode >= 200 && $statusCode < 400; } + public function getMethods(): array + { + return [ + Method::GET->value, + Method::HEAD->value, + Method::POST->value, + Method::PUT->value, + Method::PATCH->value, + Method::OPTIONS->value, + ]; + } + + public function getContentTypes(): array + { + return [ + 'multipart/form-data', + 'application/x-www-form-urlencoded', + 'application/json', + 'application/xml', + 'text/html', + 'text/xml', + ]; + } + + public function getSettingsHtml(): ?string + { + return Craft::$app->getView()->renderTemplate('schedule/_components/actions/HttpRequest/settings', [ + 'action' => $this, + // Craft editableTable not support custom suggestions + // 'httpHeaderSuggestions' => [ + // [ + // 'label' => '', + // 'data' => $this->_getHttpHeaderSuggestions(), + // ] + // ], + ]); + } + protected function getOptions(): array { diff --git a/src/actions/SendEmail.php b/src/actions/SendEmail.php new file mode 100644 index 0000000..67f0b80 --- /dev/null +++ b/src/actions/SendEmail.php @@ -0,0 +1,60 @@ +emails as $email) { + if ($email['name']) { + $emails[App::parseEnv($email['email'])] = $email['name']; + } else { + $emails[] = App::parseEnv($email['email']); + } + } + + $subject = Craft::$app->view->renderString($this->subject, [ + 'action' => $this, + ]); + + $message = Craft::$app->view->renderString($this->message, [ + 'action' => $this, + ]); + + return Craft::$app->mailer + ->compose() + ->setTo($emails) + ->setHtmlBody($message) + ->setSubject($subject) + ->send(); + } + + public function getSettingsHtml(): ?string + { + return Craft::$app->getView()->renderTemplate('schedule/_components/actions/SendEmail/settings', [ + 'action' => $this, + ]); + } +} \ No newline at end of file diff --git a/src/controllers/ActionsController.php b/src/controllers/ActionsController.php index 1b80067..a5cb6af 100644 --- a/src/controllers/ActionsController.php +++ b/src/controllers/ActionsController.php @@ -3,12 +3,66 @@ namespace panlatent\schedule\controllers; use Craft; +use craft\elements\Entry; use craft\web\Controller; +use panlatent\craft\actions\abstract\ActionInterface; +use panlatent\schedule\actions\HttpRequest; use panlatent\schedule\Plugin; +use yii\web\NotFoundHttpException; use yii\web\Response; class ActionsController extends Controller { + public function actionEdit(?int $actionId = null, ?ActionInterface $action = null): Response + { + if ($actionId !== null) { + if ($action === null) { + $action = Plugin::getInstance()->actions->getActionById($actionId); + + if (!$action) { + throw new NotFoundHttpException('action not found'); + } + } + + $title = trim($action->name) ?: Craft::t('schedule', 'Edit Action'); + } else { + if ($action === null) { + $action = new HttpRequest(); + } + + $title = Craft::t('schedule', 'Create a new action'); + } + + $allActionTypes = Plugin::getInstance()->actions->getAllActionTypes(); + $actionTypeOptions = []; + foreach ($allActionTypes as $class) { + $actionTypeOptions[] = [ + 'label' => $class::displayName(), + 'value' => $class, + ]; + } + + return $this->asCpScreen() + ->title($title) + ->addCrumb(Craft::t('app', 'Settings'), 'settings') + ->addCrumb(Craft::t('app', 'Entry Types'), 'settings/entry-types') + ->action('schedule/actions/save') + ->redirectUrl('schedule/actions') + ->addAltAction(Craft::t('app', 'Save and continue editing'), [ + 'redirect' => 'settings/entry-types/{id}', + 'shortcut' => true, + 'retainScroll' => true, + ]) + ->contentTemplate('schedule/actions/_edit.twig', [ + 'actionId' => $actionId, + 'action' => $action, + 'typeName' => $action::displayName(), + 'actionTypes' => $allActionTypes, + 'actionTypeOptions' => $actionTypeOptions, + ]); + } + + public function actionRenderSettings(): Response { $this->requirePostRequest(); diff --git a/src/controllers/SchedulesController.php b/src/controllers/SchedulesController.php index c262c05..41341e7 100644 --- a/src/controllers/SchedulesController.php +++ b/src/controllers/SchedulesController.php @@ -135,36 +135,14 @@ public function actionEditSchedule(int $scheduleId = null, Schedule $schedule = ]; } - $actionInstances = []; - $actionTypeOptions = []; - foreach ($allActionTypes as $class) { - $actionInstances[$class] = new $class(); - $actionTypeOptions[] = [ - 'label' => $class::displayName(), - 'value' => $class, - ]; - } - - $timerInstances = []; - $timerTypeOptions = []; - foreach ($allTimerTypes as $class) { - $timerInstances[$class] = new $class(); - $timerTypeOptions[] = [ - 'label' => $class::displayName(), - 'value' => $class, - ]; - } - return $this->renderTemplate('schedule/_edit', [ 'isNewSchedule' => $isNewSchedule, 'groupOptions' => $groupOptions, 'schedule' => $schedule, - 'actionInstances' => $actionInstances, 'actionTypes' => $allActionTypes, - 'actionTypeOptions' => $actionTypeOptions, - 'timerInstances' => $timerInstances, + 'actionTypeOptions' => array_map(static fn($class) => ['label' => $class::displayName(), 'value' => $class], $allActionTypes), 'timerTypes' => $allTimerTypes, - 'timerTypeOptions' => $timerTypeOptions, + 'timerTypeOptions' => array_map(static fn($class) => ['label' => $class::displayName(), 'value' => $class], $allTimerTypes), ]); } diff --git a/src/controllers/TimersController.php b/src/controllers/TimersController.php index cae9b00..0797210 100644 --- a/src/controllers/TimersController.php +++ b/src/controllers/TimersController.php @@ -24,6 +24,7 @@ * * @package panlatent\schedule\controllers * @author Panlatent + * @deprecated since 1.0 */ class TimersController extends Controller { diff --git a/src/db/Table.php b/src/db/Table.php index 779b9f5..aff8e00 100644 --- a/src/db/Table.php +++ b/src/db/Table.php @@ -15,7 +15,7 @@ */ abstract class Table { -// public const ACTIONS = '{{%schedule_actions}}'; + public const ACTIONS = '{{%schedule_actions}}'; public const SCHEDULES = '{{%schedule_schedules}}'; public const SCHEDULE_GROUPS = '{{%schedule_schedulegroups}}'; public const TIMERS = '{{%schedule_timers}}'; diff --git a/src/events/ActionEvent.php b/src/events/ActionEvent.php new file mode 100644 index 0000000..045a28e --- /dev/null +++ b/src/events/ActionEvent.php @@ -0,0 +1,11 @@ + + * @deprecated since 1.0 */ class ScheduleBuildEvent extends Event { diff --git a/src/events/ScheduleEvent.php b/src/events/ScheduleEvent.php index bb1c5f5..9775e2c 100644 --- a/src/events/ScheduleEvent.php +++ b/src/events/ScheduleEvent.php @@ -7,8 +7,8 @@ namespace panlatent\schedule\events; -use panlatent\schedule\base\ScheduleInterface; -use yii\base\Event; +use craft\events\ModelEvent; +use panlatent\schedule\models\Schedule; /** * Class ScheduleEvent @@ -16,15 +16,7 @@ * @package panlatent\schedule\events * @author Panlatent */ -class ScheduleEvent extends Event +class ScheduleEvent extends ModelEvent { - /** - * @var ScheduleInterface|null - */ - public ?ScheduleInterface $schedule = null; - - /** - * @var bool - */ - public bool $isNew = false; + public ?Schedule $schedule = null; } \ No newline at end of file diff --git a/src/events/ScheduleGroupEvent.php b/src/events/ScheduleGroupEvent.php index a66feed..fabe052 100644 --- a/src/events/ScheduleGroupEvent.php +++ b/src/events/ScheduleGroupEvent.php @@ -7,8 +7,8 @@ namespace panlatent\schedule\events; +use craft\events\ModelEvent; use panlatent\schedule\models\ScheduleGroup; -use yii\base\Event; /** * Class ScheduleGroupEvent @@ -16,15 +16,10 @@ * @package panlatent\schedule\events * @author Panlatent */ -class ScheduleGroupEvent extends Event +class ScheduleGroupEvent extends ModelEvent { /** * @var ScheduleGroup|null */ public ?ScheduleGroup $group = null; - - /** - * @var bool - */ - public bool $isNew = false; } \ No newline at end of file diff --git a/src/events/TimerEvent.php b/src/events/TimerEvent.php index 0e64248..c71dbc6 100644 --- a/src/events/TimerEvent.php +++ b/src/events/TimerEvent.php @@ -9,8 +9,8 @@ namespace panlatent\schedule\events; +use craft\events\ModelEvent; use panlatent\schedule\base\TimerInterface; -use yii\base\Event; /** * Class TimerEvent @@ -18,15 +18,10 @@ * @package panlatent\schedule\events * @author Panlatent */ -class TimerEvent extends Event +class TimerEvent extends ModelEvent { /** * @var TimerInterface|null */ public ?TimerInterface $timer = null; - - /** - * @var bool - */ - public bool $isNew = false; } \ No newline at end of file diff --git a/src/fields/Action.php b/src/fields/Action.php new file mode 100644 index 0000000..1354079 --- /dev/null +++ b/src/fields/Action.php @@ -0,0 +1,10 @@ +createTable('{{%schedule_actions}}', [ -// 'id' => $this->primaryKey(), -// 'type' => $this->string()->notNull(), -// 'settings' => $this->text(), -// 'dateCreated' => $this->dateTime()->notNull(), -// 'dateUpdated' => $this->dateTime()->notNull(), -// 'uid' => $this->uid(), -// ]); + $this->createTable('{{%schedule_actions}}', [ + 'id' => $this->primaryKey(), + 'type' => $this->string()->notNull(), + 'settings' => $this->text(), + 'dateCreated' => $this->dateTime()->notNull(), + 'dateUpdated' => $this->dateTime()->notNull(), + 'uid' => $this->uid(), + ]); $this->createTable('{{%schedule_schedulegroups}}', [ 'id' => $this->primaryKey(), @@ -48,9 +48,9 @@ public function safeUp(): bool 'handle' => $this->string()->notNull(), 'description' => $this->string(), // 'static' => $this->boolean()->defaultValue(false), - 'action' => $this->text()->notNull(), - 'onSuccess' => $this->text()->notNull(), - 'onFailed' => $this->text()->notNull(), + 'actionId' => $this->integer()->notNull(), + 'onSuccess' => $this->integer(), + 'onFailed' => $this->integer(), //'enabledLog' => $this->boolean()->defaultValue(false), 'enabled' => $this->boolean()->notNull()->defaultValue(true), 'sortOrder' => $this->smallInteger()->unsigned(), @@ -62,9 +62,9 @@ public function safeUp(): bool $this->createIndex(null, '{{%schedule_schedules}}', 'handle', true); $this->createIndex(null, '{{%schedule_schedules}}', 'dateCreated'); $this->createIndex(null, '{{%schedule_schedules}}', ['sortOrder', 'dateCreated']); -// $this->addForeignKey(null, '{{%schedule_schedules}}', 'actionId', '{{%schedule_actions}}', 'id'); -// $this->addForeignKey(null, '{{%schedule_schedules}}', 'onSuccess', '{{%schedule_actions}}', 'id', 'SET NULL'); -// $this->addForeignKey(null, '{{%schedule_schedules}}', 'onFailed', '{{%schedule_actions}}', 'id', 'SET NULL'); + $this->addForeignKey(null, '{{%schedule_schedules}}', 'actionId', '{{%schedule_actions}}', 'id'); + $this->addForeignKey(null, '{{%schedule_schedules}}', 'onSuccess', '{{%schedule_actions}}', 'id', 'SET NULL'); + $this->addForeignKey(null, '{{%schedule_schedules}}', 'onFailed', '{{%schedule_actions}}', 'id', 'SET NULL'); $this->createTable('{{%schedule_scheduletraces}}', [ 'id' => $this->primaryKey(), @@ -135,5 +135,6 @@ public function safeDown(): void $this->dropTableIfExists('{{%schedule_scheduletraces}}'); $this->dropTableIfExists('{{%schedule_schedules}}'); $this->dropTableIfExists('{{%schedule_schedulegroups}}'); + $this->dropTableIfExists('{{%schedule_actions}}'); } } \ No newline at end of file diff --git a/src/models/Schedule.php b/src/models/Schedule.php index dc860a7..212dccf 100644 --- a/src/models/Schedule.php +++ b/src/models/Schedule.php @@ -12,7 +12,6 @@ /** * @property-read ScheduleGroup $group - * @property-read ActionInterface[] $actions * @since 1.0.0 */ class Schedule extends Model diff --git a/src/records/Schedule.php b/src/records/Schedule.php index 3f64d32..3d03156 100644 --- a/src/records/Schedule.php +++ b/src/records/Schedule.php @@ -19,15 +19,11 @@ * @property string $name * @property string $handle * @property string $description - * @property string $type - * @property string $user - * @property string $settings - * @property bool $static + * @property int $actionId + * @property int $onSuccess + * @property int $onFailed * @property bool $enabled * @property bool $enabledLog - * @property int $lastStartedTime - * @property int $lastFinishedTime - * @property bool $lastStatus * @property int $sortOrder * @author Panlatent */ diff --git a/src/services/Actions.php b/src/services/Actions.php index 0e33d29..0b2357e 100644 --- a/src/services/Actions.php +++ b/src/services/Actions.php @@ -2,21 +2,53 @@ namespace panlatent\schedule\services; +use Craft; +use craft\db\Query; use craft\errors\MissingComponentException; use craft\events\RegisterComponentTypesEvent; +use craft\helpers\ArrayHelper; use craft\helpers\Component as ComponentHelper; use panlatent\craft\actions\abstract\ActionInterface; use panlatent\schedule\actions\Command; use panlatent\schedule\actions\Console; use panlatent\schedule\actions\HttpRequest; +use panlatent\schedule\actions\SendEmail; +use panlatent\schedule\db\Table; +use panlatent\schedule\events\ActionEvent; use panlatent\schedule\log\LogAdapter; use panlatent\schedule\models\Context; use yii\base\Component; +use yii\web\Request; class Actions extends Component { public const EVENT_REGISTER_ACTION_TYPES = 'registerActionTypes'; + /** + * @event ActionEvent + */ + public const EVENT_BEFORE_SAVE_ACTION = 'beforeSaveAction'; + + /** + * @event ActionEvent + */ + public const EVENT_AFTER_SAVE_ACTION = 'afterSaveAction'; + + /** + * @event ActionEvent + */ + public const EVENT_BEFORE_DELETE_ACTION = 'beforeDeleteAction'; + + /** + * @event ActionEvent + */ + public const EVENT_AFTER_DELETE_ACTION = 'afterDeleteAction'; + + /** + * @var ActionInterface[] + */ + private ?array $actions = null; + public function getAllActionTypes(): array { $event = new RegisterComponentTypesEvent([ @@ -24,6 +56,7 @@ public function getAllActionTypes(): array Command::class, Console::class, HttpRequest::class, + SendEmail::class, ] ]); @@ -32,9 +65,31 @@ public function getAllActionTypes(): array return $event->types; } + /** + * @return ActionInterface[] + */ + public function getAllActions(): array + { + if ($this->actions === null) { + $this->actions = []; + $rows = $this->createQuery()->all(); + foreach ($rows as $row) { + $action = $this->createAction($row); + $this->actions[] = $action; + } + } + + return $this->actions; + } + + public function getActionById(int $actionId) + { + return ArrayHelper::firstWhere($this->getAllActions(), 'id', $actionId); + } + public function runAction(ActionInterface $action): bool { - $context = new Context(new LogAdapter(\Craft::$app->getLog()->getLogger(), 'action')); + $context = new Context(new LogAdapter(Craft::$app->getLog()->getLogger(), 'action')); return $action->execute($context); @@ -48,4 +103,82 @@ public function createAction(mixed $config): ActionInterface } } + + public function saveAction(ActionInterface $action, bool $runValidation = true): bool + { + $isNew = $action->getIsNew(); + + if ($this->hasEventHandlers(self::EVENT_BEFORE_SAVE_ACTION)) { + $this->trigger(self::EVENT_BEFORE_SAVE_ACTION, new ActionEvent([ + 'action' => $action, + 'isNew' => $isNew, + ])); + } + + if (!$action->beforeSave($isNew)) { + return false; + } + + if ($runValidation && !$action->validate()) { + Craft::info('Action not saved due to validation error.', __METHOD__); + return false; + } + + $transaction = Craft::$app->getDb()->beginTransaction(); + try { + Craft::$app->getDb() + ->createCommand() + ->upsert(Table::ACTIONS, [ + 'type' => get_class($action), + 'settings' => $action->getSettings(), + 'dateUpdated' => $action->dateUpdated, + 'dateCreated' => $action->dateCreated, + 'uid' => $action->uid, + ], [ + 'type' => get_class($action), + 'settings' => $action->getSettings(), + 'dateUpdated' => $action->dateUpdated, + ]) + ->execute(); + + if ($isNew) { + $action->id = $record->id; + } + + $transaction->commit(); + } catch (\Throwable $exception) { + $transaction->rollBack(); + + throw $exception; + } + + // If has some respositories ... + // $this->__METHOD__[$action->id] = $action; + // $this->__METHOD__[$action->handle] = $action; + + if ($this->hasEventHandlers(self::EVENT_AFTER_SAVE_ACTION)) { + $this->trigger(self::EVENT_AFTER_SAVE_ACTION, new ActionEvent([ + 'action' => $action, + 'isNew' => $isNew, + ])); + } + + $action->afterSave($isNew); + + return true; + } + + private function createQuery(): Query + { + return (new Query()) + ->select([ + 'actions.id', + 'actions.type', + 'actions.settings', + 'actions.dateCreated', + 'actions.dateUpdated', + 'actions.uid', + ]) + ->from(['actions' => Table::ACTIONS]); + } } \ No newline at end of file diff --git a/src/services/Schedules.php b/src/services/Schedules.php index 9d551e0..66fc510 100644 --- a/src/services/Schedules.php +++ b/src/services/Schedules.php @@ -276,11 +276,6 @@ public function getAllSchedules(): array { if ($this->_schedules === null) { $this->_schedules = []; - - foreach (Plugin::getInstance()->getSettings()->schedules as $item) { - - } - $results = $this->_createScheduleQuery()->all(); foreach ($results as $result) { $schedule = $this->createSchedule($result); @@ -424,7 +419,13 @@ public function createScheduleFromRequest(Request $request = null): Schedule $request = Craft::$app->getRequest(); } - $type = $request->getBodyParam('type'); + $actionType = $request->getRequiredBodyParam('actionType'); + $actionConfig = $request->getBodyParam('actionTypes.' . $actionType) ?? []; + $action = Plugin::getInstance()->actions->createAction(['type' => $actionType] + $actionConfig); + + $timerType = $request->getBodyParam('timerType'); + $timerConfig = $request->getBodyParam('timeTypes.' . $timerType) ?? []; + $timer = Plugin::getInstance()->timers->createTimer(['type' => $timerType] + $timerConfig); return $this->createSchedule([ 'id' => $request->getBodyParam('scheduleId'), @@ -432,9 +433,9 @@ public function createScheduleFromRequest(Request $request = null): Schedule 'name' => $request->getBodyParam('name'), 'handle' => $request->getBodyParam('handle'), 'description' => $request->getBodyParam('description'), - 'type' => $type, - 'settings' => $request->getBodyParam('types.' . $type, []), - 'static' => (bool)$request->getBodyParam('static'), + //'static' => (bool)$request->getBodyParam('static'), + 'timer' => $timer, + 'action' => $action, 'enabled' => (bool)$request->getBodyParam('enabled'), 'enabledLog' => $request->getBodyParam('enabledLog'), ]); @@ -457,8 +458,7 @@ public function createSchedule(mixed $config): Schedule */ public function saveSchedule(Schedule $schedule, bool $runValidation = true): bool { - /** @var Schedule $schedule */ - $isNewSchedule = $schedule->getIsNew(); + $isNewSchedule = !$schedule->id; if ($this->hasEventHandlers(self::EVENT_BEFORE_SAVE_SCHEDULE)) { $this->trigger(self::EVENT_BEFORE_SAVE_SCHEDULE, new ScheduleEvent([ @@ -467,10 +467,6 @@ public function saveSchedule(Schedule $schedule, bool $runValidation = true): bo ])); } - if (!$schedule->beforeSave($isNewSchedule)) { - return false; - } - if ($isNewSchedule) { $schedule->uid = StringHelper::UUID(); } elseif ($schedule->uid === null) { @@ -512,14 +508,28 @@ public function saveSchedule(Schedule $schedule, bool $runValidation = true): bo $record = new ScheduleRecord(); } + if (!$schedule->action->id) { + Craft::$app->getDb() + ->createCommand() + ->upsert(Table::ACTIONS, [ + + ], [ + 'type' => get_class($schedule->action), + 'settings' => $action->getSettings(), + 'dateUpdated' => $action->dateUpdated, + 'dateCreated' => $action->dateCreated, + 'uid' => $action->uid, + ]) + ->execute(); + } + $record->groupId = $schedule->groupId; $record->name = $schedule->name; $record->handle = $schedule->handle; $record->description = $schedule->description; - $record->type = get_class($schedule); - $record->user = $schedule->user; - $record->settings = Json::encode($schedule->getSettings()); - $record->static = false; + $record->actionId = $schedule->action->id; + $record->onSuccess = null; + $record->onFailed = null; $record->enabled = (bool)$schedule->enabled; $record->enabledLog = (bool)$schedule->enabledLog; $record->save(false); @@ -546,8 +556,6 @@ public function saveSchedule(Schedule $schedule, bool $runValidation = true): bo $this->_schedules[] = $schedule; } - $schedule->afterSave($isNewSchedule); - if ($this->hasEventHandlers(self::EVENT_AFTER_SAVE_SCHEDULE)) { $this->trigger(self::EVENT_AFTER_SAVE_SCHEDULE, new ScheduleEvent([ 'schedule' => $schedule, @@ -735,9 +743,8 @@ private function _createScheduleQuery(): Query 'schedules.name', 'schedules.handle', 'schedules.description', - //'schedules.type', //'schedules.user', - 'schedules.action', + 'schedules.actionId', 'schedules.onSuccess', 'schedules.onFailed', //'schedules.static', diff --git a/src/templates/_components/actions/Console/settings.twig b/src/templates/_components/actions/Console/settings.twig index 99da98d..8f06f2d 100644 --- a/src/templates/_components/actions/Console/settings.twig +++ b/src/templates/_components/actions/Console/settings.twig @@ -1,7 +1,6 @@ {% import "_includes/forms" as forms %} {% set scheduleType = className(schedule) %} -{% namespace 'types[' ~ scheduleType ~']' %} {{ forms.autosuggestField({ label: 'Command'|t('schedule'), @@ -29,5 +28,3 @@ size: 5, errors: schedule.getErrors('timeout'), }) }} - -{% endnamespace %} \ No newline at end of file diff --git a/src/templates/_components/actions/HttpRequest/settings.twig b/src/templates/_components/actions/HttpRequest/settings.twig new file mode 100644 index 0000000..c5286b9 --- /dev/null +++ b/src/templates/_components/actions/HttpRequest/settings.twig @@ -0,0 +1,146 @@ +{% import "_includes/forms" as forms %} + +{% set actionType = className(action) %} + +{% set methodOptions = [] %} +{% for method in action.methods %} + {% set methodOptions = methodOptions|merge([{ + label: method, + value: method, + }]) %} +{% endfor %} + +{{ forms.selectField({ + label: "HTTP Method"|t('action'), + required: true, + id: 'method', + name: 'method', + value: action.method, + options: methodOptions, + errors: action.getErrors('method'), +}) }} + +{{ forms.autosuggestField({ + label: 'Request URL'|t('schedule'), + requied: true, + id: 'url', + name: 'url', + value: action.url, + errors: action.getErrors('url'), + suggestEnvVars: true, +}) }} + +{{ forms.editableTableField({ + label: "Headers"|t("schedule"), + id: "headers", + name: "headers", + addRowLabel: "Add a header"|t("schedule"), + cols: { + enabled: { + type: 'checkbox', + thin: true, + checked: true, + }, + name: { + type: 'singleline', + heading: "Header Name"|t("schedule"), + placeholder: "Add Header Name"|t("schedule"), + code: true, + }, + value: { + type: 'singleline', + heading: "Header Value"|t("schedule"), + placeholder: "Add Header Value"|t("schedule"), + code: true, + } + }, + rows: action.headers, + defaultValues: { + enabled: true, + }, + allowAdd: true, + allowReorder: true, + allowDelete: true, + errors: action.getErrors("headers"), +}) }} + +{{ forms.editableTableField({ + label: "Query Params"|t("schedule"), + id: "queryParams", + name: "queryParams", + addRowLabel: "Add a parameter"|t("schedule"), + cols: { + enabled: { + type: 'checkbox', + thin: true, + }, + name: { + type: 'singleline', + heading: "Parameter"|t("schedule"), + placeholder: "Add URL Parameter"|t("schedule"), + code: true, + }, + value: { + type: 'singleline', + heading: "Value"|t("schedule"), + placeholder: "Add Value"|t("schedule"), + code: true, + }, + }, + rows: action.queryParams, + defaultValues: { + enabled: true, + }, + allowAdd: true, + allowReorder: true, + allowDelete: true, + errors: action.getErrors("queryParams"), +}) }} + +{% set contentTypeSuggestions = [] %} +{% for contentType in action.contentTypes %} + {% set contentTypeSuggestions = contentTypeSuggestions|merge([ + { + name: contentType, + hint: contentType, + } + ]) %} +{% endfor %} + + + +{% js %} + $('#{{ 'method'|namespaceInputId }}').change(function() { + var method = $(this).val(); + if (method === 'GET' || method === 'HEAD') { + $('#{{ 'requestContent'|namespaceInputId }}').addClass('hidden'); + } else { + $('#{{ 'requestContent'|namespaceInputId }}').removeClass('hidden'); + } + }); +{% endjs %} \ No newline at end of file diff --git a/src/templates/_components/actions/SendEmail/settings.twig b/src/templates/_components/actions/SendEmail/settings.twig new file mode 100644 index 0000000..c93c279 --- /dev/null +++ b/src/templates/_components/actions/SendEmail/settings.twig @@ -0,0 +1,48 @@ +{% import "_includes/forms" as forms %} + +{{ forms.editableTableField({ + allowAdd: true, + allowReorder: true, + allowDelete: true, + rows: settings.emails ?? [], + label: 'Addresses'|t('schedule'), + instructions: 'The email column supports environment variables'|t('schedule'), + cols: { + email: { + type: 'autosuggest', + suggestEnvVars: true, + heading: 'Email Address'|t('schedule') + }, + name: { + type: 'singleline', + heading: 'Name'|t('schedule') + } + }, + id: 'adresses', + name: 'adresses', + addRowLabel: "Add a address"|t("schedule"), + errors: action.getErrors('adresses') ?? [] +}) }} + +{{ forms.autoSuggestField({ + first: true, + label: "Subject"|t, + instructions: "", + id: "subject", + name: "subject", + value: action.subject, + required: true, + errors: action.getErrors("subject"), +}) }} + +{{ forms.textareaField({ + first: true, + label: "Message"|t('schedule'), + instructions: "", + id: "message", + name: "message", + value: action.message|nl2br, + rows: 10, + required: true, + errors: action.getErrors("message"), +}) }} diff --git a/src/templates/_components/timers/Cron.twig b/src/templates/_components/timers/Cron.twig index 385bece..780c9ae 100644 --- a/src/templates/_components/timers/Cron.twig +++ b/src/templates/_components/timers/Cron.twig @@ -1,15 +1,5 @@ {% import "_includes/forms.twig" as forms %} -{#{{ forms.selectField({#} -{# label: 'Mode'|t('schedule'),#} -{# name: 'timerType',#} -{# value: timer.mode,#} -{# options: modeOptions,#} -{# toggle: true,#} -{#}) }}#} - - -
{{ forms.radio({ label: 'Every'|t('schedule'), diff --git a/src/templates/_edit.twig b/src/templates/_edit.twig index d221461..593e854 100644 --- a/src/templates/_edit.twig +++ b/src/templates/_edit.twig @@ -15,13 +15,10 @@ }] %} {% set tabs = { - settings: { label: "Basic Settings"|t('schedule'), url: '#settings' }, - timers: { label: "When"|t("schedule"), url: '#timers' }, - action: { label: "Do"|t("schedule"), url: '#action' }, - then: { label: "Then"|t("schedule"), url: '#then' }, + settings: { label: "Settings"|t('schedule'), url: '#settings' }, } %} -{% import "_includes/forms.twig" as forms %} +{% import "schedule/_includes/forms.twig" as forms %} {% block content %}
@@ -58,11 +55,8 @@ errors: schedule.getErrors('handle'), }) }} -
- - - {% endif %} + +
+ {{ "Then"|t('app') }} +
+ {{ forms.actionTypeField({ + label: 'On Success'|t('schedule'), + id: 'successActionType', + name: 'successActionType', + errors: schedule.getErrors('successActionType'), + }) }} + + {{ forms.actionTypeField({ + label: 'On Failed'|t('schedule'), + id: 'failedActionType', + name: 'failedActionType', + errors: schedule.getErrors('failedActionType'), + }) }} +
+
+
{{ "Status"|t('app') }}
diff --git a/src/templates/_includes/forms/actionType.twig b/src/templates/_includes/forms/actionType.twig index 4451716..5722cfb 100644 --- a/src/templates/_includes/forms/actionType.twig +++ b/src/templates/_includes/forms/actionType.twig @@ -1,6 +1,6 @@ {% set addOptionFn %} (createOption, selectize) => { - const slideout = new Craft.CpScreenSlideout('entry-types/edit'); + const slideout = new Craft.CpScreenSlideout('schedule/actions/edit'); slideout.on('submit', ev => { createOption({ text: ev.data.name, @@ -15,5 +15,5 @@ {% include '_includes/forms/selectize' with { options: options ?? [] ?? craft.cp.getEntryTypeOptions(), - addOptionLabel: 'Create a new entry type…'|t('app'), + addOptionLabel: 'Create a new action…'|t('schedule'), } %} diff --git a/src/templates/actions/_edit.twig b/src/templates/actions/_edit.twig new file mode 100644 index 0000000..eeee129 --- /dev/null +++ b/src/templates/actions/_edit.twig @@ -0,0 +1,53 @@ +{% import "_includes/forms.twig" as forms %} + +{{ forms.selectField({ + label: 'Type'|t('schedule'), + id: 'actionType', + name: 'actionType', + value: className(action), + options: actionTypeOptions, + toggle: true, +}) }} + +{% for actionType in actionTypes %} + {% set isCurrent = (actionType == className(action)) %} + +{% endfor %} + +{% set actionTypeIds = [] %} +{% for actionType in actionTypes %} + {% if actionType != className(action) %} + {% set actionTypeIds = actionTypeIds|merge({(actionType): actionType|id|namespaceInputId}) %} + {% endif %} +{% endfor %} + +{% js %} +var actionTypes = {{ actionTypeIds|json_encode|raw }}; +$('#{{ 'actionType'|namespaceInputId }}').on('change', function(value) { + var actionType = $(this).val(); + if (actionType in actionTypes) { + var id = '#' + actionTypes[actionType]; + let _cancelToken = axios.CancelToken.source(); + Craft.sendActionRequest("POST", "schedule/actions/render-settings", { + cancelToken: _cancelToken.token, + data: { + type: actionType, + }, + }).then(function(response) { + let $settings = $(response.data.settingsHtml || ''); + $(id).html(response.data.settingsHtml); + Craft.appendHeadHtml(response.data.headHtml); + Craft.appendBodyHtml(response.data.bodyHtml); + }) + delete actionTypes[actionType] + } +}); +{% endjs %}