Added endpoints for speakers profile edit request

request-edit-speaker-permission

PUT /api/v1/speakers/{speaker_id}/edit-permission

required scopes

/speakers/write
/summits/write

get-edit-speaker-permission

GET /api/v1/speakers/{speaker_id}/edit-permission

required scopes

/summits/read
/summits/read/all

Change-Id: I53e34e32116a2bf2a39ed583fce648fc01be12fd
This commit is contained in:
smarcet 2019-04-24 15:20:35 -03:00
parent b8db89260e
commit 3c757f9cd2
63 changed files with 2162 additions and 353 deletions

View File

@ -835,10 +835,21 @@ final class OAuth2SummitSpeakersApiController extends OAuth2ProtectedController
if (!Request::isJson()) return $this->error400();
$data = Input::json();
$current_member_id = $this->resource_server_context->getCurrentUserExternalId();
if (is_null($current_member_id))
return $this->error403();
$member = $this->member_repository->getById($current_member_id);
if (is_null($member))
return $this->error403();
$speaker = $this->speaker_repository->getById($speaker_id);
if (is_null($speaker)) return $this->error404();
if(!$speaker->canBeEditedBy($member)){
return $this->error403();
}
$rules = [
'title' => 'sometimes|string|max:100',
'first_name' => 'sometimes|string|max:100',
@ -1148,4 +1159,104 @@ final class OAuth2SummitSpeakersApiController extends OAuth2ProtectedController
}
}
/**
* @param $speaker_id
* @return \Illuminate\Http\JsonResponse|mixed
*/
public function requestSpeakerEditPermission($speaker_id){
try {
$current_member_id = $this->resource_server_context->getCurrentUserExternalId();
if (is_null($current_member_id))
return $this->error403();
$request = $this->service->requestSpeakerEditPermission($current_member_id, $speaker_id);
return $this->created(
SerializerRegistry::getInstance()->getSerializer($request)
);
} catch (ValidationException $ex1) {
Log::warning($ex1);
return $this->error412(array($ex1->getMessage()));
} catch (EntityNotFoundException $ex2) {
Log::warning($ex2);
return $this->error404(array('message' => $ex2->getMessage()));
} catch (Exception $ex) {
Log::error($ex);
return $this->error500($ex);
}
}
/**
* @param $speaker_id
* @return \Illuminate\Http\JsonResponse|mixed
*/
public function getSpeakerEditPermission($speaker_id){
try {
$current_member_id = $this->resource_server_context->getCurrentUserExternalId();
if (is_null($current_member_id))
return $this->error403();
$request = $this->service->getSpeakerEditPermission($current_member_id, $speaker_id);
return $this->ok(
SerializerRegistry::getInstance()->getSerializer($request)->serialize()
);
} catch (ValidationException $ex1) {
Log::warning($ex1);
return $this->error412(array($ex1->getMessage()));
} catch (EntityNotFoundException $ex2) {
Log::warning($ex2);
return $this->error404(array('message' => $ex2->getMessage()));
} catch (Exception $ex) {
Log::error($ex);
return $this->error500($ex);
}
}
/**
* @param $speaker_id
* @param $hash
* @return \Illuminate\Http\JsonResponse|mixed
*/
public function approveSpeakerEditPermission($speaker_id, $hash){
try {
$request = $this->service->approveSpeakerEditPermission($hash, $speaker_id);
return response()->view('speakers.edit_permissions.approved', [], 200);
} catch (ValidationException $ex1) {
Log::warning($ex1);
return response()->view('speakers.edit_permissions.approved_validation_error', [], 412);
} catch (EntityNotFoundException $ex2) {
Log::warning($ex2);
return response()->view('speakers.edit_permissions.approved_error', [], 404);
} catch (Exception $ex) {
Log::error($ex);
return response()->view('speakers.edit_permissions.approved_error', [], 500);
}
}
/**
* @param $speaker_id
* @param $hash
* @return \Illuminate\Http\JsonResponse|mixed
*/
public function declineSpeakerEditPermission($speaker_id, $hash){
try {
$request = $this->service->rejectSpeakerEditPermission($hash, $speaker_id);
return response()->view('speakers.edit_permissions.rejected', [], 200);
} catch (ValidationException $ex1) {
Log::warning($ex1);
return response()->view('speakers.edit_permissions.rejected_validation_error', [], 412);
} catch (EntityNotFoundException $ex2) {
Log::warning($ex2);
return response()->view('speakers.edit_permissions.rejected_error', [], 404);
} catch (Exception $ex) {
Log::error($ex);
return response()->view('speakers.edit_permissions.rejected_error', [], 500);
}
}
}

View File

