Added endpoints for Presentation Submission (CFP)

POST /api/v1/summits/{id}/presentations

scopes
* summits/write
* summits/write-event
* summits/write-presentation

payload

* 'title'                     => 'required|string|max:100'
* 'description'               => 'required|string'
* 'social_description'        => 'required|string|max:100'
* 'level'                     => 'required|in:Beginner,Intermediate,Advanced,N/A'
* 'attendees_expected_learnt' => 'required|string|max:1000'
* 'type_id'                   => 'required|integer'
* 'track_id'                  => 'required|integer'
* 'attending_media'           => 'required|boolean'
* 'links'                     => 'required|url_array'
* 'extra_questions'           => 'sometimes|entity_value_array'

PUT api/v1/summits/{id}/presentations/{presentation_id}

scopes
* summits/write
* summits/write-event
* summits/write-presentation

* 'title'                     => 'sometimes|string|max:100'
* 'description'               => 'sometimes|string'
* 'social_description'        => 'sometimes|string|max:100'
* 'level'                     => 'sometimes|in:Beginner,Intermediate,Advanced,N/A'
* 'attendees_expected_learnt' => 'sometimes|string|max:1000'
* 'type_id'                   => 'sometimes|integer'
* 'track_id'                  => 'sometimes|integer'
* 'attending_media'           => 'sometimes|boolean'
* 'links'                     => 'sometimes|url_array'
* 'extra_questions'           => 'sometimes|entity_value_array'

Change-Id: Iadc8bf072491a64ef48a1b475b36b92c2fe1d707
This commit is contained in:
Sebastian Marcet 2018-07-31 15:13:18 -03:00
parent f1c9e40a17
commit a1776966b3
35 changed files with 1999 additions and 53 deletions

View File

@ -12,8 +12,10 @@
* limitations under the License.
**/
use libs\utils\HTMLCleaner;
use models\exceptions\EntityNotFoundException;
use models\exceptions\ValidationException;
use models\main\IMemberRepository;
use models\oauth2\IResourceServerContext;
use models\summit\ISummitEventRepository;
use models\summit\ISummitRepository;
@ -48,18 +50,32 @@ final class OAuth2PresentationApiController extends OAuth2ProtectedController
*/
private $presentation_repository;
/**
* @var IMemberRepository
*/
private $member_repository;
/**
* OAuth2PresentationApiController constructor.
* @param IPresentationService $presentation_service
* @param ISummitRepository $summit_repository
* @param ISummitEventRepository $presentation_repository
* @param IMemberRepository $member_repository
* @param IResourceServerContext $resource_server_context
*/
public function __construct
(
IPresentationService $presentation_service,
ISummitRepository $summit_repository,
ISummitEventRepository $presentation_repository,
IMemberRepository $member_repository,
IResourceServerContext $resource_server_context
)
{
parent::__construct($resource_server_context);
$this->presentation_repository = $presentation_repository;
$this->presentation_service = $presentation_service;
$this->member_repository = $member_repository;
$this->summit_repository = $summit_repository;
}
@ -232,4 +248,160 @@ final class OAuth2PresentationApiController extends OAuth2ProtectedController
return $this->error500($ex);
}
}
/**
* @param $summit_id
* @return mixed
*/
public function submitPresentation($summit_id){
try {
$summit = SummitFinderStrategyFactory::build($this->summit_repository, $this->resource_server_context)->find($summit_id);
if (is_null($summit)) return $this->error404();
if(!Request::isJson()) return $this->error400();
$member_id = $this->resource_server_context->getCurrentUserExternalId();
if(is_null($member_id))
return $this->error403();
$member = $this->member_repository->getById($member_id);
if(is_null($member))
return $this->error403();
$data = Input::json();
$rules =
[
'title' => 'required|string|max:100',
'description' => 'required|string',
'social_description' => 'required|string|max:100',
'level' => 'required|in:Beginner,Intermediate,Advanced,N/A',
'attendees_expected_learnt' => 'required|string|max:1000',
'type_id' => 'required|integer',
'track_id' => 'required|integer',
'attending_media' => 'required|boolean',
'links' => 'required|url_array',
'extra_questions' => 'sometimes|entity_value_array',
];
$data = $data->all();
// Creates a Validator instance and validates the data.
$validation = Validator::make($data, $rules);
if ($validation->fails()) {
$ex = new ValidationException;
$ex->setMessages($validation->messages()->toArray());
throw $ex;
}
$fields = [
'title',
'description',
'social_summary',
'attendees_expected_learnt',
];
$presentation = $this->presentation_service->submitPresentation($summit, $member, HTMLCleaner::cleanData($data, $fields));
return $this->created(SerializerRegistry::getInstance()->getSerializer($presentation)->serialize());
}
catch (EntityNotFoundException $ex1)
{
Log::warning($ex1);
return $this->error404(['message' => $ex1->getMessage()]);
}
catch (ValidationException $ex2)
{
Log::warning($ex2);
return $this->error412($ex2->getMessages());
}
catch (Exception $ex)
{
Log::error($ex);
return $this->error500($ex);
}
}
/**
* @param $summit_id
* @param $presentation_id
* @return mixed
*/
public function updatePresentationSubmission($summit_id, $presentation_id){
try {
$summit = SummitFinderStrategyFactory::build($this->summit_repository, $this->resource_server_context)->find($summit_id);
if (is_null($summit)) return $this->error404();
if(!Request::isJson()) return $this->error400();
$member_id = $this->resource_server_context->getCurrentUserExternalId();
if(is_null($member_id))
return $this->error403();
$member = $this->member_repository->getById($member_id);
if(is_null($member))
return $this->error403();
$data = Input::json();
$rules =
[
'title' => 'sometimes|string|max:100',
'description' => 'sometimes|string',
'social_description' => 'sometimes|string|max:100',
'level' => 'sometimes|in:Beginner,Intermediate,Advanced,N/A',
'attendees_expected_learnt' => 'sometimes|string|max:1000',
'type_id' => 'sometimes|integer',
'track_id' => 'sometimes|integer',
'attending_media' => 'sometimes|boolean',
'links' => 'sometimes|url_array',
'extra_questions' => 'sometimes|entity_value_array',
];
$data = $data->all();
// Creates a Validator instance and validates the data.
$validation = Validator::make($data, $rules);
if ($validation->fails()) {
$ex = new ValidationException;
$ex->setMessages($validation->messages()->toArray());
throw $ex;
}
$fields = [
'title',
'description',
'social_summary',
'attendees_expected_learnt',
];
$presentation = $this->presentation_service->updatePresentationSubmission(
$summit,
$presentation_id,
$member,
HTMLCleaner::cleanData($data, $fields)
);
return $this->updated(SerializerRegistry::getInstance()->getSerializer($presentation)->serialize());
}
catch (EntityNotFoundException $ex1)
{
Log::warning($ex1);
return $this->error404();
}
catch (ValidationException $ex2)
{
Log::warning($ex2);
return $this->error412($ex2->getMessages());
}
catch (Exception $ex)
{
Log::error($ex);
return $this->error500($ex);
}
}
}

View File

