diff --git a/app/Alert.php b/app/Alert.php new file mode 100644 index 0000000000..8cabda9fcb --- /dev/null +++ b/app/Alert.php @@ -0,0 +1,35 @@ +Testing

' 'today' 'next year' 'Click here' 'https://therestartproject.org' + * + * @var string + */ + protected $signature = 'alert:create {title} {html} {start} {end} {variant?} {ctatitle?} {ctalink?}'; + + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Create a system-wide alert'; + + /** + * Create a new command instance. + * + * @return void + */ + public function __construct() + { + parent::__construct(); + } + + /** + * Execute the console command. + * + * @return mixed + */ + public function handle() + { + $title = trim($this->argument('title')); + $html = trim($this->argument('html')); + $ctatitle = trim($this->argument('ctatitle')); + $ctalink = trim($this->argument('ctalink')); + $start = trim($this->argument('start')); + $end = trim($this->argument('end')); + $variant = trim($this->argument('variant')); + $variant = $variant ? $variant : 'secondary'; + + $alert = new \App\Alert(); + $alert->title = $title; + $alert->html = $html; + $alert->variant = $variant; + + if ($ctatitle && $ctalink) { + $alert->ctatitle = $ctatitle; + $alert->ctalink = $ctalink; + } + + // Parse $start as a date and set it in the alert. + $start = \Carbon\Carbon::parse($start); + $start->setTimezone('UTC'); + $end = \Carbon\Carbon::parse($end); + $end->setTimezone('UTC'); + + $alert->start = $start; + $alert->end = $end; + + $alert->save(); + + $this->info("Created alert " . $alert->id); + } +} diff --git a/app/Http/Controllers/API/AlertController.php b/app/Http/Controllers/API/AlertController.php new file mode 100644 index 0000000000..f4db42f804 --- /dev/null +++ b/app/Http/Controllers/API/AlertController.php @@ -0,0 +1,290 @@ +whereDate('end', '>=', date('Y-m-d H:i', strtotime(Carbon::now())))->get(); + \Cache::put('alerts', $alerts, 7200); + } + + return [ + 'data' => AlertCollection::make($alerts) + ]; + } + + /** + * @OA\Put( + * path="/api/v2/alerts", + * operationId="createAlert", + * tags={"Alerts"}, + * summary="Create Alert", + * description="Creates an alert.", + * @OA\Parameter( + * name="api_token", + * description="A valid user API token", + * required=true, + * in="query", + * @OA\Schema( + * type="string", + * example="1234" + * ) + * ), + * @OA\RequestBody( + * @OA\MediaType( + * mediaType="multipart/form-data", + * @OA\Schema( + * required={"title","html","start","end"}, + * @OA\Property( + * property="title", + * ref="#/components/schemas/Alert/properties/title", + * ), + * @OA\Property( + * property="html", + * ref="#/components/schemas/Alert/properties/html", + * ), + * @OA\Property( + * property="start", + * ref="#/components/schemas/Alert/properties/start", + * ), + * @OA\Property( + * property="end", + * ref="#/components/schemas/Alert/properties/end", + * ), + * @OA\Property( + * property="ctatitle", + * ref="#/components/schemas/Alert/properties/ctalink", + * ), + * @OA\Property( + * property="ctalink", + * ref="#/components/schemas/Alert/properties/ctalink", + * ), + * ) + * ) + * ), + * @OA\Response( + * response=200, + * description="Successful operation", + * @OA\JsonContent( + * @OA\Property( + * property="id", + * title="id", + * ref="#/components/schemas/Alert/properties/id" + * ) + * ), + * ) + * ) + */ + public function addAlertv2(Request $request) + { + $user = $this->getUser(); + + if (!$user->hasRole('Administrator')) { + throw new AuthenticationException(); + } + + list($start, $end, $title, $html, $ctatitle, $ctalink) = $this->validateAlertParams($request, true); + + $id = Alert::Create([ + 'start' => $start, + 'end' => $end, + 'title' => $title, + 'html' => $html, + 'ctatitle' => $ctatitle, + 'ctalink' => $ctalink + ])->id; + + return [ + 'id' => $id + ]; + } + + /** + * @OA\Patch( + * path="/api/v2/alerts/{id}", + * operationId="updateAlert", + * tags={"Alerts"}, + * summary="Edit Alert", + * description="Edits an alert.", + * @OA\Parameter( + * name="api_token", + * description="A valid user API token", + * required=true, + * in="query", + * @OA\Schema( + * type="string", + * example="1234" + * ) + * ), + * @OA\RequestBody( + * @OA\MediaType( + * mediaType="multipart/form-data", + * @OA\Schema( + * required={"title","html","start","end"}, + * @OA\Property( + * property="title", + * ref="#/components/schemas/Alert/properties/title", + * ), + * @OA\Property( + * property="html", + * ref="#/components/schemas/Alert/properties/html", + * ), + * @OA\Property( + * property="start", + * ref="#/components/schemas/Alert/properties/start", + * ), + * @OA\Property( + * property="end", + * ref="#/components/schemas/Alert/properties/end", + * ), + * @OA\Property( + * property="ctatitle", + * ref="#/components/schemas/Alert/properties/ctalink", + * ), + * @OA\Property( + * property="ctalink", + * ref="#/components/schemas/Alert/properties/ctalink", + * ), + * ) + * ) + * ), + * @OA\Response( + * response=200, + * description="Successful operation", + * @OA\JsonContent( + * @OA\Property( + * property="id", + * title="id", + * ref="#/components/schemas/Alert/properties/id" + * ) + * ), + * ) + * ) + */ + public function updateAlertv2(Request $request, $id) + { + $user = $this->getUser(); + + if (!$user->hasRole('Administrator')) { + return abort(403, 'The authenticated user is not authorized to access this resource'); + } + + $alert = Alert::findOrFail($id); + + list($start, $end, $title, $html, $ctatitle, $ctalink) = $this->validateAlertParams($request, true); + + $alert->update([ + 'start' => $start, + 'end' => $end, + 'title' => $title, + 'html' => $html, + 'ctatitle' => $ctatitle, + 'ctalink' => $ctalink + ]); + + \Cache::clear('alerts'); + + return [ + 'id' => $id + ]; + } + + private function getUser() + { + // We want to allow this call to work if a) we are logged in as a user, or b) we have a valid API token. + // + // This is a slightly odd thing to do, but it is necessary to get both the PHPUnit tests and the + // real client use of the API to work. + $user = Auth::user(); + + if (!$user) { + $user = auth('api')->user(); + } + + if (!$user) { + throw new AuthenticationException(); + } + + return $user; + } + + private function validateAlertParams(Request $request, $create): array + { + if ($create) { + $request->validate([ + 'start' => ['required', 'date_format:Y-m-d\TH:i:sP,Y-m-d\TH:i:s\Z'], + 'end' => ['required', 'date_format:Y-m-d\TH:i:sP,Y-m-d\TH:i:s\Z'], + 'title' => ['required', 'max:255'], + 'html' => ['required'], + 'ctatitle' => ['nullable', 'max:255'], + 'ctalink' => ['nullable', 'url'], + ]); + } else { + $request->validate([ + 'id' => 'required|integer', + 'start' => ['required', 'date_format:Y-m-d\TH:i:sP,Y-m-d\TH:i:s\Z'], + 'end' => ['required', 'date_format:Y-m-d\TH:i:sP,Y-m-d\TH:i:s\Z'], + 'title' => ['required', 'max:255'], + 'html' => ['required'], + 'ctatitle' => ['nullable', 'max:255'], + 'ctalink' => ['nullable', 'url'], + ]); + } + + $start = $request->input('start'); + $end = $request->input('end'); + $title = $request->input('title'); + $html = $request->input('html'); + $ctatitle = $request->input('ctatitle', null); + $ctalink = $request->input('ctalink', null); + + return [ + $start, + $end, + $title, + $html, + $ctatitle, + $ctalink + ]; + } +} diff --git a/app/Http/Resources/Alert.php b/app/Http/Resources/Alert.php new file mode 100644 index 0000000000..f5268aab58 --- /dev/null +++ b/app/Http/Resources/Alert.php @@ -0,0 +1,94 @@ + $this->id, + 'title' => $this->title, + 'html' => $this->html, + 'ctatitle' => $this->ctatitle, + 'ctalink' => $this->ctalink, + 'start' => Carbon::parse($this->start)->toIso8601String(), + 'end' => Carbon::parse($this->end)->toIso8601String(), + 'variant' => $this->variant, + ]; + + return $ret; + } +} diff --git a/app/Http/Resources/AlertCollection.php b/app/Http/Resources/AlertCollection.php new file mode 100644 index 0000000000..f850ac63d3 --- /dev/null +++ b/app/Http/Resources/AlertCollection.php @@ -0,0 +1,31 @@ +increments('id'); + $table->string('title'); + $table->text('html'); + $table->string('ctatitle')->nullable(); + $table->string('ctalink')->nullable(); + $table->string('variant')->default('secondary'); + $table->timestamp('start'); + $table->timestamp('end'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('alerts'); + } +}; diff --git a/resources/js/components/AlertBanner.vue b/resources/js/components/AlertBanner.vue new file mode 100644 index 0000000000..16096db683 --- /dev/null +++ b/resources/js/components/AlertBanner.vue @@ -0,0 +1,65 @@ + + diff --git a/resources/js/components/DashboardBanner.vue b/resources/js/components/DashboardBanner.vue deleted file mode 100644 index 4f6b082a5f..0000000000 --- a/resources/js/components/DashboardBanner.vue +++ /dev/null @@ -1,70 +0,0 @@ - - diff --git a/resources/js/components/DashboardPage.vue b/resources/js/components/DashboardPage.vue index 3217de69ea..17f41c163e 100644 --- a/resources/js/components/DashboardPage.vue +++ b/resources/js/components/DashboardPage.vue @@ -6,7 +6,7 @@
-