@ -30,6 +30,18 @@ Route::group([
Route::get('', 'OAuth2MembersApiController@getAll');
});
// speakers
Route::group(['prefix'=>'speakers'], function() {
Route::group(['prefix'=>'{speaker_id}'], function(){
Route::group(['prefix'=>'edit-permission'], function(){
Route::group(['prefix'=>'{token}'], function(){
Route::get('approve', 'OAuth2SummitSpeakersApiController@approveSpeakerEditPermission');
Route::get('decline', 'OAuth2SummitSpeakersApiController@declineSpeakerEditPermission');
});
});
});
});
// summits
Route::group(['prefix'=>'summits'], function() {
Route::get('', [ 'middleware' => 'cache:'.Config::get('cache_api_response.get_summit_response_lifetime', 600), 'uses' => 'OAuth2SummitApiController@getSummits']);

View File

@ -598,7 +598,7 @@ Route::group([
Route::group(['prefix' => 'speakers'], function () {
Route::get('', 'OAuth2SummitSpeakersApiController@getAll');
Route::post('', [ 'middleware' => 'auth.user:administrators|summit-front-end-administrators', 'uses' => 'OAuth2SummitSpeakersApiController@addSpeaker']);
Route::post('', 'OAuth2SummitSpeakersApiController@addSpeaker');
Route::put('merge/{speaker_from_id}/{speaker_to_id}', 'OAuth2SummitSpeakersApiController@merge');
Route::group(['prefix' => 'active-involvements'], function(){
@ -647,7 +647,9 @@ Route::group([
});
Route::group(['prefix' => '{speaker_id}'], function () {
Route::put('',[ 'middleware' => 'auth.user:administrators|summit-front-end-administrators', 'uses' => 'OAuth2SummitSpeakersApiController@updateSpeaker'])->where('speaker_id', 'me|[0-9]+');
Route::put('/edit-permission', 'OAuth2SummitSpeakersApiController@requestSpeakerEditPermission')->where('speaker_id', '[0-9]+');
Route::get('/edit-permission', 'OAuth2SummitSpeakersApiController@getSpeakerEditPermission')->where('speaker_id', '[0-9]+');
Route::put('','OAuth2SummitSpeakersApiController@updateSpeaker')->where('speaker_id', 'me|[0-9]+');
Route::delete('',[ 'middleware' => 'auth.user:administrators|summit-front-end-administrators', 'uses' => 'OAuth2SummitSpeakersApiController@deleteSpeaker'])->where('speaker_id', 'me|[0-9]+');
Route::get('', 'OAuth2SummitSpeakersApiController@getSpeaker');
Route::post('/photo', [ 'middleware' => 'auth.user:administrators|summit-front-end-administrators', 'uses' => 'OAuth2SummitSpeakersApiController@addSpeakerPhoto']);

View File

@ -0,0 +1,53 @@
<?php namespace App\Mail;
/**
* Copyright 2019 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use App\Models\Foundation\Summit\Speakers\SpeakerEditPermissionRequest;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
/**
* Class SpeakerEditPermissionApprovedEmail
* @package App\Mail
*/
final class SpeakerEditPermissionApprovedEmail extends Mailable
{
use Queueable, SerializesModels;
/**
* @var SpeakerEditPermissionRequest
*/
public $request;
/**
* SpeakerEditPermissionRequested constructor.
* @param SpeakerEditPermissionRequest $request
*/
public function __construct(SpeakerEditPermissionRequest $request)
{
$this->request = $request;
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this
->from('noreply@openstack.org')
->subject("OpenStack - Speaker Profile Edit Permission Approved")
->view('emails.speakers.permissioneditapproved');
}
}

View File

@ -0,0 +1,53 @@
<?php namespace App\Mail;
/**
* Copyright 2019 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use App\Models\Foundation\Summit\Speakers\SpeakerEditPermissionRequest;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
/**
* Class SpeakerEditPermissionRejectedEmail
* @package App\Mail
*/
final class SpeakerEditPermissionRejectedEmail extends Mailable
{
use Queueable, SerializesModels;
/**
* @var SpeakerEditPermissionRequest
*/
public $request;
/**
* SpeakerEditPermissionRequested constructor.
* @param SpeakerEditPermissionRequest $request
*/
public function __construct(SpeakerEditPermissionRequest $request)
{
$this->request = $request;
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this
->from('noreply@openstack.org')
->subject("OpenStack - Speaker Profile Edit Permission Rejected")
->view('emails.speakers.permissioneditrejected');
}
}

View File

@ -0,0 +1,60 @@
<?php namespace App\Mail;
/**
* Copyright 2019 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use App\Models\Foundation\Summit\Speakers\SpeakerEditPermissionRequest;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
/**
* Class SpeakerEditPermissionRequested
* @package App\Mail
*/
final class SpeakerEditPermissionRequestedEmail extends Mailable
{
use Queueable, SerializesModels;
/**
* @var SpeakerEditPermissionRequest
*/
public $request;
/**
* @var string
*/
public $token;
/**
* SpeakerEditPermissionRequested constructor.
* @param SpeakerEditPermissionRequest $request
* @param string $token
*/
public function __construct(SpeakerEditPermissionRequest $request, string $token)
{
$this->request = $request;
$this->token = $token;
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this
->from('noreply@openstack.org')
->subject("OpenStack - Speaker Profile Edit Permission Requested")
->view('emails.speakers.permissioneditrequested');
}
}

View File

@ -149,6 +149,8 @@ final class SerializerRegistry
self::SerializerType_Private => AdminPresentationSpeakerSerializer::class
];
$this->registry['SpeakerEditPermissionRequest'] = SpeakerEditPermissionRequestSerializer::class;
// RSVP
$this->registry['RSVP'] = RSVPSerializer::class;
$this->registry['RSVPTemplate'] = RSVPTemplateSerializer::class;
@ -197,6 +199,7 @@ final class SerializerRegistry
$this->registry['SummitLocationImage'] = SummitLocationImageSerializer::class;
$this->registry['SummitLocationBanner'] = SummitLocationBannerSerializer::class;
$this->registry['ScheduledSummitLocationBanner'] = ScheduledSummitLocationBannerSerializer::class;
// track tag groups
$this->registry['TrackTagGroup'] = TrackTagGroupSerializer::class;
$this->registry['TrackTagGroupAllowedTag'] = TrackTagGroupAllowedTagSerializer::class;

View File

@ -0,0 +1,27 @@
<?php namespace ModelSerializers;
/**
* Copyright 2019 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
/**
* Class SpeakerEditPermissionRequestSerializer
* @package ModelSerializers
*/
class SpeakerEditPermissionRequestSerializer extends SilverStripeSerializer
{
protected static $array_mappings = [
'Approved' => 'approved:json_boolean',
'ApprovedDate' => 'approved_date:datetime_epoch',
'SpeakerId' => 'speaker_id:json_int',
'RequestedById' => 'requested_by_id:json_int',
];
}

View File

@ -23,7 +23,10 @@ use DateTime;
* "SpeakerCreationEmailCreationRequest" = "SpeakerCreationEmailCreationRequest",
* "MemberPromoCodeEmailCreationRequest"= "MemberPromoCodeEmailCreationRequest",
* "CalendarSyncErrorEmailRequest" = "CalendarSyncErrorEmailRequest",
* "SpeakerSelectionAnnouncementEmailCreationRequest" = "SpeakerSelectionAnnouncementEmailCreationRequest"})
* "SpeakerSelectionAnnouncementEmailCreationRequest" = "SpeakerSelectionAnnouncementEmailCreationRequest",
* "PresentationCreatorNotificationEmailRequest" = "PresentationCreatorNotificationEmailRequest",
* "PresentationSpeakerNotificationEmailRequest" = "PresentationSpeakerNotificationEmailRequest"
* })
* Class EmailCreationRequest
* @package models\main
*/

View File

@ -0,0 +1,52 @@
<?php namespace models\main;
/**
* Copyright 2019 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use Doctrine\ORM\Mapping AS ORM;
use models\summit\Presentation;
/**
* @ORM\Entity
* @ORM\Table(name="PresentationCreatorNotificationEmailRequest")
* Class PresentationCreatorNotificationEmailRequest
* @package models\main
*/
class PresentationCreatorNotificationEmailRequest extends EmailCreationRequest
{
/**
* @ORM\ManyToOne(targetEntity="models\summit\Presentation")
* @ORM\JoinColumn(name="PresentationID", referencedColumnName="ID")
* @var Presentation
*/
protected $presentation;
public function __construct()
{
$this->template_name = "presentation-creator-notification";
parent::__construct();
}
/**
* @return Presentation
*/
public function getPresentation(): Presentation
{
return $this->presentation;
}
/**
* @param Presentation $presentation
*/
public function setPresentation(Presentation $presentation): void
{
$this->presentation = $presentation;
}
}

View File

@ -0,0 +1,77 @@
<?php namespace models\main;
/**
* Copyright 2019 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use Doctrine\ORM\Mapping AS ORM;
use models\summit\Presentation;
use models\summit\PresentationSpeaker;
/**
* @ORM\Entity
* @ORM\Table(name="PresentationSpeakerNotificationEmailRequest")
* Class PresentationSpeakerNotificationEmailRequest
* @package models\main
*/
class PresentationSpeakerNotificationEmailRequest extends EmailCreationRequest
{
/**
* @ORM\ManyToOne(targetEntity="models\summit\PresentationSpeaker")
* @ORM\JoinColumn(name="SpeakerID", referencedColumnName="ID")
* @var PresentationSpeaker
*/
protected $speaker;
/**
* @ORM\ManyToOne(targetEntity="models\summit\Presentation")
* @ORM\JoinColumn(name="PresentationID", referencedColumnName="ID")
* @var Presentation
*/
protected $presentation;
public function __construct()
{
$this->template_name = "presentation-speaker-notification";
parent::__construct();
}
/**
* @return PresentationSpeaker
*/
public function getSpeaker()
{
return $this->speaker;
}
/**
* @param PresentationSpeaker $speaker
*/
public function setSpeaker($speaker)
{
$this->speaker = $speaker;
}
/**
* @return Presentation
*/
public function getPresentation(): Presentation
{
return $this->presentation;
}
/**
* @param Presentation $presentation
*/
public function setPresentation(Presentation $presentation): void
{
$this->presentation = $presentation;
}
}

View File

@ -30,7 +30,7 @@ class InteropProgramVersion extends SilverstripeBaseModel
/**
* @ORM\ManyToMany(targetEntity="InteropCapability", cascade={"persist"})
* @ORM\JoinTable(name="nteropProgramVersion_Capabilities",
* @ORM\JoinTable(name="InteropProgramVersion_Capabilities",
* joinColumns={@ORM\JoinColumn(name="InteropProgramVersionID", referencedColumnName="ID")},
* inverseJoinColumns={@ORM\JoinColumn(name="InteropCapabilityID", referencedColumnName="ID")}
* )

View File

@ -0,0 +1,34 @@
<?php namespace App\Models\Foundation\Summit\Factories;
/**
* Copyright 2019 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use App\Models\Foundation\Summit\Speakers\SpeakerEditPermissionRequest;
use models\main\Member;
use models\summit\PresentationSpeaker;
/**
* Class SpeakerEditPermissionRequestFactory
* @package App\Models\Foundation\Summit\Factories
*/
final class SpeakerEditPermissionRequestFactory
{
/**
* @param PresentationSpeaker $speaker
* @param Member $requestor
* @return SpeakerEditPermissionRequest
*/
public static function build(PresentationSpeaker $speaker, Member $requestor){
$request = new SpeakerEditPermissionRequest();
$request->setSpeaker($speaker);
$request->setRequestedBy($requestor);
return $request;
}
}

View File

@ -0,0 +1,36 @@
<?php namespace App\Models\Foundation\Summit\Repositories;
/**
* Copyright 2019 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use App\Models\Foundation\Summit\Speakers\SpeakerEditPermissionRequest;
use models\main\Member;
use models\summit\PresentationSpeaker;
use models\utils\IBaseRepository;
/**
* Interface ISpeakerEditPermissionRequestRepository
* @package App\Models\Foundation\Summit\Repositories
*/
interface ISpeakerEditPermissionRequestRepository extends IBaseRepository
{
/**
* @param PresentationSpeaker $speaker
* @param Member $requestor
* @return ?SpeakerEditPermissionRequest
*/
public function getBySpeakerAndRequestor(PresentationSpeaker $speaker, Member $requestor): ?SpeakerEditPermissionRequest;
/**
* @param string $token
* @return ?SpeakerEditPermissionRequest
*/
public function getByToken(string $token):?SpeakerEditPermissionRequest;
}

View File

@ -11,9 +11,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use Doctrine\ORM\Mapping AS ORM;
use App\Models\Foundation\Main\Language;
use App\Models\Foundation\Summit\SelectionPlan;
use Doctrine\ORM\Mapping AS ORM;
use App\Events\PresentationSpeakerCreated;
use App\Events\PresentationSpeakerDeleted;
use App\Events\PresentationSpeakerUpdated;
@ -147,6 +147,12 @@ class PresentationSpeaker extends SilverstripeBaseModel
*/
private $moderated_presentations;
/**
* @ORM\OneToMany(targetEntity="App\Models\Foundation\Summit\Speakers\SpeakerEditPermissionRequest", mappedBy="speaker", cascade={"persist"})
* @var Presentation[]
*/
private $granted_edit_permissions;
/**
* @ORM\ManyToOne(targetEntity="models\main\File", cascade={"persist"})
* @ORM\JoinColumn(name="PhotoID", referencedColumnName="ID")
@ -330,6 +336,7 @@ class PresentationSpeaker extends SilverstripeBaseModel
$this->organizational_roles = new ArrayCollection;
$this->active_involvements = new ArrayCollection;
$this->announcement_summit_emails = new ArrayCollection;
$this->granted_edit_permissions = new ArrayCollection;
}
/**
@ -1620,4 +1627,17 @@ SQL;
}
return $photoUrl;
}
/**
* @param Member $member
* @return bool
*/
public function canBeEditedBy(Member $member):bool{
if($member->isAdmin()) return true;
$criteria = Criteria::create();
$criteria
->where(Criteria::expr()->eq('requested_by', $member))
->andWhere(Criteria::expr()->eq('approved', true));
return $this->granted_edit_permissions->matching($criteria)->count() > 0 ;
}
}

View File

@ -0,0 +1,207 @@
<?php namespace App\Models\Foundation\Summit\Speakers;
/**
* Copyright 2019 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use Doctrine\ORM\Mapping AS ORM;
use DateTime;
use Illuminate\Support\Facades\Config;
use models\main\Member;
use models\summit\PresentationSpeaker;
use models\utils\RandomGenerator;
use models\utils\SilverstripeBaseModel;
/**
* @ORM\Entity(repositoryClass="App\Repositories\Summit\DoctrineSpeakerEditPermissionRequestRepository")
* @ORM\Table(name="SpeakerEditPermissionRequest")
* Class SpeakerEditPermissionRequest
* @package models\summit
*/
class SpeakerEditPermissionRequest extends SilverstripeBaseModel
{
/**
* @ORM\ManyToOne(targetEntity="models\summit\PresentationSpeaker", inversedBy="granted_edit_permissions")
* @ORM\JoinColumn(name="SpeakerID", referencedColumnName="ID")
* @var PresentationSpeaker
*/
private $speaker;
/**
* @ORM\ManyToOne(targetEntity="models\main\Member")
* @ORM\JoinColumn(name="RequestedByID", referencedColumnName="ID")
* @var Member
*/
private $requested_by;
/**
* @ORM\Column(name="Approved", type="boolean")
*/
private $approved;
/**
* @ORM\Column(name="ApprovedDate", type="datetime")
*/
private $approved_date;
/**
* @ORM\Column(name="Hash", type="string")
*/
private $hash;
public function __construct()
{
parent::__construct();
$this->approved = false;
}
/**
* @return PresentationSpeaker
*/
public function getSpeaker(): PresentationSpeaker
{
return $this->speaker;
}
/**
* @param PresentationSpeaker $speaker
*/
public function setSpeaker(PresentationSpeaker $speaker): void
{
$this->speaker = $speaker;
}
/**
* @return Member
*/
public function getRequestedBy(): Member
{
return $this->requested_by;
}
/**
* @param Member $requested_by
*/
public function setRequestedBy(Member $requested_by): void
{
$this->requested_by = $requested_by;
}
/**
* @return bool
*/
public function isApproved():bool
{
return $this->approved;
}
/**
* @param mixed $approved
*/
public function setApproved($approved): void
{
$this->approved = $approved;
}
/**
* @return DateTime
*/
public function getApprovedDate():DateTime
{
return $this->approved_date;
}
/**
* @param DateTime $approved_date
*/
public function setApprovedDate($approved_date): void
{
$this->approved_date = $approved_date;
}
/**
* @return string
*/
public function getHash():string
{
return $this->hash;
}
/**
* @return string
*/
public function generateConfirmationToken() {
$generator = new RandomGenerator();
$token = sprintf("%s.%s.%s",$generator->randomToken(), $this->speaker->getId() , $this->requested_by->getId());
$this->hash = self::HashConfirmationToken($token);
return $token;
}
/**
* @param string $token
* @return string
*/
public static function HashConfirmationToken(string $token){
return md5($token);
}
public function approve():void{
$this->approved = true;
$this->approved_date = new \DateTime('now', new \DateTimeZone('UTC'));
}
public function reject():void{
$this->approved = false;
$this->approved_date = new \DateTime('now', new \DateTimeZone('UTC'));
}
/**
* @return bool
*/
public function isActionTaken():bool
{
return !is_null( $this->approved_date );
}
/**
* @param int $speaker_id
* @param string $token
* @return string
*/
public function getConfirmationLink(int $speaker_id, string $token): string{
return sprintf("%s/api/public/v1/speakers/%s/edit-permission/%s/approve", Config::get("app.url", '#'), $speaker_id, $token);
}
/**
* @return int
*/
public function getSpeakerId():int {
try {
return !is_null($this->speaker) ? $this->speaker->getId() : 0;
}
catch(\Exception $ex){
return 0;
}
}
/**
* @return int
*/
public function getRequestedById():int {
try {
return !is_null($this->requested_by) ? $this->requested_by->getId() : 0;
}
catch(\Exception $ex){
return 0;
}
}
}

View File

@ -1,5 +1,4 @@
<?php namespace App\Models\ResourceServer;
/**
* Copyright 2016 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
@ -12,10 +11,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use models\utils\IEntity;
use Doctrine\ORM\Mapping AS ORM;
/***
* @ORM\MappedSuperclass
* Class ResourceServerEntity
@ -23,6 +20,7 @@ use Doctrine\ORM\Mapping AS ORM;
*/
class ResourceServerEntity implements IEntity
{
const EntityManager = 'config';
const DefaultTimeZone = 'America/Chicago';

View File

@ -29,8 +29,8 @@ final class RandomGenerator {
// TODO Fails with "Could not gather sufficient random data" on IIS, temporarily disabled on windows
if(!$isWin) {
if(function_exists('mcrypt_create_iv')) {
$e = mcrypt_create_iv(64, MCRYPT_DEV_URANDOM);
if(function_exists('random_bytes')) {
$e = random_bytes(64);
if($e !== false) return $e;
}
}

View File

@ -152,5 +152,5 @@ class SilverstripeBaseModel extends BaseEntity
return Registry::getManager(self::EntityManager);
}
const EntityManager = 'ss';
const EntityManager = 'model';
}

View File

@ -0,0 +1,35 @@
<?php namespace App\Repositories;
/**
* Copyright 2019 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use App\Models\ResourceServer\ResourceServerEntity;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\ClassMetadata;
use LaravelDoctrine\ORM\Facades\Registry;
/**
* Class ConfigDoctrineRepository
* @package App\Repositories
*/
abstract class ConfigDoctrineRepository extends DoctrineRepository
{
/**
* Initializes a new <tt>EntityRepository</tt>.
*
* @param EntityManager $em The EntityManager to use.
* @param ClassMetadata $class The class descriptor.
*/
public function __construct($em, ClassMetadata $class)
{
$em = Registry::getManager(ResourceServerEntity::EntityManager);
parent::__construct($em, $class);
}
}

View File

@ -11,37 +11,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\QueryBuilder;
use LaravelDoctrine\ORM\Facades\Registry;use models\utils\IBaseRepository;
use LaravelDoctrine\ORM\Facades\Registry;
use models\utils\IBaseRepository;
use models\utils\IEntity;
use models\utils\SilverstripeBaseModel;
use utils\Filter;
use utils\Order;
use utils\PagingInfo;
use utils\PagingResponse;
use Doctrine\ORM\Tools\Pagination\Paginator;
/**
* Class DoctrineRepository
* @package App\Repositories
*/
abstract class DoctrineRepository extends EntityRepository implements IBaseRepository
{
protected static $em_name = 'default';
/**
* Initializes a new <tt>EntityRepository</tt>.
*
* @param EntityManager $em The EntityManager to use.
* @param ClassMetadata $class The class descriptor.
*/
public function __construct($em, ClassMetadata $class)
{
$em = Registry::getManager(static::$em_name);
parent::__construct($em, $class);
}
public function getById($id)
{

View File

@ -25,6 +25,7 @@ use App\Models\Foundation\Summit\Repositories\IPresentationSpeakerSummitAssistan
use App\Models\Foundation\Summit\Repositories\IRSVPTemplateRepository;
use App\Models\Foundation\Summit\Repositories\ISelectionPlanRepository;
use App\Models\Foundation\Summit\Repositories\ISpeakerActiveInvolvementRepository;
use App\Models\Foundation\Summit\Repositories\ISpeakerEditPermissionRequestRepository;
use App\Models\Foundation\Summit\Repositories\ISpeakerOrganizationalRoleRepository;
use App\Models\Foundation\Summit\Repositories\ISummitEventTypeRepository;
use App\Models\Foundation\Summit\Repositories\ISummitLocationBannerRepository;
@ -33,6 +34,7 @@ use App\Models\Foundation\Summit\Repositories\ISummitTrackRepository;
use App\Models\Foundation\Summit\Repositories\ITrackQuestionTemplateRepository;
use App\Models\Foundation\Summit\Repositories\ITrackTagGroupAllowedTagsRepository;
use App\Models\Foundation\Summit\SelectionPlan;
use App\Models\Foundation\Summit\Speakers\SpeakerEditPermissionRequest;
use App\Models\Foundation\Summit\TrackTagGroupAllowedTag;
use Illuminate\Support\Facades\App;
use Illuminate\Support\ServiceProvider;
@ -392,5 +394,12 @@ final class RepositoriesProvider extends ServiceProvider
}
);
App::singleton(
ISpeakerEditPermissionRequestRepository::class,
function(){
return EntityManager::getRepository(SpeakerEditPermissionRequest::class);
}
);
}
}

View File

@ -15,7 +15,9 @@
use App\Models\ResourceServer\ApiEndpoint;
use App\Models\ResourceServer\IApiEndpoint;
use App\Models\ResourceServer\IApiEndpointRepository;
use App\Repositories\ConfigDoctrineRepository;
use App\Repositories\DoctrineRepository;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\QueryBuilder;
use Illuminate\Support\Facades\Log;
@ -24,10 +26,20 @@ use Illuminate\Support\Facades\Log;
* @package repositories\resource_server
*/
final class DoctrineApiEndpointRepository
extends DoctrineRepository
extends ConfigDoctrineRepository
implements IApiEndpointRepository
{
/**
* Initializes a new <tt>EntityRepository</tt>.
*
* @param EntityManager $em The EntityManager to use.
* @param ClassMetadata $class The class descriptor.
*/
public function __construct($em, ClassMetadata $class)
{
parent::__construct($em, $class);
}
/**
* @param string $url
* @param string $http_method
@ -59,7 +71,7 @@ final class DoctrineApiEndpointRepository
*/
protected function getBaseEntity()
{
// TODO: Implement getBaseEntity() method.
return ApiEndpoint::class;
}
/**
@ -67,7 +79,7 @@ final class DoctrineApiEndpointRepository
*/
protected function getFilterMappings()
{
return ApiEndpoint::class;
return [];
}
/**

View File

@ -13,15 +13,22 @@
**/
use App\Models\ResourceServer\EndPointRateLimitByIP;
use App\Models\ResourceServer\IEndpointRateLimitByIPRepository;
use App\Repositories\ConfigDoctrineRepository;
use App\Repositories\DoctrineRepository;
use Doctrine\ORM\QueryBuilder;
use Illuminate\Support\Facades\Log;
use models\utils\IEntity;
use utils\Filter;
use utils\Order;
use utils\PagingInfo;
use utils\PagingResponse;
/**
* Class DoctrineEndPointRateLimitByIPRepository
* @package repositories\resource_server
*/
final class DoctrineEndPointRateLimitByIPRepository
extends DoctrineRepository
extends ConfigDoctrineRepository
implements IEndpointRateLimitByIPRepository
{
/**
@ -84,4 +91,50 @@ final class DoctrineEndPointRateLimitByIPRepository
{
return $query;
}
/**
* @param int $id
* @return IEntity
*/
public function getById($id)
{
// TODO: Implement getById() method.
}
/**
* @param IEntity $entity
* @return void
*/
public function add($entity)
{
// TODO: Implement add() method.
}
/**
* @param IEntity $entity
* @return void
*/
public function delete($entity)
{
// TODO: Implement delete() method.
}
/**
* @return IEntity[]
*/
public function getAll()
{
// TODO: Implement getAll() method.
}
/**
* @param PagingInfo $paging_info
* @param Filter|null $filter
* @param Order|null $order
* @return PagingResponse
*/
public function getAllByPage(PagingInfo $paging_info, Filter $filter = null, Order $order = null)
{
// TODO: Implement getAllByPage() method.
}
}

View File

@ -1,5 +1,4 @@
<?php namespace repositories\resource_server;
/**
* Copyright 2015 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
@ -12,11 +11,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use App\Models\ResourceServer\IApiEndpoint;
use App\Models\ResourceServer\IApiEndpointRepository;
use models\utils\EloquentBaseRepository;
use models\utils\IEntity;
use utils\Filter;
use utils\Order;
use utils\PagingInfo;
use utils\PagingResponse;
/**
* Class EloquentApiEndpointRepository
* @package repositories\resource_server
@ -53,4 +55,49 @@ class EloquentApiEndpointRepository extends EloquentBaseRepository implements IA
))->firstOrFail();
}
/**
* @param int $id
* @return IEntity
*/
public function getById($id)
{
// TODO: Implement getById() method.
}
/**
* @param IEntity $entity
* @return void
*/
public function add($entity)
{
// TODO: Implement add() method.
}
/**
* @param IEntity $entity
* @return void
*/
public function delete($entity)
{
// TODO: Implement delete() method.
}
/**
* @return IEntity[]
*/
public function getAll()
{
// TODO: Implement getAll() method.
}
/**
* @param PagingInfo $paging_info
* @param Filter|null $filter
* @param Order|null $order
* @return PagingResponse
*/
public function getAllByPage(PagingInfo $paging_info, Filter $filter = null, Order $order = null)
{
// TODO: Implement getAllByPage() method.
}
}

View File

@ -1,5 +1,4 @@
<?php namespace App\Repositories;
/**
* Copyright 2016 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
@ -12,16 +11,31 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\QueryBuilder;
use Illuminate\Support\Facades\App;
use models\utils\SilverstripeBaseModel;
use LaravelDoctrine\ORM\Facades\Registry;
/**
* Class SilverStripeDoctrineRepository
* @package App\Repositories
*/
abstract class SilverStripeDoctrineRepository extends DoctrineRepository
{
protected static $em_name = 'ss';
/**
* Initializes a new <tt>EntityRepository</tt>.
*
* @param EntityManager $em The EntityManager to use.
* @param ClassMetadata $class The class descriptor.
*/
public function __construct($em, ClassMetadata $class)
{
$em = Registry::getManager(SilverstripeBaseModel::EntityManager);
parent::__construct($em, $class);
}
/**
* @return array

View File

@ -0,0 +1,72 @@
<?php namespace App\Repositories\Summit;
/**
* Copyright 2019 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use App\Models\Foundation\Summit\Repositories\ISpeakerEditPermissionRequestRepository;
use App\Models\Foundation\Summit\Speakers\SpeakerEditPermissionRequest;
use App\Repositories\SilverStripeDoctrineRepository;
use models\main\Member;
use models\summit\PresentationSpeaker;
/**
* Class DoctrineSpeakerEditPermissionRequestRepository
* @package App\Repositories\Summit
*/
final class DoctrineSpeakerEditPermissionRequestRepository
extends SilverStripeDoctrineRepository
implements ISpeakerEditPermissionRequestRepository
{
/**
* @return string
*/
protected function getBaseEntity()
{
return SpeakerEditPermissionRequest::class;
}
/**
* @param PresentationSpeaker $speaker
* @param Member $requestor
* @return ?SpeakerEditPermissionRequest
*/
public function getBySpeakerAndRequestor(PresentationSpeaker $speaker, Member $requestor): ?SpeakerEditPermissionRequest
{
return $this->getEntityManager()
->createQueryBuilder()
->select("r")
->from(SpeakerEditPermissionRequest::class, "r")
->where("r.speaker = :speaker")
->andWhere("r.requested_by = :requestor")
->setParameter("speaker", $speaker)
->setParameter("requestor", $requestor)
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
}
/**
* @param string $token
* @return ?SpeakerEditPermissionRequest
*/
public function getByToken(string $token): ?SpeakerEditPermissionRequest
{
return $this->getEntityManager()
->createQueryBuilder()
->select("r")
->from(SpeakerEditPermissionRequest::class, "r")
->where("r.hash = :hash")
->setParameter("hash", SpeakerEditPermissionRequest::HashConfirmationToken($token))
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
}
}

View File

@ -11,6 +11,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use App\Models\Foundation\Summit\Speakers\SpeakerEditPermissionRequest;
use models\exceptions\EntityNotFoundException;
use models\exceptions\ValidationException;
use models\main\EmailCreationRequest;
@ -130,4 +131,40 @@ interface ISpeakerService
* @throws ValidationException
*/
public function sendSpeakerSummitAssistanceAnnouncementMail(Summit $summit, $assistance_id);
/**
* @param int $requested_by_id
* @param int $speaker_id
* @return SpeakerEditPermissionRequest
* @throws EntityNotFoundException
* @throws ValidationException
*/
public function requestSpeakerEditPermission(int $requested_by_id, int $speaker_id):SpeakerEditPermissionRequest;
/**
* @param int $requested_by_id
* @param int $speaker_id
* @return SpeakerEditPermissionRequest
* @throws EntityNotFoundException
* @throws ValidationException
*/
public function getSpeakerEditPermission(int $requested_by_id, int $speaker_id):SpeakerEditPermissionRequest;
/**
* @param string $token
* @param int $speaker_id
* @return SpeakerEditPermissionRequest
* @throws EntityNotFoundException
* @throws ValidationException
*/
public function approveSpeakerEditPermission(string $token, int $speaker_id):SpeakerEditPermissionRequest;
/**
* @param string $token
* @param int $speaker_id
* @return SpeakerEditPermissionRequest
* @throws EntityNotFoundException
* @throws ValidationException
*/
public function rejectSpeakerEditPermission(string $token, int $speaker_id):SpeakerEditPermissionRequest;
}

View File

@ -23,9 +23,12 @@ use App\Models\Foundation\Summit\Events\Presentations\TrackQuestions\TrackAnswer
use Illuminate\Support\Facades\Event;
use models\exceptions\EntityNotFoundException;
use models\exceptions\ValidationException;
use models\main\IEmailCreationRequestRepository;
use models\main\IFolderRepository;
use models\main\ITagRepository;
use models\main\Member;
use models\main\PresentationCreatorNotificationEmailRequest;
use models\main\PresentationSpeakerNotificationEmailRequest;
use models\summit\ISpeakerRepository;
use models\summit\ISummitEventRepository;
use models\summit\Presentation;
@ -77,6 +80,11 @@ final class PresentationService
*/
private $folder_repository;
/**
* @var IEmailCreationRequestRepository
*/
private $email_creation_request_repository;
/**
* PresentationService constructor.
* @param ISummitEventRepository $presentation_repository
@ -85,6 +93,7 @@ final class PresentationService
* @param IFolderService $folder_service
* @param IFileUploader $file_uploader
* @param IFolderRepository $folder_repository
* @param IEmailCreationRequestRepository $email_creation_request_repository
* @param ITransactionService $tx_service
*/
public function __construct
@ -95,6 +104,7 @@ final class PresentationService
IFolderService $folder_service,
IFileUploader $file_uploader,
IFolderRepository $folder_repository,
IEmailCreationRequestRepository $email_creation_request_repository,
ITransactionService $tx_service
)
{
@ -105,6 +115,7 @@ final class PresentationService
$this->folder_service = $folder_service;
$this->file_uploader = $file_uploader;
$this->folder_repository = $folder_repository;
$this->email_creation_request_repository = $email_creation_request_repository;
}
/**
@ -605,6 +616,23 @@ final class PresentationService
$presentation->setProgress(Presentation::PHASE_COMPLETE);
$presentation->setStatus(Presentation::STATUS_RECEIVED);
// create email requests
foreach($presentation->getSpeakers() as $speaker){
if($speaker->getMemberId() == $presentation->getCreatorId()) continue;
$email = new PresentationSpeakerNotificationEmailRequest();
$email->setSpeaker($speaker);
$email->setPresentation($presentation);
$this->email_creation_request_repository->add($email);
}
$email = new PresentationCreatorNotificationEmailRequest();
$email->setPresentation($presentation);
$this->email_creation_request_repository->add($email);
return $presentation;
});
}

View File

@ -1,5 +1,4 @@
<?php namespace services\model;
/**
* Copyright 2017 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
@ -12,15 +11,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use App\Mail\SpeakerEditPermissionApprovedEmail;
use App\Mail\SpeakerEditPermissionRejectedEmail;
use App\Mail\SpeakerEditPermissionRequestedEmail;
use App\Models\Foundation\Main\CountryCodes;
use App\Models\Foundation\Main\Repositories\ILanguageRepository;
use App\Models\Foundation\Summit\Factories\PresentationSpeakerSummitAssistanceConfirmationRequestFactory;
use App\Models\Foundation\Summit\Factories\SpeakerEditPermissionRequestFactory;
use App\Models\Foundation\Summit\Repositories\IPresentationSpeakerSummitAssistanceConfirmationRequestRepository;
use App\Models\Foundation\Summit\Repositories\ISpeakerActiveInvolvementRepository;
use App\Models\Foundation\Summit\Repositories\ISpeakerEditPermissionRequestRepository;
use App\Models\Foundation\Summit\Repositories\ISpeakerOrganizationalRoleRepository;
use App\Models\Foundation\Summit\Speakers\SpeakerEditPermissionRequest;
use App\Services\Model\AbstractService;
use App\Services\Model\IFolderService;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Mail;
use libs\utils\ITransactionService;
use models\exceptions\EntityNotFoundException;
use models\exceptions\ValidationException;
@ -45,7 +52,6 @@ use models\summit\SpeakerSummitRegistrationPromoCode;
use models\summit\SpeakerTravelPreference;
use models\summit\Summit;
use App\Http\Utils\IFileUploader;
/**
* Class SpeakerService
* @package services\model
@ -109,6 +115,11 @@ final class SpeakerService
*/
private $file_uploader;
/**
* @var ISpeakerEditPermissionRequestRepository
*/
private $speaker_edit_permisssion_repository;
/**
* SpeakerService constructor.
* @param ISpeakerRepository $speaker_repository
@ -122,6 +133,7 @@ final class SpeakerService
* @param ISpeakerOrganizationalRoleRepository $speaker_organizational_role_repository
* @param ISpeakerActiveInvolvementRepository $speaker_involvement_repository
* @param IFileUploader $file_uploader
* @param ISpeakerEditPermissionRequestRepository $speaker_edit_permisssion_repository
* @param ITransactionService $tx_service
*/
public function __construct
@ -137,6 +149,7 @@ final class SpeakerService
ISpeakerOrganizationalRoleRepository $speaker_organizational_role_repository,
ISpeakerActiveInvolvementRepository $speaker_involvement_repository,
IFileUploader $file_uploader,
ISpeakerEditPermissionRequestRepository $speaker_edit_permisssion_repository,
ITransactionService $tx_service
)
{
@ -152,6 +165,7 @@ final class SpeakerService
$this->speaker_organizational_role_repository = $speaker_organizational_role_repository;
$this->speaker_involvement_repository = $speaker_involvement_repository;
$this->file_uploader = $file_uploader;
$this->speaker_edit_permisssion_repository = $speaker_edit_permisssion_repository;
}
/**
@ -1018,4 +1032,107 @@ final class SpeakerService
});
}
/**
* @param int $requested_by_id
* @param int $speaker_id
* @return SpeakerEditPermissionRequest
* @throws EntityNotFoundException
* @throws ValidationException
*/
public function requestSpeakerEditPermission(int $requested_by_id, int $speaker_id): SpeakerEditPermissionRequest
{
return $this->tx_service->transaction(function () use ($requested_by_id, $speaker_id) {
$requestor = $this->member_repository->getById($requested_by_id);
if(is_null($requestor))
throw new EntityNotFoundException();
$speaker = $this->speaker_repository->getById($speaker_id);
if(is_null($speaker))
throw new EntityNotFoundException();
$request = $this->speaker_edit_permisssion_repository->getBySpeakerAndRequestor($speaker, $requestor);
if(!is_null($request) && $request->isActionTaken())
throw new ValidationException("there is another permission edit request already redeem!");
// build request with factory
$request = SpeakerEditPermissionRequestFactory::build($speaker, $requestor);
$token = $request->generateConfirmationToken();
Mail::to($request->getSpeaker()->getEmail())->send(new SpeakerEditPermissionRequestedEmail($request, $token));
$this->speaker_edit_permisssion_repository->add($request);
return $request;
});
}
/**
* @param int $requested_by_id
* @param int $speaker_id
* @return SpeakerEditPermissionRequest
* @throws EntityNotFoundException
* @throws ValidationException
*/
public function getSpeakerEditPermission(int $requested_by_id, int $speaker_id): SpeakerEditPermissionRequest
{
return $this->tx_service->transaction(function () use ($requested_by_id, $speaker_id) {
$requestor = $this->member_repository->getById($requested_by_id);
if(is_null($requestor))
throw new EntityNotFoundException();
$speaker = $this->speaker_repository->getById($speaker_id);
if(is_null($speaker))
throw new EntityNotFoundException();
$request = $this->speaker_edit_permisssion_repository->getBySpeakerAndRequestor($speaker, $requestor);
if(is_null($request))
throw new EntityNotFoundException();
return $request;
});
}
/**
* @param string $token
* @param int $speaker_id
* @return SpeakerEditPermissionRequest
* @throws EntityNotFoundException
* @throws ValidationException
*/
public function approveSpeakerEditPermission(string $token, int $speaker_id): SpeakerEditPermissionRequest
{
return $this->tx_service->transaction(function () use ($token, $speaker_id) {
$request = $this->speaker_edit_permisssion_repository->getByToken($token);
if(is_null($request))
throw new EntityNotFoundException();
if($request->isApproved())
throw new ValidationException();
$request->approve();
Mail::to($request->getRequestedBy()->getEmail())->send(new SpeakerEditPermissionApprovedEmail($request));
return $request;
});
}
/**
* @param string $token
* @param int $speaker_id
* @return SpeakerEditPermissionRequest
* @throws EntityNotFoundException
* @throws ValidationException
*/
public function rejectSpeakerEditPermission(string $token, int $speaker_id): SpeakerEditPermissionRequest
{
return $this->tx_service->transaction(function () use ($token, $speaker_id) {
$request = $this->speaker_edit_permisssion_repository->getByToken($token);
if(is_null($request))
throw new EntityNotFoundException();
if($request->isActionTaken())
throw new ValidationException();
$request->reject();
Mail::to($request->getRequestedBy()->getEmail())->send(new SpeakerEditPermissionRejectedEmail($request));
return $request;
});
}
}