@ -270,8 +270,14 @@ Route::group([
// presentations
Route::group(['prefix' => 'presentations'], function () {
// opened without role CFP - valid selection plan on CFP status
Route::post('', 'OAuth2PresentationApiController@submitPresentation');
Route::group(['prefix' => '{presentation_id}'], function () {
// opened without role CFP - valid selection plan on CFP status
Route::put('', 'OAuth2PresentationApiController@updatePresentationSubmission');
Route::group(['prefix' => 'videos'], function () {
Route::get('', 'OAuth2PresentationApiController@getPresentationVideos');

View File

@ -38,6 +38,7 @@ use App\ModelSerializers\PushNotificationMessageSerializer;
use App\ModelSerializers\Software\OpenStackComponentSerializer;
use App\ModelSerializers\Software\OpenStackReleaseSerializer;
use App\ModelSerializers\Summit\AdminSummitSerializer;
use App\ModelSerializers\Summit\Presentation\TrackQuestions\TrackAnswerSerializer;
use App\ModelSerializers\Summit\RSVP\Templates\RSVPDropDownQuestionTemplateSerializer;
use App\ModelSerializers\Summit\RSVP\Templates\RSVPLiteralContentQuestionTemplateSerializer;
use App\ModelSerializers\Summit\RSVP\Templates\RSVPMultiValueQuestionTemplateSerializer;
@ -107,6 +108,7 @@ final class SerializerRegistry
$this->registry['PresentationCategoryGroup'] = PresentationCategoryGroupSerializer::class;
$this->registry['PrivatePresentationCategoryGroup'] = PrivatePresentationCategoryGroupSerializer::class;
$this->registry['Tag'] = TagSerializer::class;
$this->registry['TrackAnswer'] = TrackAnswerSerializer::class;
$this->registry['SummitEvent'] = SummitEventSerializer::class;
$this->registry['SummitGroupEvent'] = SummitGroupEventSerializer::class;
$this->registry['SummitEventMetricsSnapshot'] = SummitEventMetricsSnapshotSerializer::class;

View File

@ -30,7 +30,6 @@ class PresentationSerializer extends SummitEventSerializer
];
protected static $allowed_fields = [
'track_id',
'moderator_speaker_id',
'level',
@ -47,6 +46,7 @@ class PresentationSerializer extends SummitEventSerializer
'videos',
'speakers',
'links',
'extra_questions',
];
/**
@ -103,6 +103,15 @@ class PresentationSerializer extends SummitEventSerializer
$values['videos'] = $videos;
}
if(in_array('extra_questions', $relations))
{
$answers = [];
foreach ($presentation->getAnswers() as $answer) {
$answers[]= SerializerRegistry::getInstance()->getSerializer($answer)->serialize();
}
$values['extra_questions'] = $answers;
}
if (!empty($expand)) {
foreach (explode(',', $expand) as $relation) {
switch (trim($relation)) {

View File

@ -0,0 +1,26 @@
<?php namespace App\ModelSerializers\Summit\Presentation\TrackQuestions;
/**
* Copyright 2018 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 ModelSerializers\SilverStripeSerializer;
/**
* Class TrackAnswerSerializer
* @package App\ModelSerializers\Summit\Presentation\TrackQuestions
*/
final class TrackAnswerSerializer extends SilverStripeSerializer
{
protected static $array_mappings = [
'Value' => 'value:json_string',
'QuestionName' => 'question_name:json_string',
'QuestionId' => 'question_id:json_int',
];
}

View File

@ -199,4 +199,8 @@ abstract class PresentationMaterial extends SilverstripeBaseModel
public function inserted($args){
Event::fire(new PresentationMaterialCreated($this, $args));
}
public function clearPresentation(){
$this->presentation = null;
}
}

View File

@ -12,11 +12,13 @@
* limitations under the License.
**/
use Doctrine\ORM\Mapping AS ORM;
use App\Models\Foundation\Summit\Events\Presentations\TrackQuestions\TrackAnswer;
use App\Models\Foundation\Summit\SelectionPlan;
use Doctrine\Common\Collections\Criteria;
use Doctrine\Common\Collections\ArrayCollection;
use App\Models\Foundation\Summit\Events\Presentations\TrackQuestions\TrackQuestionTemplate;
use models\exceptions\ValidationException;
use models\main\Member;
/**
* Class Presentation
* @ORM\Entity
@ -116,8 +118,16 @@ class Presentation extends SummitEvent
*/
private $moderator;
/**
* @ORM\ManyToOne(targetEntity="App\Models\Foundation\Summit\SelectionPlan")
* @ORM\ManyToOne(targetEntity="models\main\Member")
* @ORM\JoinColumn(name="CreatorID", referencedColumnName="ID", onDelete="SET NULL")
* @var Member
*/
private $creator;
/**
* @ORM\ManyToOne(targetEntity="App\Models\Foundation\Summit\SelectionPlan", inversedBy="presentations")
* @ORM\JoinColumn(name="SelectionPlanID", referencedColumnName="ID")
* @var SelectionPlan
*/
@ -149,6 +159,12 @@ class Presentation extends SummitEvent
*/
private $selected_presentations;
/**
* @ORM\OneToMany(targetEntity="App\Models\Foundation\Summit\Events\Presentations\TrackQuestions\TrackAnswer", mappedBy="presentation", cascade={"persist"}, orphanRemoval=true, fetch="EXTRA_LAZY")
* @var TrackAnswer[]
*/
private $answers;
/**
* @return bool
*/
@ -179,6 +195,7 @@ class Presentation extends SummitEvent
$this->materials = new ArrayCollection();
$this->speakers = new ArrayCollection();
$this->answers = new ArrayCollection();
$this->to_record = false;
$this->attending_media = false;
}
@ -251,6 +268,7 @@ class Presentation extends SummitEvent
* @param PresentationSpeaker $speaker
*/
public function addSpeaker(PresentationSpeaker $speaker){
if($this->speakers->contains($speaker)) return;
$this->speakers->add($speaker);
$speaker->addPresentation($this);
}
@ -556,4 +574,107 @@ class Presentation extends SummitEvent
public function clearSelectionPlan(){
$this->selection_plan = null;
}
/**
* @return Member
*/
public function getCreator()
{
return $this->creator;
}
/**
* @param Member $creator
*/
public function setCreator(Member $creator)
{
$this->creator = $creator;
}
/**
* @return TrackAnswer[]
*/
public function getAnswers()
{
return $this->answers;
}
/**
* @param TrackAnswer[] $answers
*/
public function setAnswers($answers)
{
$this->answers = $answers;
}
/**
* @param TrackAnswer $answer
*/
public function addAnswer(TrackAnswer $answer){
$this->answers->add($answer);
$answer->setPresentation($this);
}
/**
* @param string $link
* @return PresentationLink|null
*/
public function findLink($link){
$links = $this->getLinks();
foreach ($links as $entity){
if($entity->getLink() == $link)
return $entity;
}
return null;
}
public function clearLinks(){
$links = $this->getLinks();
foreach ($links as $link){
$this->materials->removeElement($link);
$link->clearPresentation();
}
}
/**
* @param TrackQuestionTemplate $question
* @return TrackAnswer|null
*/
public function getTrackExtraQuestionAnswer(TrackQuestionTemplate $question){
$criteria = Criteria::create();
$criteria->where(Criteria::expr()->eq('question', $question));
$res = $this->answers->matching($criteria)->first();
return $res === false ? null : $res;
}
/**
* @return int
*/
public function getCreatorId()
{
try{
if(is_null($this->creator)) return 0;
return $this->creator->getId();
}
catch(\Exception $ex){
return 0;
}
}
/**
* @return int
*/
public function getSelectionPlanId()
{
try{
if(is_null($this->selection_plan)) return 0;
return $this->selection_plan->getId();
}
catch(\Exception $ex){
return 0;
}
}
}

View File

@ -11,10 +11,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use Doctrine\ORM\Mapping AS ORM;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use Doctrine\ORM\Query\ResultSetMappingBuilder;
use App\Models\Foundation\Summit\Events\Presentations\TrackQuestions\TrackQuestionTemplate;
use Doctrine\Common\Collections\Criteria;
use models\main\Tag;
use models\utils\SilverstripeBaseModel;
use Doctrine\Common\Collections\ArrayCollection;
@ -153,12 +154,49 @@ class PresentationCategory extends SilverstripeBaseModel
*/
protected $allowed_tags;
/**
* @ORM\ManyToMany(targetEntity="App\Models\Foundation\Summit\Events\Presentations\TrackQuestions\TrackQuestionTemplate", cascade={"persist"}, inversedBy="tracks")
* @ORM\JoinTable(name="PresentationCategory_ExtraQuestions",
* joinColumns={@ORM\JoinColumn(name="PresentationCategoryID", referencedColumnName="ID")},
* inverseJoinColumns={@ORM\JoinColumn(name="TrackQuestionTemplateID", referencedColumnName="ID")}
* )
* @var TrackQuestionTemplate[]
*/
protected $extra_questions;
/**
* @param int $id
* @return TrackQuestionTemplate|null
*/
public function getExtraQuestionById($id){
$res = $this->extra_questions->filter(function(TrackQuestionTemplate $question) use($id){
return $question->getIdentifier() == $id;
});
$res = $res->first();
return $res === false ? null : $res;
}
/**
* @param string $name
* @return TrackQuestionTemplate|null
*/
public function getExtraQuestionByName($name){
$res = $this->extra_questions->filter(function(TrackQuestionTemplate $question) use($name){
return $question->getName() == trim($name);
});
$res = $res->first();
return $res === false ? null : $res;
}
public function __construct()
{
parent::__construct();
$this->groups = new ArrayCollection;
$this->allowed_tags = new ArrayCollection;
$this->extra_questions = new ArrayCollection;
$this->session_count = 0;
$this->alternate_count = 0;
$this->lightning_alternate_count = 0;

View File

@ -138,10 +138,10 @@ class PresentationCategoryGroup extends SilverstripeBaseModel
/**
* owning side
* @ORM\ManyToMany(targetEntity="models\summit\PresentationCategory", inversedBy="groups")
* @ORM\ManyToMany(targetEntity="models\summit\PresentationCategory")
* @ORM\JoinTable(name="PresentationCategoryGroup_Categories",
* joinColumns={@ORM\JoinColumn(name="PresentationCategoryGroupID", referencedColumnName="ID")},
* inverseJoinColumns={@ORM\JoinColumn(name="PresentationCategoryID", referencedColumnName="ID")}
* joinColumns={@ORM\JoinColumn(name="PresentationCategoryGroupID", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="PresentationCategoryID", referencedColumnName="id")}
* )
* @var PresentationCategory[]
*/

View File

@ -0,0 +1,112 @@
<?php namespace App\Models\Foundation\Summit\Events\Presentations\TrackQuestions;
/**
* Copyright 2018 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\utils\SilverstripeBaseModel;
/**
* @ORM\Entity
* @ORM\Table(name="TrackAnswer")
* Class TrackAnswer
* @package App\Models\Foundation\Summit\Events\Presentations\TrackQuestions
*/
class TrackAnswer extends SilverstripeBaseModel
{
/**
* @ORM\Column(name="Value", type="string")
* @var string
*/
private $value;
/**
* @ORM\ManyToOne(targetEntity="TrackQuestionTemplate", fetch="EXTRA_LAZY", inversedBy="answers")
* @ORM\JoinColumn(name="QuestionID", referencedColumnName="ID", onDelete="SET NULL")
* @var TrackQuestionTemplate
*/
private $question;
/**
* @ORM\ManyToOne(targetEntity="models\summit\Presentation", fetch="EXTRA_LAZY", inversedBy="answers")
* @ORM\JoinColumn(name="PresentationID", referencedColumnName="ID", onDelete="SET NULL")
* @var Presentation
*/
private $presentation;
/**
* @return string
*/
public function getValue()
{
return $this->value;
}
/**
* @param string $value
*/
public function setValue($value)
{
$this->value = $value;
}
/**
* @return TrackQuestionTemplate
*/
public function getQuestion()
{
return $this->question;
}
/**
* @param TrackQuestionTemplate $question
*/
public function setQuestion($question)
{
$this->question = $question;
}
/**
* @return Presentation
*/
public function getPresentation()
{
return $this->presentation;
}
/**
* @param Presentation $presentation
*/
public function setPresentation($presentation)
{
$this->presentation = $presentation;
}
/**
* @return string
*/
public function getQuestionName(){
return $this->question->getName();
}
/**
* @return int
*/
public function getQuestionId(){
return $this->question->getId();
}
public function __construct()
{
parent::__construct();
}
}

View File

@ -0,0 +1,43 @@
<?php
namespace App\Models\Foundation\Summit\Events\Presentations\TrackQuestions;
/**
* Copyright 2018 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;
/**
* @ORM\Entity
* @ORM\Table(name="TrackCheckBoxListQuestionTemplate")
* Class TrackCheckBoxListQuestionTemplate
* @package App\Models\Foundation\Summit\Events\Presentations\TrackQuestions
*/
class TrackCheckBoxListQuestionTemplate extends TrackMultiValueQuestionTemplate
{
const ClassName = 'TrackCheckBoxListQuestionTemplate';
/**
* @return string
*/
public function getClassName(){
return self::ClassName;
}
public static $metadata = [
'class_name' => self::ClassName,
];
/**
* @return array
*/
public static function getMetadata(){
return array_merge(TrackMultiValueQuestionTemplate::getMetadata(), self::$metadata);
}
}

View File

@ -0,0 +1,42 @@
<?php namespace App\Models\Foundation\Summit\Events\Presentations\TrackQuestions;
/**
* Copyright 2018 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;
/**
* @ORM\Entity
* @ORM\Table(name="TrackCheckBoxQuestionTemplate")
* Class TrackCheckBoxQuestionTemplate
* @package App\Models\Foundation\Summit\Events\Presentations\TrackQuestions
*/
class TrackCheckBoxQuestionTemplate extends TrackSingleValueTemplateQuestion
{
const ClassName = 'TrackCheckBoxQuestionTemplate';
/**
* @return string
*/
public function getClassName(){
return self::ClassName;
}
public static $metadata = [
'class_name' => self::ClassName,
];
/**
* @return array
*/
public static function getMetadata(){
return array_merge(TrackSingleValueTemplateQuestion::getMetadata(), self::$metadata);
}
}

View File

@ -0,0 +1,97 @@
<?php namespace App\Models\Foundation\Summit\Events\Presentations\TrackQuestions;
/**
* Copyright 2018 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;
/**
* @ORM\Entity
* @ORM\Table(name="TrackDropDownQuestionTemplate")
* Class TrackDropDownQuestionTemplate
* @package App\Models\Foundation\Summit\Events\Presentations\TrackQuestions
*/
class TrackDropDownQuestionTemplate extends TrackMultiValueQuestionTemplate
{
/**
* @ORM\Column(name="IsMultiSelect", type="boolean")
* @var bool
*/
private $is_multi_select;
/**
* @ORM\Column(name="IsCountrySelector", type="boolean")
* @var bool
*/
private $is_country_selector;
public function __construct()
{
parent::__construct();
$this->is_multi_select = false;
$this->is_country_selector = false;
}
/**
* @return bool
*/
public function isMultiSelect()
{
return $this->is_multi_select;
}
/**
* @param bool $is_multi_select
*/
public function setIsMultiSelect($is_multi_select)
{
$this->is_multi_select = $is_multi_select;
}
/**
* @return bool
*/
public function isCountrySelector()
{
return $this->is_country_selector;
}
/**
* @param bool $is_country_selector
*/
public function setIsCountrySelector($is_country_selector)
{
$this->is_country_selector = $is_country_selector;
}
const ClassName = 'TrackDropDownQuestionTemplate';
/**
* @return string
*/
public function getClassName(){
return self::ClassName;
}
public static $metadata = [
'class_name' => self::ClassName,
'is_multi_select' => 'boolean',
'is_country_selector' => 'boolean',
];
/**
* @return array
*/
public static function getMetadata(){
return array_merge(TrackMultiValueQuestionTemplate::getMetadata(), self::$metadata);
}
}

View File

@ -0,0 +1,66 @@
<?php namespace App\Models\Foundation\Summit\Events\Presentations\TrackQuestions;
/**
* Copyright 2018 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;
/**
* @ORM\Entity
* @ORM\Table(name="TrackLiteralContentQuestionTemplate")
* Class TrackLiteralContentQuestionTemplate
* @package App\Models\Foundation\Summit\Events\Presentations\TrackQuestions
*/
class TrackLiteralContentQuestionTemplate extends TrackQuestionTemplate
{
/**
* @ORM\Column(name="Content", type="string")
* @var string
*/
private $content;
/**
* @return string
*/
public function getContent()
{
return $this->content;
}
/**
* @param string $content
*/
public function setContent($content)
{
$this->content = $content;
}
const ClassName = 'TrackLiteralContentQuestionTemplate';
/**
* @return string
*/
public function getClassName(){
return self::ClassName;
}
public static $metadata = [
'class_name' => self::ClassName,
'content' => 'string',
];
/**
* @return array
*/
public static function getMetadata(){
return array_merge(TrackQuestionTemplate::getMetadata(), self::$metadata);
}
}

View File

@ -0,0 +1,121 @@
<?php namespace App\Models\Foundation\Summit\Events\Presentations\TrackQuestions;
/**
* Copyright 2018 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\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping AS ORM;
/**
* @ORM\Entity
* @ORM\Table(name="TrackMultiValueQuestionTemplate")
* Class TrackMultiValueQuestionTemplate
* @package App\Models\Foundation\Summit\Events\Presentations\TrackQuestions
*/
class TrackMultiValueQuestionTemplate extends TrackQuestionTemplate
{
/**
* @ORM\Column(name="EmptyString", type="string")
* @var string
*/
protected $empty_string;
/**
* @ORM\OneToMany(targetEntity="TrackQuestionValueTemplate", mappedBy="owner", cascade={"persist"})
* @var TrackQuestionValueTemplate[]
*/
protected $values;
/**
* @ORM\ManyToOne(targetEntity="TrackQuestionValueTemplate", fetch="EXTRA_LAZY")
* @ORM\JoinColumn(name="DefaultValueID", referencedColumnName="ID", onDelete="SET NULL")
* @var TrackQuestionValueTemplate
*/
protected $default_value;
public function __construct()
{
parent::__construct();
$this->values = new ArrayCollection;
}
/**
* @return string
*/
public function getEmptyString()
{
return $this->empty_string;
}
/**
* @param string $empty_string
*/
public function setEmptyString($empty_string)
{
$this->empty_string = $empty_string;
}
/**
* @return TrackQuestionValueTemplate[]
*/
public function getValues()
{
return $this->values;
}
/**
* @param TrackQuestionValueTemplate[] $values
*/
public function setValues($values)
{
$this->values = $values;
}
/**
* @return TrackQuestionValueTemplate
*/
public function getDefaultValue()
{
return $this->default_value;
}
/**
* @param TrackQuestionValueTemplate $default_value
*/
public function setDefaultValue($default_value)
{
$this->default_value = $default_value;
}
/**
* @return string
*/
public function getClassName(){
return self::ClassName;
}
const ClassName = 'TrackMultiValueQuestionTemplate';
public static $metadata = [
'class_name' => self::ClassName,
'empty_string' => 'string',
'default_value_id' => 'int',
'values' => 'array'
];
/**
* @return array
*/
public static function getMetadata(){
return array_merge(TrackQuestionTemplate::getMetadata(), self::$metadata);
}
}

View File

@ -0,0 +1,236 @@
<?php namespace App\Models\Foundation\Summit\Events\Presentations\TrackQuestions;
/**
* Copyright 2018 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 Doctrine\Common\Collections\ArrayCollection;
use models\summit\PresentationCategory;
use models\utils\SilverstripeBaseModel;
/**
* @ORM\Entity
* @ORM\Table(name="TrackQuestionTemplate")
* @ORM\InheritanceType("JOINED")
* @ORM\DiscriminatorColumn(name="ClassName", type="string")
* @ORM\DiscriminatorMap({
* "TrackQuestionTemplate" = "TrackQuestionTemplate",
* "TrackSingleValueTemplateQuestion" = "TrackSingleValueTemplateQuestion",
* "TrackMultiValueQuestionTemplate" = "TrackMultiValueQuestionTemplate",
* "TrackLiteralContentQuestionTemplate" = "TrackLiteralContentQuestionTemplate",
* "TrackRadioButtonListQuestionTemplate" = "TrackRadioButtonListQuestionTemplate",
* "TrackCheckBoxListQuestionTemplate" = "TrackCheckBoxListQuestionTemplate",
* "TrackDropDownQuestionTemplate" = "TrackDropDownQuestionTemplate",
* "TrackTextBoxQuestionTemplate" = "TrackTextBoxQuestionTemplate",
* "TrackCheckBoxQuestionTemplate" = "TrackCheckBoxQuestionTemplate"
* })
* Class TrackQuestionTemplate
* @package App\Models\Foundation\Summit\Events\Presentations
*/
class TrackQuestionTemplate extends SilverstripeBaseModel
{
/**
* @ORM\Column(name="Name", type="string")
* @var string
*/
protected $name;
/**
* @ORM\Column(name="label", type="string")
* @var string
*/
protected $label;
/**
* @ORM\Column(name="Mandatory", type="boolean")
* @var bool
*/
protected $is_mandatory;
/**
* @ORM\Column(name="ReadOnly", type="boolean")
* @var bool
*/
protected $is_read_only;
/**
* @ORM\Column(name="AfterQuestion", type="string")
* @var string
*/
protected $after_question;
/**
* @ORM\ManyToMany(targetEntity="models\summit\PresentationCategory", mappedBy="extra_questions")
* @var PresentationCategory[]
*/
protected $tracks;
/**
* @ORM\OneToMany(targetEntity="TrackAnswer", mappedBy="question", cascade={"persist"})
* @var TrackAnswer[]
*/
protected $answers;
/**
* @return string
*/
public function getClassName(){
return self::ClassName;
}
const ClassName = 'TrackQuestionTemplate';
public function __construct()
{
parent::__construct();
$this->is_mandatory = false;
$this->is_read_only = false;
$this->tracks = new ArrayCollection();
$this->answers = new ArrayCollection();
}
public static $metadata = [
'name' => 'string',
'label' => 'string',
'is_mandatory' => 'boolean',
'is_read_only' => 'boolean',
'after_question' => 'string',
'tracks' => 'array',
'answers' => 'array',
];
/**
* @return array
*/
public static function getMetadata(){
return self::$metadata;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @param string $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* @return string
*/
public function getLabel()
{
return $this->label;
}
/**
* @param string $label
*/
public function setLabel($label)
{
$this->label = $label;
}
/**
* @return bool
*/
public function isMandatory()
{
return $this->is_mandatory;
}
/**
* @param bool $is_mandatory
*/
public function setIsMandatory($is_mandatory)
{
$this->is_mandatory = $is_mandatory;
}
/**
* @return bool
*/
public function isReadOnly()
{
return $this->is_read_only;
}
/**
* @param bool $is_read_only
*/
public function setIsReadOnly($is_read_only)
{
$this->is_read_only = $is_read_only;
}
/**
* @return string
*/
public function getAfterQuestion()
{
return $this->after_question;
}
/**
* @param string $after_question
*/
public function setAfterQuestion($after_question)
{
$this->after_question = $after_question;
}
/**
* @return PresentationCategory[]
*/
public function getTracks()
{
return $this->tracks;
}
/**
* @param PresentationCategory[] $tracks
*/
public function setTracks($tracks)
{
$this->tracks = $tracks;
}
/**
* @return TrackAnswer[]
*/
public function getAnswers()
{
return $this->answers;
}
/**
* @param TrackAnswer[] $answers
*/
public function setAnswers($answers)
{
$this->answers = $answers;
}
/**
* @param TrackAnswer $answer
*/
public function addAnswer(TrackAnswer $answer){
$this->answers->add($answer);
$answer->setQuestion($this);
}
}

View File

@ -0,0 +1,113 @@
<?php namespace App\Models\Foundation\Summit\Events\Presentations\TrackQuestions;
/**
* Copyright 2018 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\utils\SilverstripeBaseModel;
/**
* @ORM\Entity
* @ORM\Table(name="TrackQuestionValueTemplate")
* Class TrackQuestionValueTemplate
* @package App\Models\Foundation\Summit\Events\Presentations\TrackQuestions
*/
class TrackQuestionValueTemplate extends SilverstripeBaseModel
{
/**
* @ORM\Column(name="Value", type="string")
* @var string
*/
private $value;
/**
* @ORM\Column(name="Label", type="string")
* @var string
*/
private $label;
/**
* @ORM\Column(name="Order", type="integer")
* @var int
*/
private $order;
/**
* @ORM\ManyToOne(targetEntity="TrackMultiValueQuestionTemplate", fetch="EXTRA_LAZY", inversedBy="values")
* @ORM\JoinColumn(name="OwnerID", referencedColumnName="ID", onDelete="SET NULL")
* @var TrackMultiValueQuestionTemplate
*/
private $owner;
/**
* @return string
*/
public function getValue()
{
return $this->value;
}
/**
* @param string $value
*/
public function setValue($value)
{
$this->value = $value;
}
/**
* @return string
*/
public function getLabel()
{
return $this->label;
}
/**
* @param string $label
*/
public function setLabel($label)
{
$this->label = $label;
}
/**
* @return int
*/
public function getOrder()
{
return $this->order;
}
/**
* @param int $order
*/
public function setOrder($order)
{
$this->order = $order;
}
/**
* @return TrackMultiValueQuestionTemplate
*/
public function getOwner()
{
return $this->owner;
}
/**
* @param TrackMultiValueQuestionTemplate $owner
*/
public function setOwner($owner)
{
$this->owner = $owner;
}
}

View File

@ -0,0 +1,43 @@
<?php namespace App\Models\Foundation\Summit\Events\Presentations\TrackQuestions;
/**
* Copyright 2018 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;
/**
* @ORM\Entity
* @ORM\Table(name="TrackRadioButtonListQuestionTemplate")
* Class TrackRadioButtonListQuestionTemplate
* @package App\Models\Foundation\Summit\Events\Presentations\TrackQuestions
*/
final class TrackRadioButtonListQuestionTemplate extends TrackMultiValueQuestionTemplate
{
const ClassName = 'TrackRadioButtonListQuestionTemplate';
/**
* @return string
*/
public function getClassName(){
return self::ClassName;
}
public static $metadata = [
'class_name' => self::ClassName,
];
/**
* @return array
*/
public static function getMetadata(){
return array_merge(TrackMultiValueQuestionTemplate::getMetadata(), self::$metadata);
}
}

View File

@ -0,0 +1,66 @@
<?php namespace App\Models\Foundation\Summit\Events\Presentations\TrackQuestions;
/**
* Copyright 2018 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;
/**
* @ORM\Entity
* @ORM\Table(name="TrackSingleValueTemplateQuestion")
* Class TrackSingleValueTemplateQuestion
* @package App\Models\Foundation\Summit\Events\Presentations\TrackQuestions
*/
class TrackSingleValueTemplateQuestion extends TrackQuestionTemplate
{
/**
* @ORM\Column(name="InitialValue", type="string")
* @var string
*/
protected $initial_value;
const ClassName = 'TrackSingleValueTemplateQuestion';
/**
* @return string
*/
public function getClassName(){
return self::ClassName;
}
public static $metadata = [
'class_name' => self::ClassName,
'initial_value' => 'string',
];
/**
* @return array
*/
public static function getMetadata(){
return array_merge(TrackQuestionTemplate::getMetadata(), self::$metadata);
}
/**
* @return string
*/
public function getInitialValue()
{
return $this->initial_value;
}
/**
* @param string $initial_value
*/
public function setInitialValue($initial_value)
{
$this->initial_value = $initial_value;
}
}

View File

@ -0,0 +1,42 @@
<?php namespace App\Models\Foundation\Summit\Events\Presentations\TrackQuestions;
/**
* Copyright 2018 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;
/**
* @ORM\Entity
* @ORM\Table(name="TrackTextBoxQuestionTemplate")
* Class TrackTextBoxQuestionTemplate
* @package App\Models\Foundation\Summit\Events\Presentations\TrackQuestions
*/
final class TrackTextBoxQuestionTemplate extends TrackSingleValueTemplateQuestion
{
const ClassName = 'TrackTextBoxQuestionTemplate';
/**
* @return string
*/
public function getClassName(){
return self::ClassName;
}
public static $metadata = [
'class_name' => self::ClassName,
];
/**
* @return array
*/
public static function getMetadata(){
return array_merge(TrackSingleValueTemplateQuestion::getMetadata(), self::$metadata);
}
}

View File

@ -12,7 +12,6 @@
* limitations under the License.
**/
use App\Models\Foundation\Summit\Events\RSVP\RSVPTemplate;
use Doctrine\ORM\Mapping AS ORM;
use App\Events\SummitEventCreated;
use App\Events\SummitEventDeleted;
use App\Events\SummitEventUpdated;
@ -28,6 +27,7 @@ use DateTime;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Config;
use Cocur\Slugify\Slugify;
use Doctrine\ORM\Mapping AS ORM;
/**
* @ORM\Entity(repositoryClass="App\Repositories\Summit\DoctrineSummitEventRepository")
* @ORM\Table(name="SummitEvent")

View File

@ -15,6 +15,7 @@ use Doctrine\ORM\Mapping AS ORM;
use App\Models\Utils\TimeZoneEntity;
use Doctrine\Common\Collections\ArrayCollection;
use models\summit\Presentation;
use models\summit\PresentationCategory;
use models\summit\PresentationCategoryGroup;
use models\summit\Summit;
use models\summit\SummitOwned;
@ -328,4 +329,64 @@ class SelectionPlan extends SilverstripeBaseModel
$this->presentations->add($presentation);
$presentation->setSelectedPresentations($this);
}
public function getStageStatus($stage) {
$getStartDate = "get{$stage}BeginDate";
$getEndDate = "get{$stage}EndDate";
$start_date = $this->$getStartDate();
$end_date = $this->$getEndDate();
if (empty($start_date) || empty($end_date)) {
return null;
}
$utc_time_zone = new \DateTimeZone('UTC');
$start_date->setTimeZone($utc_time_zone);
$end_date->setTimeZone($utc_time_zone);
$now = new \DateTime('now', new \DateTimeZone( 'UTC'));
if ($now > $end_date) {
return Summit::STAGE_FINISHED;
} else if ($now < $start_date) {
return Summit::STAGE_UNSTARTED;
} else {
return Summit::STAGE_OPEN;
}
}
/**
* @param PresentationCategory $track
* @return bool
*/
public function hasTrack(PresentationCategory $track){
foreach($this->category_groups as $track_group){
if($track_group->hasCategory($track->getIdentifier())) return true;
}
return false;
}
/**
* @return bool
*/
public function isVotingOpen()
{
return $this->getStageStatus('Voting') === Summit::STAGE_OPEN;
}
/**
* @return bool
*/
public function isSubmissionOpen()
{
return $this->getStageStatus('Submission') === Summit::STAGE_OPEN;
}
/**
* @return bool
*/
public function isSelectionOpen()
{
return $this->getStageStatus('Selection') === Summit::STAGE_OPEN;
}
}

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\SelectionPlan;
use Doctrine\ORM\Mapping AS ORM;
use App\Events\PresentationSpeakerCreated;
use App\Events\PresentationSpeakerDeleted;
@ -393,6 +394,38 @@ class PresentationSpeaker extends SilverstripeBaseModel
});
}
const ROLE_SPEAKER = 'ROLE_SPEAKER';
const ROLE_CREATOR = 'ROLE_CREATOR';
const ROLE_MODERATOR ='ROLE_MODERATOR';
/**
* @param SelectionPlan $selectionPlan
* @param $role
* @return array
*/
public function getPresentationsBySelectionPlanAndRole(SelectionPlan $selectionPlan, $role){
if($role == self::ROLE_SPEAKER){
$res = $this->presentations->filter(function(Presentation $presentation) use($selectionPlan){
if($presentation->getSelectionPlanId() != $selectionPlan->getId()) return false;
if($presentation->getSummit()->getId() != $selectionPlan->getSummitId()) return false;
if($presentation->getModeratorId() == $this->getId()) return false;
if($presentation->getCreatorId() == $this->getMemberId()) return false;
});
return $res->toArray();
}
if($role == self::ROLE_CREATOR){
return $selectionPlan->getSummit()->getCreatedPresentations($this, $selectionPlan);
}
if($role == self::ROLE_MODERATOR){
return $selectionPlan->getSummit()->getModeratedPresentationsBy($this, $selectionPlan);
}
return [];
}
/**
* @param Summit $summit
* @param string $role

View File

@ -20,6 +20,7 @@ use App\Models\Utils\TimeZoneEntity;
use DateTime;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\Query\Expr\Select;
use models\exceptions\ValidationException;
use models\main\Company;
use models\main\File;
@ -840,6 +841,43 @@ class Summit extends SilverstripeBaseModel
return $query->setParameter('summit_id', $this->getIdentifier())->getResult();
}
/**
* @param PresentationSpeaker $speaker
* @param SelectionPlan $selectionPlan
* @return array
*/
public function getModeratedPresentationsBy(PresentationSpeaker $speaker, SelectionPlan $selectionPlan){
$query = $this->createQuery("SELECT p from models\summit\Presentation p
JOIN p.summit s
JOIN p.moderator m
JOIN p.selection_plan sp
WHERE s.id = :summit_id and m.id = :moderator_id and sp.id = :selection_plan_id");
return $query
->setParameter('summit_id', $this->getIdentifier())
->setParameter('moderator_id', $speaker->getIdentifier())
->setParameter('selection_plan_id', $selectionPlan->getIdentifier())
->getResult();
}
/**
* @param PresentationSpeaker $speaker
* @param SelectionPlan $selectionPlan
* @return array
*/
public function getCreatedPresentations(PresentationSpeaker $speaker, SelectionPlan $selectionPlan){
$query = $this->createQuery("SELECT p from models\summit\Presentation p
JOIN p.summit s
JOIN p.creator c
JOIN p.selection_plan sp
WHERE s.id = :summit_id and c.id = :creator_id and sp.id = :selection_plan_id");
return $query
->setParameter('summit_id', $this->getIdentifier())
->setParameter('creator_id', $speaker->getMemberId())
->setParameter('selection_plan_id', $selectionPlan->getIdentifier())
->getResult();
}
/**
* @param int $event_id
* @return null|SummitEvent
@ -2116,4 +2154,50 @@ SQL;
return $this;
}
/**
* @return SelectionPlan[]
*/
public function getActiveSelectionPlans() {
$criteria = Criteria::create();
$criteria->where(Criteria::expr()->eq('is_enabled', 1));
return $this->selection_plans->matching($criteria)->toArray();
}
/**
* @return bool
*/
public function isSubmissionOpen()
{
foreach ($this->getActiveSelectionPlans() as $plan) {
if ($plan->isSubmissionOpen())
return true;
}
return false;
}
/**
* @return bool
*/
public function isPresentationEditionAllowed()
{
return $this->isSubmissionOpen() || $this->isVotingOpen();
}
/**
* @return bool
*/
public function isVotingOpen()
{
foreach ($this->getActiveSelectionPlans() as $plan) {
if ($plan->isVotingOpen()) {
return true;
}
}
return false;
}
const STAGE_UNSTARTED = -1;
const STAGE_OPEN = 0;
const STAGE_FINISHED = 1;
}

View File

@ -39,7 +39,7 @@ class TrackTagGroupAllowedTag extends BaseEntity
private $tag;
/**
* @ORM\ManyToOne(targetEntity="TrackTagGroup")
* @ORM\ManyToOne(targetEntity="TrackTagGroup", inversedBy="allowed_tags")
* @ORM\JoinColumn(name="TrackTagGroupID", referencedColumnName="ID")
* @var TrackTagGroup
*/

View File

@ -198,6 +198,33 @@ class AppServiceProvider extends ServiceProvider
return true;
});
Validator::extend('url_array', function($attribute, $value, $parameters, $validator)
{
$validator->addReplacer('url_array', function($message, $attribute, $rule, $parameters) use ($validator) {
return sprintf("%s should be an array of urls", $attribute);
});
if(!is_array($value)) return false;
foreach($value as $element)
{
if(!filter_var($element, FILTER_VALIDATE_URL)) return false;
}
return true;
});
Validator::extend('entity_value_array', function($attribute, $value, $parameters, $validator)
{
$validator->addReplacer('entity_value_array', function($message, $attribute, $rule, $parameters) use ($validator) {
return sprintf("%s should be an array of {id,value} tuple", $attribute);
});
if(!is_array($value)) return false;
foreach($value as $element)
{
if(!isset($element['id'])) return false;
if(!isset($element['value'])) return false;
}
return true;
});
Validator::extend('team_permission', function($attribute, $value, $parameters, $validator)
{
$validator->addReplacer('team_permission', function($message, $attribute, $rule, $parameters) use ($validator) {

View File

@ -52,4 +52,6 @@ final class SummitScopes
const WriteSummitSpeakerAssistanceData = '%s/summit-speaker-assistance/write';
const WriteTicketTypeData = '%s/ticket-types/write';
const WritePresentationData = '%s/summits/write-presentation';
}

View File

@ -12,9 +12,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use models\exceptions\EntityNotFoundException;
use models\exceptions\ValidationException;
use models\main\Member;
use models\summit\Presentation;
use models\summit\PresentationVideo;
use models\summit\Summit;
/**
* Interface IPresentationService
* @package services\model
@ -43,4 +46,25 @@ interface IPresentationService
* @return void
*/
public function deleteVideo($presentation_id, $video_id);
/**
* @param Summit $summit
* @param Member $member
* @param array $data
* @return Presentation
* @throws ValidationException
* @throws EntityNotFoundException
*/
public function submitPresentation(Summit $summit, Member $member, array $data);
/**
* @param Summit $summit
* @param int $presentation_id
* @param Member $member
* @param array $data
* @return Presentation
* @throws ValidationException
* @throws EntityNotFoundException
*/
public function updatePresentationSubmission(Summit $summit, $presentation_id, Member $member, array $data);
}

View File

@ -1,4 +1,5 @@
<?php namespace services\model;
/**
* Copyright 2016 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
@ -11,19 +12,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use App\Events\PresentationMaterialDeleted;
use App\Events\PresentationMaterialUpdated;
use App\Models\Foundation\Summit\SelectionPlan;
use App\Services\Model\AbstractService;
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\Member;
use models\summit\factories\IPresentationVideoFactory;
use models\summit\ISpeakerRepository;
use models\summit\ISummitEventRepository;
use models\summit\Presentation;
use models\summit\PresentationLink;
use models\summit\PresentationSpeaker;
use models\summit\PresentationType;
use models\summit\PresentationVideo;
use libs\utils\ITransactionService;
use models\summit\Summit;
/**
* Class PresentationService
* @package services\model
@ -42,16 +49,24 @@ final class PresentationService
*/
private $video_factory;
/**
* @var ISpeakerRepository
*/
private $speaker_repository;
public function __construct
(
IPresentationVideoFactory $video_factory,
ISummitEventRepository $presentation_repository ,
ITransactionService $tx_service
IPresentationVideoFactory $video_factory,
ISummitEventRepository $presentation_repository,
ISpeakerRepository $speaker_repository,
ITransactionService $tx_service
)
{
parent::__construct($tx_service);
$this->presentation_repository = $presentation_repository;
$this->video_factory = $video_factory;
$this->speaker_repository = $speaker_repository;
$this->video_factory = $video_factory;
}
/**
@ -61,21 +76,21 @@ final class PresentationService
*/
public function addVideoTo($presentation_id, array $video_data)
{
$video = $this->tx_service->transaction(function() use($presentation_id, $video_data){
$video = $this->tx_service->transaction(function () use ($presentation_id, $video_data) {
$presentation = $this->presentation_repository->getById($presentation_id);
if(is_null($presentation))
if (is_null($presentation))
throw new EntityNotFoundException('presentation not found!');
if(!$presentation instanceof Presentation)
if (!$presentation instanceof Presentation)
throw new EntityNotFoundException('presentation not found!');
if($presentation->hasVideos())
if ($presentation->hasVideos())
throw new ValidationException(sprintf('presentation %s already has a video!', $presentation_id));
if(!isset($video_data['name'])) $video_data['name'] = $presentation->getTitle();
if (!isset($video_data['name'])) $video_data['name'] = $presentation->getTitle();
$video = $this->video_factory->build($video_data);
@ -95,34 +110,34 @@ final class PresentationService
*/
public function updateVideo($presentation_id, $video_id, array $video_data)
{
$video = $this->tx_service->transaction(function() use($presentation_id, $video_id, $video_data){
$video = $this->tx_service->transaction(function () use ($presentation_id, $video_id, $video_data) {
$presentation = $this->presentation_repository->getById($presentation_id);
if(is_null($presentation))
if (is_null($presentation))
throw new EntityNotFoundException('presentation not found!');
if(!$presentation instanceof Presentation)
if (!$presentation instanceof Presentation)
throw new EntityNotFoundException('presentation not found!');
$video = $presentation->getVideoBy($video_id);
if(is_null($video))
if (is_null($video))
throw new EntityNotFoundException('video not found!');
if(!$video instanceof PresentationVideo)
if (!$video instanceof PresentationVideo)
throw new EntityNotFoundException('video not found!');
if(isset($video_data['name']))
if (isset($video_data['name']))
$video->setName(trim($video_data['name']));
if(isset($video_data['you_tube_id']))
if (isset($video_data['you_tube_id']))
$video->setYoutubeId(trim($video_data['you_tube_id']));
if(isset($video_data['description']))
if (isset($video_data['description']))
$video->setDescription(trim($video_data['description']));
if(isset($video_data['display_on_site']))
if (isset($video_data['display_on_site']))
$video->setDisplayOnSite((bool)$video_data['display_on_site']);
return $video;
@ -139,22 +154,22 @@ final class PresentationService
*/
public function deleteVideo($presentation_id, $video_id)
{
$this->tx_service->transaction(function() use($presentation_id, $video_id){
$this->tx_service->transaction(function () use ($presentation_id, $video_id) {
$presentation = $this->presentation_repository->getById($presentation_id);
if(is_null($presentation))
if (is_null($presentation))
throw new EntityNotFoundException('presentation not found!');
if(!$presentation instanceof Presentation)
if (!$presentation instanceof Presentation)
throw new EntityNotFoundException('presentation not found!');
$video = $presentation->getVideoBy($video_id);
if(is_null($video))
if (is_null($video))
throw new EntityNotFoundException('video not found!');
if(!$video instanceof PresentationVideo)
if (!$video instanceof PresentationVideo)
throw new EntityNotFoundException('video not found!');
$presentation->removeVideo($video);
@ -163,4 +178,243 @@ final class PresentationService
});
}
/**
* @param Summit $summit
* @return int
*/
public function getSubmissionLimitFor(Summit $summit)
{
$res = -1;
if($summit->isSubmissionOpen()) {
$res = intval($summit->getCurrentSelectionPlanByStatus(SelectionPlan::STATUS_SUBMISSION)->getMaxSubmissionAllowedPerUser());
}
// zero means infinity
return $res === 0 ? PHP_INT_MAX : $res;
}
/**
* @param Summit $summit
* @param Member $member
* @param array $data
* @return Presentation
* @throws ValidationException
* @throws EntityNotFoundException
* @throws \Exception
*/
public function submitPresentation(Summit $summit, Member $member, array $data)
{
return $this->tx_service->transaction(function () use ($summit, $member, $data) {
$current_selection_plan = $summit->getCurrentSelectionPlanByStatus(SelectionPlan::STATUS_SUBMISSION);
$current_speaker = $this->speaker_repository->getByMember($member);
if (is_null($current_speaker))
throw new ValidationException(trans());
if (is_null($current_selection_plan))
throw new ValidationException(trans());
// check qty
$limit = $this->getSubmissionLimitFor($summit);
$count = count($current_speaker->getPresentationsBySelectionPlanAndRole($current_selection_plan, PresentationSpeaker::ROLE_CREATOR)) +
count($current_speaker->getPresentationsBySelectionPlanAndRole($current_selection_plan, PresentationSpeaker::ROLE_MODERATOR)) +
count($current_speaker->getPresentationsBySelectionPlanAndRole($current_selection_plan, PresentationSpeaker::ROLE_SPEAKER));
if ($count >= $limit)
throw new ValidationException(trans(
'validation_errors.PresentationService.submitPresentation.limitReached',
['limit' => $limit]));
$presentation = new Presentation();
$presentation->setCreator($member);
$presentation->setSelectionPlan($current_selection_plan);
$summit->addEvent($presentation);
$presentation->setProgress(Presentation::PHASE_SUMMARY);
$presentation = $this->saveOrUpdatePresentation
(
$summit,
$current_selection_plan,
$presentation,
$current_speaker,
$data
);
return $presentation;
});
}
/**
* @param Summit $summit
* @param int $presentation_id
* @param Member $member
* @param array $data
* @return Presentation
* @throws ValidationException
* @throws EntityNotFoundException
*/
public function updatePresentationSubmission(Summit $summit, $presentation_id, Member $member, array $data){
return $this->tx_service->transaction(function () use ($summit, $presentation_id, $member, $data) {
$current_selection_plan = $summit->getCurrentSelectionPlanByStatus(SelectionPlan::STATUS_SUBMISSION);
$current_speaker = $this->speaker_repository->getByMember($member);
if (is_null($current_speaker))
throw new ValidationException(trans());
if (is_null($current_selection_plan))
throw new ValidationException(trans());
$presentation = $summit->getEvent($presentation_id);
if (is_null($current_selection_plan))
throw new EntityNotFoundException(trans());
return $this->saveOrUpdatePresentation
(
$summit,
$current_selection_plan,
$presentation,
$current_speaker,
$data
);
});
}
/**
* @param Summit $summit
* @param SelectionPlan $selection_plan
* @param Presentation $presentation
* @param PresentationSpeaker $current_speaker
* @param array $data
* @return Presentation
* @throws \Exception
*/
private function saveOrUpdatePresentation(Summit $summit,
SelectionPlan $selection_plan,
Presentation $presentation,
PresentationSpeaker $current_speaker,
array $data
){
return $this->tx_service->transaction(function () use ($summit, $selection_plan, $presentation, $current_speaker, $data) {
$event_type = $summit->getEventType(intval($data['type_id']));
if (is_null($event_type)) {
throw new EntityNotFoundException(
trans(
'not_found_errors.PresentationService.saveOrUpdatePresentation.eventTypeNotFound',
['type_id' => $data['type_id']]
)
);
}
if(!$event_type instanceof PresentationType){
throw new ValidationException(trans(
'validation_errors.PresentationService.saveOrUpdatePresentation.invalidPresentationType',
['type_id' => $event_type->getIdentifier()])
);
}
if(!$event_type->isShouldBeAvailableOnCfp()){
throw new ValidationException(trans(
'validation_errors.PresentationService.saveOrUpdatePresentation.notAvailableCFP',
['type_id' => $event_type->getIdentifier()]));
}
$track = $summit->getPresentationCategory(intval($data['track_id']));
if (is_null($track)) {
throw new EntityNotFoundException(
trans(
'not_found_errors.PresentationService.saveOrUpdatePresentation.trackNotFound',
['track_id' => $data['track_id']]
)
);
}
if(!$selection_plan->hasTrack($track)){
throw new ValidationException(trans(
'validation_errors.PresentationService.saveOrUpdatePresentation.trackDontBelongToSelectionPlan',
[
'selection_plan_id' => $selection_plan->getIdentifier(),
'track_id' => $track->getIdentifier(),
]));
}
if(isset($data['title']))
$presentation->setTitle(html_entity_decode(trim($data['title'])));
if(isset($data['description']))
$presentation->setAbstract(html_entity_decode(trim($data['description'])));
if(isset($data['social_description']))
$presentation->setSocialSummary(strip_tags(trim($data['social_description'])));
if(isset($data['level']))
$presentation->setLevel($data['level']);
if(isset($data['attendees_expected_learnt']))
$presentation->setAttendeesExpectedLearnt(html_entity_decode($data['attendees_expected_learnt']));
$presentation->setAttendingMedia(isset($data['attending_media']) ?
filter_var($data['attending_media'], FILTER_VALIDATE_BOOLEAN) : 0);
$presentation->setType($event_type);
$presentation->setCategory($track);
// add me as speaker
$presentation->addSpeaker($current_speaker);
if (isset($data['links'])) {
$presentation->clearLinks();
foreach ($data['links'] as $link) {
$presentationLink = new PresentationLink();
$presentationLink->setName(trim($link));
$presentationLink->setLink(trim($link));
$presentation->addLink($presentationLink);
}
}
// extra questions values
if (isset($data['extra_questions'])) {
foreach ($data['extra_questions'] as $extra_question) {
if(!isset($extra_question['id'])) continue;
if(!isset($extra_question['value'])) continue;
$extra_question_id = $extra_question['id'];
$extra_question_value = $extra_question['value'];
$track_question = $track->getExtraQuestionById($extra_question_id);
if(is_null($track_question)){
throw new EntityNotFoundException(
trans(
'not_found_errors.PresentationService.saveOrUpdatePresentation.trackQuestionNotFound',
['question_id' => $extra_question_id]
)
);
}
$answer = $presentation->getTrackExtraQuestionAnswer($track_question);
if(is_null($answer)){
$answer = new TrackAnswer();
$presentation->addAnswer($answer);
$track_question->addAnswer($answer);
}
if(is_array($extra_question_value) ){
$extra_question_value = str_replace('{comma}', ',', $extra_question_value);
$extra_question_value = implode(',', $extra_question_value);
}
$answer->setValue($extra_question_value);
}
}
return $presentation;
});
}
}

View File

@ -86,7 +86,6 @@ final class SummitService extends AbstractService implements ISummitService
*/
const MIN_EVENT_MINUTES = 5;
/**
* @var ISummitEventRepository
*/

View File

@ -1348,6 +1348,27 @@ class ApiEndpointsSeeder extends Seeder
'http_method' => 'POST',
'scopes' => [sprintf('%s/summits/confirm-external-orders', $current_realm)],
),
// presentation submissions
[
'name' => 'submit-presentation',
'route' => '/api/v1/summits/{id}/presentations',
'http_method' => 'POST',
'scopes' => [
sprintf(SummitScopes::WriteSummitData, $current_realm),
sprintf(SummitScopes::WriteEventData, $current_realm),
sprintf(SummitScopes::WritePresentationData, $current_realm)
],
],
[
'name' => 'update-submit-presentation',
'route' => '/api/v1/summits/{id}/presentations/{presentation_id}',
'http_method' => 'PUT',
'scopes' => [
sprintf(SummitScopes::WriteSummitData, $current_realm),
sprintf(SummitScopes::WriteEventData, $current_realm),
sprintf(SummitScopes::WritePresentationData, $current_realm)
],
],
//videos
[
'name' => 'get-presentation-videos',

View File

@ -43,41 +43,46 @@ final class ApiScopesSeeder extends Seeder
$api = EntityManager::getRepository(\App\Models\ResourceServer\Api::class)->findOneBy(['name' => 'summits']);
$scopes = [
array(
[
'name' => sprintf(SummitScopes::ReadSummitData, $current_realm),
'short_description' => 'Get Summit Data',
'description' => 'Grants read only access for Summits Data',
),
array(
],
[
'name' => sprintf(SummitScopes::ReadAllSummitData, $current_realm),
'short_description' => 'Get All Summits Data',
'description' => 'Grants read only access for All Summits Data',
),
array(
],
[
'name' => sprintf('%s/me/read', $current_realm),
'short_description' => 'Get own summit member data',
'description' => 'Grants read only access for our own summit member data',
),
array(
],
[
'name' => sprintf('%s/me/summits/events/favorites/add', $current_realm),
'short_description' => 'Allows to add Summit events as favorite',
'description' => 'Allows to add Summit events as favorite',
),
array(
],
[
'name' => sprintf('%s/me/summits/events/favorites/delete', $current_realm),
'short_description' => 'Allows to remove Summit events as favorite',
'description' => 'Allows to remove Summit events as favorite',
),
array(
],
[
'name' => sprintf(SummitScopes::WriteSummitData, $current_realm),
'short_description' => 'Write Summit Data',
'description' => 'Grants write access for Summits Data',
),
],
array(
'name' => sprintf('%s/summits/write-event', $current_realm),
'name' => sprintf(SummitScopes::WriteEventData, $current_realm),
'short_description' => 'Write Summit Events',
'description' => 'Grants write access for Summits Events',
),
array(
'name' => sprintf(SummitScopes::WritePresentationData, $current_realm),
'short_description' => 'Write Summit Presentations',
'description' => 'Grants write access for Summits Presentations',
),
array(
'name' => sprintf('%s/summits/delete-event', $current_realm),
'short_description' => 'Delete Summit Events',
@ -108,11 +113,11 @@ final class ApiScopesSeeder extends Seeder
'short_description' => 'Allow to read summit notifications',
'description' => 'Allow to read summit notifications',
),
array(
[
'name' => sprintf(SummitScopes::WriteSpeakersData, $current_realm),
'short_description' => 'Write Speakers Data',
'description' => 'Grants write access for Speakers Data',
),
],
[
'name' => sprintf(SummitScopes::WriteMySpeakersData, $current_realm),
'short_description' => 'Write My Speakers Profile Data',

View File

@ -83,4 +83,8 @@ return [
'SummitSelectionPlanService.addTrackGroupToSelectionPlan.TrackGroupNotFound' => 'track group :track_group_id not found on summit :summit_id',
'SummitSelectionPlanService.deleteTrackGroupToSelectionPlan.SelectionPlanNotFound' => 'selection plan :selection_plan_id not found on summit :summit_id',
'SummitSelectionPlanService.deleteTrackGroupToSelectionPlan.TrackGroupNotFound' => 'track group :track_group_id not found on summit :summit_id',
// Presentations
'PresentationService.saveOrUpdatePresentation.trackNotFound' => 'track :track_id not found.',
'PresentationService.submitPresentation.eventTypeNotFound' => 'event type :type_id not found.',
'PresentationService.saveOrUpdatePresentation.trackQuestionNotFound' => 'extra question :question_id not found.',
];

View File

@ -78,4 +78,9 @@ return [
'Summit.checkSelectionPlanConflicts.conflictOnVotingWorkflow' => 'there is a conflict on voting dates with selection plan :selection_plan_id on summit :summit_id',
'SummitSelectionPlanService.addSelectionPlan.alreadyExistName' => 'there is already another selection plan with same name on summit :summit_id',
'SummitSelectionPlanService.updateSelectionPlan.alreadyExistName' => 'there is already another selection plan with same name on summit :summit_id',
// Presentations
'PresentationService.saveOrUpdatePresentation.invalidPresentationType' => 'type id :type_id is not a valid presentation type',
'PresentationService.saveOrUpdatePresentation.notAvailableCFP' => 'type id :type_id is not a available for CFP',
'PresentationService.saveOrUpdatePresentation.trackDontBelongToSelectionPlan' => 'track :track_id does not belongs to selection plan :selection_plan_id',
'PresentationService.submitPresentation.limitReached' => 'You reached the limit :limit of presentations.',
];

View File

@ -0,0 +1,68 @@
<?php
/**
* Copyright 2018 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 OAuth2PresentationSubmissionTest extends ProtectedApiTest
{
/**
* @param int $summit_id
* @return mixed
*/
public function testSubmitPresentation($summit_id = 25){
$params = [
'id' => $summit_id,
];
$title = str_random(16).'_presentation';
$data = [
'title' => $title,
'description' => 'this is a description',
'social_description' => 'this is a social description',
'level' => 'N/A',
'attendees_expected_learnt' => 'super duper',
'type_id' => 171,
'track_id' => 248,
'attending_media' => true,
'links' => ['https://www.google.com'],
'extra_questions' => [
[
'id' => 24,
'value' => 'test',
]
]
];
$headers = [
"HTTP_Authorization" => " Bearer " . $this->access_token,
"CONTENT_TYPE" => "application/json"
];
$response = $this->action(
"POST",
"OAuth2PresentationApiController@submitPresentation",
$params,
[],
[],
[],
$headers,
json_encode($data)
);
$content = $response->getContent();
$this->assertResponseStatus(201);
$presentation = json_decode($content);
$this->assertTrue(!is_null($presentation));
$this->assertEquals($title, $presentation->title);
return $presentation;
}
}