View File

@ -13,6 +13,7 @@
**/
use App\Permissions\IPermissionsManager;
use App\Permissions\PermissionsManager;
use App\Repositories\DoctrineRepository;
use App\Services\Apis\CalendarSync\ICalendarSyncRemoteFacadeFactory;
use App\Services\Apis\GoogleGeoCodingAPI;
use App\Services\Apis\IGeoCodingAPI;
@ -48,6 +49,7 @@ use App\Services\SummitEventTypeService;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\ServiceProvider;
use models\utils\SilverstripeBaseModel;
use ModelSerializers\BaseSerializerTypeSelector;
use ModelSerializers\ISerializerTypeSelector;
use services\apis\CalendarSync\CalendarSyncRemoteFacadeFactory;
@ -83,7 +85,7 @@ final class ServicesProvider extends ServiceProvider
App::singleton(IPermissionsManager::class, PermissionsManager::class);
App::singleton(\libs\utils\ITransactionService::class, function(){
return new \services\utils\DoctrineTransactionService('ss');
return new \services\utils\DoctrineTransactionService(SilverstripeBaseModel::EntityManager);
});
App::singleton(\libs\utils\IEncryptionService::class, function(){

View File

@ -25,6 +25,7 @@
"guzzlehttp/guzzle": "^6.3",
"idct/sftp-client": "dev-master",
"laravel-doctrine/extensions": "1.0.*",
"laravel-doctrine/migrations": "^1.2",
"laravel-doctrine/orm": "1.4.*",
"laravel/framework": "5.6.*",
"laravel/tinker": "^1.0",

363
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"content-hash": "d9c8d933d23b34781d4010039da200c7",
"content-hash": "d03caf628ec9076f0ab50d8cdab0685d",
"packages": [
{
"name": "cocur/slugify",
@ -730,6 +730,80 @@
],
"time": "2014-09-09T13:34:57+00:00"
},
{
"name": "doctrine/migrations",
"version": "v1.8.1",
"source": {
"type": "git",
"url": "https://github.com/doctrine/migrations.git",
"reference": "215438c0eef3e5f9b7da7d09c6b90756071b43e6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/migrations/zipball/215438c0eef3e5f9b7da7d09c6b90756071b43e6",
"reference": "215438c0eef3e5f9b7da7d09c6b90756071b43e6",
"shasum": ""
},
"require": {
"doctrine/dbal": "~2.6",
"ocramius/proxy-manager": "^1.0|^2.0",
"php": "^7.1",
"symfony/console": "~3.3|^4.0"
},
"require-dev": {
"doctrine/coding-standard": "^1.0",
"doctrine/orm": "~2.5",
"jdorn/sql-formatter": "~1.1",
"mikey179/vfsstream": "^1.6",
"phpunit/phpunit": "~7.0",
"squizlabs/php_codesniffer": "^3.0",
"symfony/yaml": "~3.3|^4.0"
},
"suggest": {
"jdorn/sql-formatter": "Allows to generate formatted SQL with the diff command.",
"symfony/yaml": "Allows the use of yaml for migration configuration files."
},
"bin": [
"bin/doctrine-migrations"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "v1.8.x-dev"
}
},
"autoload": {
"psr-4": {
"Doctrine\\DBAL\\Migrations\\": "lib/Doctrine/DBAL/Migrations",
"Doctrine\\Migrations\\": "lib/Doctrine/Migrations"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Benjamin Eberlei",
"email": "kontakt@beberlei.de"
},
{
"name": "Jonathan Wage",
"email": "jonwage@gmail.com"
},
{
"name": "Michael Simonson",
"email": "contact@mikesimonson.com"
}
],
"description": "Database Schema migrations using Doctrine DBAL",
"homepage": "https://www.doctrine-project.org/projects/migrations.html",
"keywords": [
"database",
"migrations"
],
"time": "2018-06-06T21:00:30+00:00"
},
{
"name": "doctrine/orm",
"version": "v2.6.3",
@ -1956,6 +2030,66 @@
],
"time": "2018-03-01T17:07:28+00:00"
},
{
"name": "laravel-doctrine/migrations",
"version": "1.2.0",
"source": {
"type": "git",
"url": "https://github.com/laravel-doctrine/migrations.git",
"reference": "3fa4ebc8cd5e4ce4c029c4c2a8d7cb5bf8f5f645"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel-doctrine/migrations/zipball/3fa4ebc8cd5e4ce4c029c4c2a8d7cb5bf8f5f645",
"reference": "3fa4ebc8cd5e4ce4c029c4c2a8d7cb5bf8f5f645",
"shasum": ""
},
"require": {
"doctrine/migrations": "~1.8",
"illuminate/config": "~5.1",
"illuminate/console": "~5.1",
"illuminate/contracts": "~5.1",
"laravel-doctrine/orm": "~1.0",
"php": "^7.1"
},
"require-dev": {
"mockery/mockery": "~0.9",
"phpunit/phpunit": "~4.0"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"LaravelDoctrine\\Migrations\\MigrationsServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"LaravelDoctrine\\Migrations\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Patrick Brouwers",
"email": "patrick@maatwebsite.nl"
}
],
"description": "Doctrine Migrations for Laravel",
"keywords": [
"data mapper",
"database",
"doctrine",
"laravel",
"migrations",
"orm"
],
"time": "2018-09-03T11:46:53+00:00"
},
{
"name": "laravel-doctrine/orm",
"version": "v1.4.10",
@ -2577,6 +2711,126 @@
],
"time": "2019-02-16T20:54:15+00:00"
},
{
"name": "ocramius/package-versions",
"version": "1.4.0",
"source": {
"type": "git",
"url": "https://github.com/Ocramius/PackageVersions.git",
"reference": "a4d4b60d0e60da2487bd21a2c6ac089f85570dbb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Ocramius/PackageVersions/zipball/a4d4b60d0e60da2487bd21a2c6ac089f85570dbb",
"reference": "a4d4b60d0e60da2487bd21a2c6ac089f85570dbb",
"shasum": ""
},
"require": {
"composer-plugin-api": "^1.0.0",
"php": "^7.1.0"
},
"require-dev": {
"composer/composer": "^1.6.3",
"doctrine/coding-standard": "^5.0.1",
"ext-zip": "*",
"infection/infection": "^0.7.1",
"phpunit/phpunit": "^7.0.0"
},
"type": "composer-plugin",
"extra": {
"class": "PackageVersions\\Installer",
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"autoload": {
"psr-4": {
"PackageVersions\\": "src/PackageVersions"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Marco Pivetta",
"email": "ocramius@gmail.com"
}
],
"description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)",
"time": "2019-02-21T12:16:21+00:00"
},
{
"name": "ocramius/proxy-manager",
"version": "2.2.2",
"source": {
"type": "git",
"url": "https://github.com/Ocramius/ProxyManager.git",
"reference": "14b137b06b0f911944132df9d51e445a35920ab1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Ocramius/ProxyManager/zipball/14b137b06b0f911944132df9d51e445a35920ab1",
"reference": "14b137b06b0f911944132df9d51e445a35920ab1",
"shasum": ""
},
"require": {
"ocramius/package-versions": "^1.1.3",
"php": "^7.2.0",
"zendframework/zend-code": "^3.3.0"
},
"require-dev": {
"couscous/couscous": "^1.6.1",
"ext-phar": "*",
"humbug/humbug": "1.0.0-RC.0@RC",
"nikic/php-parser": "^3.1.1",
"padraic/phpunit-accelerator": "dev-master@DEV",
"phpbench/phpbench": "^0.12.2",
"phpstan/phpstan": "dev-master#856eb10a81c1d27c701a83f167dc870fd8f4236a as 0.9.999",
"phpstan/phpstan-phpunit": "dev-master#5629c0a1f4a9c417cb1077cf6693ad9753895761",
"phpunit/phpunit": "^6.4.3",
"squizlabs/php_codesniffer": "^2.9.1"
},
"suggest": {
"ocramius/generated-hydrator": "To have very fast object to array to object conversion for ghost objects",
"zendframework/zend-json": "To have the JsonRpc adapter (Remote Object feature)",
"zendframework/zend-soap": "To have the Soap adapter (Remote Object feature)",
"zendframework/zend-xmlrpc": "To have the XmlRpc adapter (Remote Object feature)"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0.x-dev"
}
},
"autoload": {
"psr-0": {
"ProxyManager\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Marco Pivetta",
"email": "ocramius@gmail.com",
"homepage": "http://ocramius.github.io/"
}
],
"description": "A library providing utilities to generate, instantiate and generally operate with Object Proxies",
"homepage": "https://github.com/Ocramius/ProxyManager",
"keywords": [
"aop",
"lazy loading",
"proxy",
"proxy pattern",
"service proxies"
],
"time": "2018-09-27T13:45:01+00:00"
},
{
"name": "paragonie/random_compat",
"version": "v9.99.99",
@ -4908,6 +5162,113 @@
"environment"
],
"time": "2019-01-29T11:11:52+00:00"
},
{
"name": "zendframework/zend-code",
"version": "3.3.1",
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-code.git",
"reference": "c21db169075c6ec4b342149f446e7b7b724f95eb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-code/zipball/c21db169075c6ec4b342149f446e7b7b724f95eb",
"reference": "c21db169075c6ec4b342149f446e7b7b724f95eb",
"shasum": ""
},
"require": {
"php": "^7.1",
"zendframework/zend-eventmanager": "^2.6 || ^3.0"
},
"require-dev": {
"doctrine/annotations": "~1.0",
"ext-phar": "*",
"phpunit/phpunit": "^6.2.3",
"zendframework/zend-coding-standard": "^1.0.0",
"zendframework/zend-stdlib": "^2.7 || ^3.0"
},
"suggest": {
"doctrine/annotations": "Doctrine\\Common\\Annotations >=1.0 for annotation features",
"zendframework/zend-stdlib": "Zend\\Stdlib component"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.3.x-dev",
"dev-develop": "3.4.x-dev"
}
},
"autoload": {
"psr-4": {
"Zend\\Code\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"description": "provides facilities to generate arbitrary code using an object oriented interface",
"homepage": "https://github.com/zendframework/zend-code",
"keywords": [
"code",
"zf2"
],
"time": "2018-08-13T20:36:59+00:00"
},
{
"name": "zendframework/zend-eventmanager",
"version": "3.2.1",
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-eventmanager.git",
"reference": "a5e2583a211f73604691586b8406ff7296a946dd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-eventmanager/zipball/a5e2583a211f73604691586b8406ff7296a946dd",
"reference": "a5e2583a211f73604691586b8406ff7296a946dd",
"shasum": ""
},
"require": {
"php": "^5.6 || ^7.0"
},
"require-dev": {
"athletic/athletic": "^0.1",
"container-interop/container-interop": "^1.1.0",
"phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2",
"zendframework/zend-coding-standard": "~1.0.0",
"zendframework/zend-stdlib": "^2.7.3 || ^3.0"
},
"suggest": {
"container-interop/container-interop": "^1.1.0, to use the lazy listeners feature",
"zendframework/zend-stdlib": "^2.7.3 || ^3.0, to use the FilterChain feature"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.2-dev",
"dev-develop": "3.3-dev"
}
},
"autoload": {
"psr-4": {
"Zend\\EventManager\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"description": "Trigger and listen to events within a PHP application",
"homepage": "https://github.com/zendframework/zend-eventmanager",
"keywords": [
"event",
"eventmanager",
"events",
"zf2"
],
"time": "2018-04-25T15:33:34+00:00"
}
],
"packages-dev": [

View File

@ -158,6 +158,7 @@ return [
libs\utils\CustomDoctrineServiceProvider::class,
LaravelDoctrine\Extensions\BeberleiExtensionsServiceProvider::class,
Sichikawa\LaravelSendgridDriver\SendgridTransportServiceProvider::class,
LaravelDoctrine\Migrations\MigrationsServiceProvider::class,
],
/*

View File

@ -26,7 +26,7 @@ return [
|
*/
'default' => env('DB_CONNECTION', 'openstackid_resources'),
'default' => env('DB_CONNECTION', 'config'),
/*
|--------------------------------------------------------------------------
@ -46,8 +46,8 @@ return [
'connections' => [
//primary DB
'openstackid_resources' => array(
// config DB
'config' => [
'driver' => 'mysql',
'host' => env('DB_HOST'),
'database' => env('DB_DATABASE'),
@ -57,9 +57,9 @@ return [
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
),
//secondary DB (SS OS)
'ss' => array(
],
// Model DB
'model' => [
'driver' => 'mysql',
'host' => env('SS_DB_HOST'),
'database' => env('SS_DATABASE'),
@ -69,7 +69,7 @@ return [
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
),
],
],

View File

@ -21,10 +21,10 @@ return [
|
*/
'managers' => [
'default' => [
'config' => [
'dev' => env('APP_DEBUG', true),
'meta' => env('DOCTRINE_METADATA', 'annotations'),
'connection' => env('DB_CONNECTION', 'openstackid_resources'),
'connection' => env('DB_CONNECTION', 'config'),
'namespaces' => [
'App'
],
@ -77,10 +77,10 @@ return [
'enum' => 'string'
]
],
'ss' => [
'model' => [
'dev' => env('APP_DEBUG'),
'meta' => env('DOCTRINE_METADATA', 'annotations'),
'connection' => 'ss',
'connection' => 'model',
'namespaces' => [
'App'
],

108
config/migrations.php Normal file
View File

@ -0,0 +1,108 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Entity Manager Migrations Configuration
|--------------------------------------------------------------------------
|
| Each entity manager can have a custom migration configuration. Provide
| the name of the entity manager as the key, then duplicate the settings.
| This will allow generating custom migrations per EM instance and not have
| collisions when executing them.
|
*/
'config' => [
/*
|--------------------------------------------------------------------------
| Migration Repository Table
|--------------------------------------------------------------------------
|
| This table keeps track of all the migrations that have already run for
| your application. Using this information, we can determine which of
| the migrations on disk haven't actually been run in the database.
|
*/
'table' => 'DoctrineMigration',
/*
|--------------------------------------------------------------------------
| Migration Directory
|--------------------------------------------------------------------------
|
| This directory is where all migrations will be stored for this entity
| manager. Use different directories for each entity manager.
|
*/
'directory' => sprintf("%s/%s", database_path('migrations') ,"config"),
/*
|--------------------------------------------------------------------------
| Migration Namespace
|--------------------------------------------------------------------------
|
| This namespace will be used on all migrations. To prevent collisions, add
| the entity manager name (connection name).
|
*/
'namespace' => 'Database\\Migrations\\Config',
/*
|--------------------------------------------------------------------------
| Migration Repository Table
|--------------------------------------------------------------------------
|
| Tables which are filtered by Regular Expression. You optionally
| exclude or limit to certain tables. The default will
| filter all tables.
|
*/
'schema' => [
'filter' => '/^(?!password_resets|failed_jobs).*$/'
]
],
'model' => [
/*
|--------------------------------------------------------------------------
| Migration Repository Table
|--------------------------------------------------------------------------
|
| This table keeps track of all the migrations that have already run for
| your application. Using this information, we can determine which of
| the migrations on disk haven't actually been run in the database.
|
*/
'table' => 'DoctrineMigration',
/*
|--------------------------------------------------------------------------
| Migration Directory
|--------------------------------------------------------------------------
|
| This directory is where all migrations will be stored for this entity
| manager. Use different directories for each entity manager.
|
*/
'directory' => sprintf("%s/%s", database_path('migrations') ,"model"),
/*
|--------------------------------------------------------------------------
| Migration Namespace
|--------------------------------------------------------------------------
|
| This namespace will be used on all migrations. To prevent collisions, add
| the entity manager name (connection name).
|
*/
'namespace' => 'Database\\Migrations\\Model',
/*
|--------------------------------------------------------------------------
| Migration Repository Table
|--------------------------------------------------------------------------
|
| Tables which are filtered by Regular Expression. You optionally
| exclude or limit to certain tables. The default will
| filter all tables.
|
*/
'schema' => [
'filter' => '/^(?!password_resets|failed_jobs).*$/'
]
],
];

View File

@ -15,7 +15,7 @@ return [
|
*/
'default' => env('QUEUE_DRIVER', 'sync'),
'default' => env('QUEUE_DRIVER', 'redis'),
/*
|--------------------------------------------------------------------------
@ -30,38 +30,11 @@ return [
'connections' => [
'sync' => [
'driver' => 'sync',
],
'database' => [
'driver' => 'database',
'table' => 'jobs',
'queue' => 'default',
'expire' => 60,
],
'beanstalkd' => [
'driver' => 'beanstalkd',
'host' => 'localhost',
'queue' => 'default',
'ttr' => 60,
],
'sqs' => [
'driver' => 'sqs',
'key' => 'your-public-key',
'secret' => 'your-secret-key',
'prefix' => 'https://sqs.us-east-1.amazonaws.com/your-account-id',
'queue' => 'your-queue-name',
'region' => 'us-east-1',
],
'redis' => [
'driver' => 'redis',
'driver' => 'redis',
'connection' => 'default',
'queue' => 'default',
'expire' => 60,
'queue' => 'default',
'expire' => 60,
],
],
@ -78,7 +51,7 @@ return [
*/
'failed' => [
'database' => env('DB_CONNECTION', 'mysql'),
'database' => env('DB_CONNECTION', 'config'),
'table' => 'failed_jobs',
],

View File

@ -1,38 +0,0 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateApisTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if (!Schema::hasTable('apis')) {
Schema::create('apis', function(Blueprint $table)
{
$table->bigIncrements('id');
$table->string('name',255)->unique();
$table->text('description')->nullable();
$table->boolean('active')->default(true);
$table->timestamps();
});
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('apis');
}
}

View File

@ -1,52 +0,0 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateApiScopesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if (!Schema::hasTable('api_scopes')) {
Schema::create('api_scopes', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name', 512);
$table->string('short_description', 512);
$table->text('description');
$table->boolean('active')->default(true);
$table->boolean('default')->default(false);
$table->boolean('system')->default(false);
$table->timestamps();
// FK
$table->bigInteger("api_id")->unsigned()->nullable();
$table->index('api_id');
$table->foreign('api_id')
->references('id')
->on('apis')
->onDelete('cascade')
->onUpdate('no action');
});
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('api_scopes', function ($table) {
$table->dropForeign('api_id');
});
Schema::drop('api_scopes');
}
}

View File

@ -1,85 +0,0 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateApiEndpointsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if (!Schema::hasTable('api_endpoints')) {
Schema::create('api_endpoints', function (Blueprint $table) {
$table->bigIncrements('id');
$table->boolean('active')->default(true);
$table->boolean('allow_cors')->default(true);
$table->boolean('allow_credentials')->default(true);
$table->text('description')->nullable();
$table->string('name', 255)->unique();
$table->timestamps();
$table->text("route");
$table->enum('http_method', array('GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE', 'CONNECT', 'OPTIONS', 'PATCH'));
$table->bigInteger("rate_limit")->unsigned()->nullable();
//FK
$table->bigInteger("api_id")->unsigned();
$table->index('api_id');
$table->foreign('api_id')
->references('id')
->on('apis')
->onDelete('cascade')
->onUpdate('no action');
});
}
if (!Schema::hasTable('api_endpoints')) {
Schema::create('endpoint_api_scopes', function ($table) {
$table->timestamps();
$table->bigInteger("api_endpoint_id")->unsigned();
$table->index('api_endpoint_id');
$table->foreign('api_endpoint_id')
->references('id')
->on('api_endpoints')
->onDelete('cascade')
->onUpdate('no action');;
// FK 2
$table->bigInteger("scope_id")->unsigned();
$table->index('scope_id');
$table->foreign('scope_id')
->references('id')
->on('api_scopes')
->onDelete('cascade')
->onUpdate('no action');
});
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('endpoint_api_scopes', function ($table) {
$table->dropForeign('api_endpoint_id');
});
Schema::table('endpoint_api_scopes', function ($table) {
$table->dropForeign('scope_id');
});
Schema::dropIfExists('endpoint_api_scopes');
Schema::table('api_endpoints', function ($table) {
$table->dropForeign('api_id');
});
Schema::drop('api_endpoints');
}
}

View File

@ -1,38 +0,0 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateTableIpRateLimitRoute extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if (!Schema::hasTable('ip_rate_limit_routes')) {
Schema::create('ip_rate_limit_routes', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('ip', 255);
$table->text("route");
$table->boolean('active')->default(true);
$table->enum('http_method', array('GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE', 'CONNECT', 'OPTIONS', 'PATCH'));
$table->bigInteger("rate_limit")->unsigned()->default(0);
$table->bigInteger("rate_limit_decay")->unsigned()->default(0);
$table->timestamps();
});
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('ip_rate_limit_routes');
}
}

View File

@ -1,53 +0,0 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class UpdateTableApiEndpoint extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if (Schema::hasColumn('api_endpoints', 'rate_limit')) {
Schema::table('api_endpoints', function (Blueprint $table) {
$table->dropColumn("rate_limit");
});
}
if (!Schema::hasColumn('api_endpoints', 'rate_limit')) {
Schema::table('api_endpoints', function (Blueprint $table) {
$table->bigInteger("rate_limit")->unsigned()->default(0);
});
}
if (!Schema::hasColumn('api_endpoints', 'rate_limit_decay')) {
Schema::table('api_endpoints', function (Blueprint $table) {
$table->bigInteger("rate_limit_decay")->unsigned()->default(0);
});
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('api_endpoints', function(Blueprint $table)
{
$table->dropColumn('rate_limit_decay');
$table->dropColumn("rate_limit");
});
Schema::table('api_endpoints', function(Blueprint $table)
{
$table->bigInteger("rate_limit")->unsigned()->nullable();
});
}
}

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,136 @@
<?php namespace Database\Migrations\Config;
/**
* Copyright 2019 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use Doctrine\Migrations\AbstractMigration;
use Doctrine\DBAL\Schema\Schema as Schema;
use LaravelDoctrine\Migrations\Schema\Table;
use LaravelDoctrine\Migrations\Schema\Builder;
/**
* Class Version20190422160409
* @package Database\Migrations\Config
*/
final class Version20190422160409 extends AbstractMigration
{
/**
* @param Schema $schema
*/
public function up(Schema $schema)
{
$builder = new Builder($schema);
if(!$builder->hasTable("apis")) {
$builder->create('apis', function (Table $table) {
$table->bigIncrements('id');
$table->string('name',255);
$table->unique('name');
$table->text('description')->setNotnull(false);
$table->boolean('active')->setDefault(true);
$table->timestamp('created_at')->setDefault('CURRENT_TIMESTAMP');
$table->timestamp('updated_at')->setDefault('CURRENT_TIMESTAMP');
});
}
if(!$builder->hasTable("api_scopes")) {
$builder->create('api_scopes', function (Table $table) {
$table->bigIncrements('id');
$table->string('name', 512);
$table->string('short_description', 512);
$table->text('description');
$table->boolean('active')->setNotnull(true);
$table->boolean('default')->setNotnull(false);
$table->boolean('system')->setNotnull(false);
$table->timestamp('created_at')->setDefault('CURRENT_TIMESTAMP');
$table->timestamp('updated_at')->setDefault('CURRENT_TIMESTAMP');
// FK
$table->bigInteger("api_id")->setUnsigned(true)->setNotnull(false);
$table->index('api_id');
$table->foreign('apis', 'api_id', 'id');
});
}
if(!$builder->hasTable("api_endpoints")) {
$builder->create('api_endpoints', function (Table $table) {
$table->bigIncrements('id');
$table->boolean('active')->setDefault(true);
$table->boolean('allow_cors')->setDefault(true);
$table->boolean('allow_credentials')->setDefault(true);
$table->text('description')->setNotnull(false);
$table->string('name', 255);
$table->unique('name');
$table->timestamp('created_at')->setDefault('CURRENT_TIMESTAMP');
$table->timestamp('updated_at')->setDefault('CURRENT_TIMESTAMP');
$table->text("route");
$table->getTable()->addColumn('http_method', 'array');
$table->bigInteger("rate_limit")->setUnsigned(true)->setDefault(0);
$table->bigInteger("rate_limit_decay")->setUnsigned(true)->setDefault(0);
//FK
$table->bigInteger("api_id")->setUnsigned(true);
$table->index('api_id');
$table->foreign('apis','api_id', 'id');
});
}
if(!$builder->hasTable("endpoint_api_scopes")) {
$builder->create('endpoint_api_scopes', function (Table $table) {
$table->bigIncrements('id');
$table->timestamp('created_at')->setDefault('CURRENT_TIMESTAMP');
$table->timestamp('updated_at')->setDefault('CURRENT_TIMESTAMP');
$table->bigInteger("api_endpoint_id")->setUnsigned(true);
$table->index('api_endpoint_id');
$table->foreign('api_endpoints','api_endpoint_id', 'id');
// FK 2
$table->bigInteger("scope_id")->setUnsigned(true);
$table->index('scope_id');
$table->foreign('api_scopes','scope_id', 'id');
});
}
if(!$builder->hasTable("ip_rate_limit_routes")) {
$builder->create('ip_rate_limit_routes', function (Table $table) {
$table->bigIncrements('id');
$table->string('ip',255);
$table->text("route");
$table->boolean('active')->setDefault(true);
//$table->getTable()->addColumn('http_method', enum ('GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE', 'CONNECT', 'OPTIONS', 'PATCH'));
$table->getTable()->addColumn('http_method', 'array');
$table->bigInteger("rate_limit")->setUnsigned(true)->setDefault(0);
$table->bigInteger("rate_limit_decay")->setUnsigned(true)->setDefault(0);
$table->timestamp('created_at')->setDefault('CURRENT_TIMESTAMP');
$table->timestamp('updated_at')->setDefault('CURRENT_TIMESTAMP');
});
}
}
/**
* @param Schema $schema
*/
public function down(Schema $schema)
{
$builder = new Builder($schema);
$builder->dropIfExists('endpoint_api_scopes');
$builder->dropIfExists('apis');
$builder->dropIfExists('api_scopes');
$builder->dropIfExists('api_endpoints');
$builder->dropIfExists('ip_rate_limit_routes');
}
}

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,58 @@
<?php namespace Database\Migrations\Model;
/**
* Copyright 2019 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use Doctrine\Migrations\AbstractMigration;
use Doctrine\DBAL\Schema\Schema as Schema;
use LaravelDoctrine\Migrations\Schema\Table;
use LaravelDoctrine\Migrations\Schema\Builder;
/**
* Class Version20190422151949
* @package Database\Migrations\Model
*/
final class Version20190422151949 extends AbstractMigration
{
/**
* @param Schema $schema
*/
public function up(Schema $schema)
{
$builder = new Builder($schema);
if(!$builder->hasTable("SpeakerEditPermissionRequest")) {
$builder->create('SpeakerEditPermissionRequest', function (Table $table) {
$table->increments('ID');
$table->integer("SpeakerID");
$table->integer("RequestedByID");
$table->boolean("Approved")->setDefault(false);
$table->dateTime("ApprovedDate")->setNotnull(false);
$table->dateTime("Created")->setNotnull(true);
$table->dateTime("LastEdited")->setNotnull(true);
$table->text("Hash");
$table->foreign("PresentationSpeaker", "SpeakerID", "ID");
$table->foreign("Member", "RequestedByID", "ID");
});
}
}
/**
* @param Schema $schema
*/
public function down(Schema $schema)
{
$builder = new Builder($schema);
if($builder->hasTable("SpeakerEditPermissionRequest"))
$builder->drop('SpeakerEditPermissionRequest');
}
}

View File

@ -0,0 +1,47 @@
<?php namespace Database\Migrations\Model;
/**
* Copyright 2019 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use Doctrine\Migrations\AbstractMigration;
use Doctrine\DBAL\Schema\Schema as Schema;
use LaravelDoctrine\Migrations\Schema\Table;
use LaravelDoctrine\Migrations\Schema\Builder;
/**
* Class Version20190506153014
* @package Database\Migrations\Model
*/
class Version20190506153014 extends AbstractMigration
{
/**
* @param Schema $schema
*/
public function up(Schema $schema)
{
$builder = new Builder($schema);
if(!$builder->hasTable("PresentationCreatorNotificationEmailRequest")) {
$this->addSql("CREATE TABLE PresentationCreatorNotificationEmailRequest (PresentationID INT DEFAULT NULL, ID INT NOT NULL, INDEX IDX_B302D49879B1711B (PresentationID), PRIMARY KEY(ID)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;");
$this->addSql("ALTER TABLE PresentationCreatorNotificationEmailRequest ADD CONSTRAINT FK_B302D49879B1711B FOREIGN KEY (PresentationID) REFERENCES Presentation (ID);");
$this->addSql("ALTER TABLE PresentationCreatorNotificationEmailRequest ADD CONSTRAINT FK_B302D49811D3633A FOREIGN KEY (ID) REFERENCES EmailCreationRequest (ID) ON DELETE CASCADE");
}
}
/**
* @param Schema $schema
*/
public function down(Schema $schema)
{
$builder = new Builder($schema);
$this->addSql("ALTER TABLE `PresentationCreatorNotificationEmailRequest` DROP FOREIGN KEY `FK_B302D49879B1711B`;");
$this->addSql("ALTER TABLE `PresentationCreatorNotificationEmailRequest` DROP FOREIGN KEY `FK_B302D49811D3633A`;");
$builder->dropIfExists('PresentationCreatorNotificationEmailRequest');
}
}

View File

@ -0,0 +1,49 @@
<?php namespace Database\Migrations\Model;
/**
* Copyright 2019 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use Doctrine\Migrations\AbstractMigration;
use Doctrine\DBAL\Schema\Schema as Schema;
use LaravelDoctrine\Migrations\Schema\Table;
use LaravelDoctrine\Migrations\Schema\Builder;
/**
* Class Version20190506153909
* @package Database\Migrations\Model
*/
class Version20190506153909 extends AbstractMigration
{
/**
* @param Schema $schema
*/
public function up(Schema $schema)
{
$builder = new Builder($schema);
if(!$builder->hasTable("PresentationSpeakerNotificationEmailRequest")){
$this->addSql("CREATE TABLE PresentationSpeakerNotificationEmailRequest (SpeakerID INT DEFAULT NULL, PresentationID INT DEFAULT NULL, ID INT NOT NULL, INDEX IDX_2BFDC212FEC5CBA6 (SpeakerID), INDEX IDX_2BFDC21279B1711B (PresentationID), PRIMARY KEY(ID)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB; ");
$this->addSql("ALTER TABLE PresentationSpeakerNotificationEmailRequest ADD CONSTRAINT FK_2BFDC212FEC5CBA6 FOREIGN KEY (SpeakerID) REFERENCES PresentationSpeaker (ID); ");
$this->addSql("ALTER TABLE PresentationSpeakerNotificationEmailRequest ADD CONSTRAINT FK_2BFDC21279B1711B FOREIGN KEY (PresentationID) REFERENCES Presentation (ID);");
$this->addSql("ALTER TABLE PresentationSpeakerNotificationEmailRequest ADD CONSTRAINT FK_2BFDC21211D3633A FOREIGN KEY (ID) REFERENCES EmailCreationRequest (ID) ON DELETE CASCADE;");
}
}
/**
* @param Schema $schema
*/
public function down(Schema $schema)
{
$builder = new Builder($schema);
$this->addSql("ALTER TABLE `PresentationSpeakerNotificationEmailRequest` DROP FOREIGN KEY `FK_2BFDC212FEC5CBA6`;");
$this->addSql("ALTER TABLE `PresentationSpeakerNotificationEmailRequest` DROP FOREIGN KEY `FK_2BFDC21279B1711B`;");
$this->addSql("ALTER TABLE `PresentationSpeakerNotificationEmailRequest` DROP FOREIGN KEY `FK_2BFDC21211D3633A`;");
$builder->dropIfExists('PresentationSpeakerNotificationEmailRequest');
}
}

View File

@ -355,6 +355,24 @@ class ApiEndpointsSeeder extends Seeder
sprintf(SummitScopes::ReadAllSummitData, $current_realm)
],
),
[
'name' => 'request-edit-speaker-permission',
'route' => '/api/v1/speakers/{speaker_id}/edit-permission',
'http_method' => 'PUT',
'scopes' => [
sprintf(SummitScopes::WriteSpeakersData, $current_realm),
sprintf(SummitScopes::WriteSummitData, $current_realm)
],
],
[
'name' => 'get-edit-speaker-permission',
'route' => '/api/v1/speakers/{speaker_id}/edit-permission',
'http_method' => 'GET',
'scopes' => [
sprintf(SummitScopes::ReadSummitData, $current_realm),
sprintf(SummitScopes::ReadAllSummitData, $current_realm)
],
],
[
'name' => 'get-my-speaker-presentations-by-role-by-selection-plan',
'route' => '/api/v1/speakers/me/presentations/{role}/selection-plans/{selection_plan_id}',

View File

@ -33,7 +33,7 @@ final class DefaultEventTypesSeeder extends Seeder
public function run()
{
$em = Registry::getManager('ss');
$em = Registry::getManager(\models\utils\SilverstripeBaseModel::EntityManager);
$repo = $em->getRepository(DefaultSummitEventType::class);
// presentation default types

View File

@ -3,7 +3,7 @@
## Prerequisites
* LAMP/LEMP environment
* PHP >= 5.4.0
* PHP >= 7.1
* Redis
* composer (https://getcomposer.org/)
@ -32,4 +32,28 @@ Laravel may require some permissions to be configured: folders within storage an
## create SS schema
php artisan doctrine:schema:create --sql --em=ss > ss.sql
php artisan doctrine:schema:create --sql --em=model > ss.sql
## Doctrine Migrations
# For Config Storage
## create new migration
php artisan doctrine:migrations:generate --connection=config --create=<table-name>
## check status
php artisan doctrine:migrations:status --connection=config
## run
php artisan doctrine:migrations:migrate --connection=config
# For Model Storage
## create new migrations
php artisan doctrine:migrations:generate --connection=model --create=<table-name>
## check status
php artisan doctrine:migrations:status --connection=model
## run
php artisan doctrine:migrations:migrate --connection=model

View File

@ -0,0 +1,7 @@
<p>
Dear {{ $request->getRequestedBy()->getFullName() }},
User {{ $request->getSpeaker()->getFullName() }} has approved your request to edit his/her Speaker Profile.
</p>
<p>
The OpenStack Team.
</p>

View File

@ -0,0 +1,7 @@
<p>
Dear {{ $request->getRequestedBy()->getFullName() }},
User {{ $request->getSpeaker()->getFullName() }} has rejected your request to edit his/her Speaker Profile.
</p>
<p>
The OpenStack Team.
</p>

View File

@ -0,0 +1,8 @@
<p>
Dear {{ $request->getSpeaker()->getFullName() }},
User {{ $request->getRequestedBy()->getFullName() }} has requested to be able to edit your Speaker Profile.
To Allow that please click on the following link <a href="{{$request->getConfirmationLink($request->getSpeaker()->getId(), $token)}}">Allow</a>.
</p>
<p>
The OpenStack Team.
</p>

View File

@ -0,0 +1,9 @@
@extends('layouts.master')
@section('title', 'Speaker Edit Permission - Approved')
@section('content')
<div class="content">
<p>
Your Speaker Edit Profile Permission Request was Approved!.
</p>
</div>
@stop

View File

@ -0,0 +1,9 @@
@extends('layouts.master')
@section('title', 'Speaker Edit Permission - Approved Error')
@section('content')
<div class="content">
<p>
There was a problem with your request.
</p>
</div>
@stop

View File

@ -0,0 +1,9 @@
@extends('layouts.master')
@section('title', 'Speaker Edit Permission - Approved Validation Error')
@section('content')
<div class="content">
<p>
There was a problem with your request. the permission request its already approved!.
</p>
</div>
@stop

View File

@ -0,0 +1,9 @@
@extends('layouts.master')
@section('title', 'Speaker Edit Permission - Rejected')
@section('content')
<div class="content">
<p>
Your Speaker Edit Profile Permission Request was Rejected!.
</p>
</div>
@stop

View File

@ -0,0 +1,9 @@
@extends('layouts.master')
@section('title', 'Speaker Edit Permission - Rejected Error')
@section('content')
<div class="content">
<p>
There was a problem with your request.
</p>
</div>
@stop

View File

@ -0,0 +1,9 @@
@extends('layouts.master')
@section('title', 'Speaker Edit Permission - Rejected Validation Error')
@section('content')
<div class="content">
<p>
There was a problem with your request. the permission request its already redeem !.
</p>
</div>
@stop

View File

@ -622,4 +622,64 @@ final class OAuth2SpeakersApiTest extends ProtectedApiTest
$presentations = json_decode($content);
$this->assertTrue(!is_null($presentations));
}
public function testRequestSpeakerEditPermission()
{
$headers = [
"HTTP_Authorization" => " Bearer " . $this->access_token,
"CONTENT_TYPE" => "application/json"
];
$params = [
'speaker_id' => 9
];
$response = $this->action
(
"PUT",
"OAuth2SummitSpeakersApiController@requestSpeakerEditPermission",
$params,
[],
[],
[],
$headers
);
$this->assertResponseStatus(201);
$content = $response->getContent();
$request = json_decode($content);
$this->assertTrue($request->id > 0);
return $request;
}
public function testGetRequestSpeakerEditPermission()
{
$headers = [
"HTTP_Authorization" => " Bearer " . $this->access_token,
"CONTENT_TYPE" => "application/json"
];
$params = [
'speaker_id' => 9
];
$response = $this->action
(
"GET",
"OAuth2SummitSpeakersApiController@getSpeakerEditPermission",
$params,
[],
[],
[],
$headers
);
$this->assertResponseStatus(200);
$content = $response->getContent();
$request = json_decode($content);
$this->assertTrue($request->id > 0);
return $request;
}
}

View File

@ -139,7 +139,6 @@ abstract class ProtectedApiTest extends \Tests\BrowserKitTestCase
{
$app = parent::createApplication();
App::singleton('App\Models\ResourceServer\IAccessTokenService', 'AccessTokenServiceStub');
return $app;
}