diff --git a/app/Http/Controllers/Apis/Protected/Summit/EventTypeValidationRulesFactory.php b/app/Http/Controllers/Apis/Protected/Summit/EventTypeValidationRulesFactory.php new file mode 100644 index 00000000..bb418890 --- /dev/null +++ b/app/Http/Controllers/Apis/Protected/Summit/EventTypeValidationRulesFactory.php @@ -0,0 +1,75 @@ + 'required|string', + 'color' => 'sometimes|hex_color', + 'black_out_times' => 'sometimes|boolean', + 'use_sponsors' => 'sometimes|boolean', + 'are_sponsors_mandatory' => 'sometimes|boolean|required_with:use_sponsors', + 'allows_attachment' => 'sometimes|boolean', + ]; + + $specific_rules = []; + + switch ($class_name){ + case PresentationType::ClassName: + { + $specific_rules = [ + 'use_speakers' => 'sometimes|boolean', + 'are_speakers_mandatory' => 'sometimes|boolean|required_with:use_speakers', + 'min_speakers' => 'sometimes|integer|required_with:use_speakers', + 'max_speakers' => 'sometimes|integer|required_with:use_speakers|greater_than_field:max_speakers', + 'use_moderator' => 'sometimes|boolean', + 'is_moderator_mandatory' => 'sometimes|boolean|required_with:use_moderator', + 'min_moderators' => 'sometimes|integer|required_with:use_moderator', + 'max_moderators' => 'sometimes|integer|required_with:use_moderator|greater_than_field:min_moderators', + 'moderator_label' => 'sometimes|string' + ]; + } + break; + + } + return array_merge($base_rules, $specific_rules); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitPromoCodesApiController.php b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitPromoCodesApiController.php index 1899b7e2..65a91215 100644 --- a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitPromoCodesApiController.php +++ b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitPromoCodesApiController.php @@ -229,6 +229,10 @@ final class OAuth2SummitPromoCodesApiController extends OAuth2ProtectedControlle } } + /** + * @param $summit_id + * @return \Illuminate\Http\Response|mixed + */ public function getAllBySummitCSV($summit_id){ $values = Input::all(); $rules = [ diff --git a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitsEventTypesApiController.php b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitsEventTypesApiController.php index e6e61909..5f48cbc5 100644 --- a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitsEventTypesApiController.php +++ b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitsEventTypesApiController.php @@ -11,6 +11,7 @@ * See the License for the specific language governing permissions and * limitations under the License. **/ +use App\Services\Model\ISummitEventTypeService; use Illuminate\Support\Facades\Request; use App\Models\Foundation\Summit\Events\SummitEventTypeConstants; use App\Models\Foundation\Summit\Repositories\ISummitEventTypeRepository; @@ -21,6 +22,7 @@ use models\exceptions\ValidationException; use models\oauth2\IResourceServerContext; use models\summit\ISummitRepository; use models\exceptions\EntityNotFoundException; +use ModelSerializers\SerializerRegistry; use utils\Filter; use utils\FilterElement; use utils\FilterParser; @@ -38,17 +40,23 @@ final class OAuth2SummitsEventTypesApiController extends OAuth2ProtectedControll */ private $summit_repository; + /** + * @var ISummitEventTypeService + */ + private $event_type_service; public function __construct ( ISummitEventTypeRepository $repository, ISummitRepository $summit_repository, + ISummitEventTypeService $event_type_service, IResourceServerContext $resource_server_context ) { parent::__construct($resource_server_context); - $this->repository = $repository; - $this->summit_repository = $summit_repository; + $this->repository = $repository; + $this->summit_repository = $summit_repository; + $this->event_type_service = $event_type_service; } /** @@ -173,4 +181,48 @@ final class OAuth2SummitsEventTypesApiController extends OAuth2ProtectedControll } } + /** + * @param $summit_id + * @return mixed + */ + public function addEventTypeBySummit($summit_id){ + try { + + if(!Request::isJson()) return $this->error403(); + $data = Input::json(); + + $summit = SummitFinderStrategyFactory::build($this->summit_repository, $this->resource_server_context)->find($summit_id); + if (is_null($summit)) return $this->error404(); + + $rules = EventTypeValidationRulesFactory::build($data->all()); + // Creates a Validator instance and validates the data. + $validation = Validator::make($data->all(), $rules); + + if ($validation->fails()) { + $messages = $validation->messages()->toArray(); + + return $this->error412 + ( + $messages + ); + } + + $event_type = $this->event_type_service->addEventType($summit, $data->all()); + + return $this->created(SerializerRegistry::getInstance()->getSerializer($event_type)->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); + } + } } \ No newline at end of file diff --git a/app/Http/routes.php b/app/Http/routes.php index 48e8d835..67be8765 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -295,6 +295,7 @@ Route::group([ // event types Route::group(array('prefix' => 'event-types'), function () { Route::get('', 'OAuth2SummitsEventTypesApiController@getAllBySummit'); + Route::post('', [ 'middleware' => 'auth.user:administrators|summit-front-end-administrators', 'uses' => 'OAuth2SummitsEventTypesApiController@addEventTypeBySummit']); }); // external orders diff --git a/app/ModelSerializers/Summit/Presentation/PresentationTypeSerializer.php b/app/ModelSerializers/Summit/Presentation/PresentationTypeSerializer.php index 20ce1cbc..eeeef724 100644 --- a/app/ModelSerializers/Summit/Presentation/PresentationTypeSerializer.php +++ b/app/ModelSerializers/Summit/Presentation/PresentationTypeSerializer.php @@ -18,8 +18,8 @@ */ class PresentationTypeSerializer extends SummitEventTypeSerializer { - protected static $array_mappings = array - ( + protected static $array_mappings = [ + 'MaxSpeakers' => 'max_speakers:json_int', 'MinSpeakers' => 'min_speakers:json_int', 'MaxModerators' => 'max_moderators:json_int', @@ -27,9 +27,9 @@ class PresentationTypeSerializer extends SummitEventTypeSerializer 'UseSpeakers' => 'use_speakers:json_boolean', 'AreSpeakersMandatory' => 'are_speakers_mandatory:json_boolean', 'UseModerator' => 'use_moderator:json_boolean', - 'ModeratorMandatory' => 'moderator_mandatory:json_boolean', + 'ModeratorMandatory' => 'is_moderator_mandatory:json_boolean', 'ModeratorLabel' => 'moderator_label:json_string', - ); + ]; /** * @param null $expand @@ -38,7 +38,7 @@ class PresentationTypeSerializer extends SummitEventTypeSerializer * @param array $params * @return array */ - public function serialize($expand = null, array $fields = array(), array $relations = array(), array $params = array() ) + public function serialize($expand = null, array $fields = [], array $relations = [], array $params = [] ) { $values = parent::serialize($expand, $fields, $relations, $params); return $values; diff --git a/app/ModelSerializers/Summit/SummitEventTypeSerializer.php b/app/ModelSerializers/Summit/SummitEventTypeSerializer.php index 46975c12..b8f6d193 100644 --- a/app/ModelSerializers/Summit/SummitEventTypeSerializer.php +++ b/app/ModelSerializers/Summit/SummitEventTypeSerializer.php @@ -26,6 +26,7 @@ class SummitEventTypeSerializer extends SilverStripeSerializer 'UseSponsors' => 'use_sponsors:json_boolean', 'AreSponsorsMandatory' => 'are_sponsors_mandatory:json_boolean', 'AllowsAttachment' => 'allows_attachment:json_boolean', + 'IsDefault' => 'is_default:json_boolean', ]; /** diff --git a/app/Models/Foundation/Summit/Events/Presentations/PresentationType.php b/app/Models/Foundation/Summit/Events/Presentations/PresentationType.php index 65ae6d82..9bd470b6 100644 --- a/app/Models/Foundation/Summit/Events/Presentations/PresentationType.php +++ b/app/Models/Foundation/Summit/Events/Presentations/PresentationType.php @@ -201,4 +201,93 @@ SQL; } const ClassName = 'PRESENTATION_TYPE'; + + /** + * @param int $max_speakers + */ + public function setMaxSpeakers($max_speakers) + { + $this->max_speakers = $max_speakers; + } + + /** + * @param int $min_speakers + */ + public function setMinSpeakers($min_speakers) + { + $this->min_speakers = $min_speakers; + } + + /** + * @param int $max_moderators + */ + public function setMaxModerators($max_moderators) + { + $this->max_moderators = $max_moderators; + } + + /** + * @param int $min_moderators + */ + public function setMinModerators($min_moderators) + { + $this->min_moderators = $min_moderators; + } + + /** + * @param bool $use_speakers + */ + public function setUseSpeakers($use_speakers) + { + $this->use_speakers = $use_speakers; + } + + /** + * @param bool $are_speakers_mandatory + */ + public function setAreSpeakersMandatory($are_speakers_mandatory) + { + $this->are_speakers_mandatory = $are_speakers_mandatory; + } + + /** + * @param bool $use_moderator + */ + public function setUseModerator($use_moderator) + { + $this->use_moderator = $use_moderator; + } + + /** + * @param bool $is_moderator_mandatory + */ + public function setIsModeratorMandatory($is_moderator_mandatory) + { + $this->is_moderator_mandatory = $is_moderator_mandatory; + } + + /** + * @param bool $should_be_available_on_cfp + */ + public function setShouldBeAvailableOnCfp($should_be_available_on_cfp) + { + $this->should_be_available_on_cfp = $should_be_available_on_cfp; + } + + /** + * @param string $moderator_label + */ + public function setModeratorLabel($moderator_label) + { + $this->moderator_label = $moderator_label; + } + + public function __construct() + { + parent::__construct(); + $this->are_speakers_mandatory = false; + $this->use_speakers = false; + $this->use_moderator = false; + $this->is_moderator_mandatory = false; + } } \ No newline at end of file diff --git a/app/Models/Foundation/Summit/Events/SummitEventType.php b/app/Models/Foundation/Summit/Events/SummitEventType.php index a0c111f5..c929fb1c 100644 --- a/app/Models/Foundation/Summit/Events/SummitEventType.php +++ b/app/Models/Foundation/Summit/Events/SummitEventType.php @@ -13,7 +13,6 @@ **/ use models\utils\SilverstripeBaseModel; use Doctrine\ORM\Mapping AS ORM; - /** * @ORM\Entity(repositoryClass="App\Repositories\Summit\DoctrineSummitEventTypeRepository") * @ORM\Table(name="SummitEventType") @@ -61,6 +60,12 @@ class SummitEventType extends SilverstripeBaseModel */ protected $allows_attachment; + /** + * @ORM\Column(name="IsDefault", type="boolean") + * @var bool + */ + protected $is_default; + /** * @return string */ @@ -164,4 +169,56 @@ class SummitEventType extends SilverstripeBaseModel const ClassName = 'EVENT_TYPE'; -} \ No newline at end of file + /** + * @return boolean + */ + public function isDefault() + { + return $this->is_default; + } + + public function setAsDefault() + { + $this->is_default = true; + } + + public function setAsNonDefault() + { + $this->is_default = false; + } + + /** + * @param bool $use_sponsors + */ + public function setUseSponsors($use_sponsors) + { + $this->use_sponsors = $use_sponsors; + } + + /** + * @param bool $are_sponsors_mandatory + */ + public function setAreSponsorsMandatory($are_sponsors_mandatory) + { + $this->are_sponsors_mandatory = $are_sponsors_mandatory; + } + + /** + * @param bool $allows_attachment + */ + public function setAllowsAttachment($allows_attachment) + { + $this->allows_attachment = $allows_attachment; + } + + public function __construct() + { + parent::__construct(); + $this->is_default = false; + $this->use_sponsors = false; + $this->blackout_times = false; + $this->are_sponsors_mandatory = false; + $this->allows_attachment = false; + } + +} diff --git a/app/Models/Foundation/Summit/Factories/SummitEventTypeFactory.php b/app/Models/Foundation/Summit/Factories/SummitEventTypeFactory.php new file mode 100644 index 00000000..12d88ce3 --- /dev/null +++ b/app/Models/Foundation/Summit/Factories/SummitEventTypeFactory.php @@ -0,0 +1,118 @@ +setMinSpeakers(intval($data['min_speakers'])); + } + + if(isset($data['max_speakers'])) { + $event_type->setMaxSpeakers(intval($data['max_speakers'])); + } + + if(isset($data['min_moderators'])) { + $event_type->setMinModerators(intval($data['min_moderators'])); + } + + if(isset($data['max_moderators'])) { + $event_type->setMaxModerators(intval($data['max_moderators'])); + } + + if(isset($data['use_speakers'])) { + $event_type->setUseSpeakers(boolval($data['use_speakers'])); + } + + if(isset($data['are_speakers_mandatory'])) { + $event_type->setAreSpeakersMandatory(boolval($data['are_speakers_mandatory'])); + } + + if(isset($data['use_moderator'])) { + $event_type->setUseModerator(boolval($data['use_moderator'])); + } + + if(isset($data['is_moderator_mandatory'])) { + $event_type->setIsModeratorMandatory(boolval($data['is_moderator_mandatory'])); + } + + if(isset($data['moderator_label'])) { + $event_type->setModeratorLabel(trim($data['moderator_label'])); + } + } + } + break; + } + + $event_type->setType(trim($data['name'])); + + if(isset($data['color'])) + $event_type->setColor(trim($data['color'])); + + if(isset($data['black_out_times'])) + $event_type->setBlackoutTimes(boolval($data['black_out_times'])); + + if(isset($data['use_sponsors'])) + $event_type->setUseSponsors(boolval($data['use_sponsors'])); + + if(isset($data['are_sponsors_mandatory'])) + $event_type->setAreSponsorsMandatory(boolval($data['are_sponsors_mandatory'])); + + if(isset($data['allows_attachment'])) + $event_type->setAllowsAttachment(boolval($data['allows_attachment'])); + + $summit->addEventType($event_type); + return $event_type; + } +} \ No newline at end of file diff --git a/app/Models/Foundation/Summit/Summit.php b/app/Models/Foundation/Summit/Summit.php index 8384f41c..fbb00228 100644 --- a/app/Models/Foundation/Summit/Summit.php +++ b/app/Models/Foundation/Summit/Summit.php @@ -718,6 +718,16 @@ class Summit extends SilverstripeBaseModel return $event_type === false ? null:$event_type; } + /** + * @param string $type + * @return bool + */ + public function hasEventType($type){ + $criteria = Criteria::create(); + $criteria->where(Criteria::expr()->eq('type', $type)); + return $this->event_types->matching($criteria)->count() > 0; + } + /** * @param int $wifi_connection_id * @return SummitWIFIConnection|null @@ -729,7 +739,6 @@ class Summit extends SilverstripeBaseModel return $wifi_conn === false ? null:$wifi_conn; } - /** * @return SummitTicketType[] */ @@ -1497,4 +1506,14 @@ SQL; { return $this->category_default_tags->toArray(); } + + /** + * @param SummitEventType $event_type + * @return $this + */ + public function addEventType(SummitEventType $event_type){ + $this->event_types->add($event_type); + $event_type->setSummit($this); + return $this; + } } \ No newline at end of file diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 1784bf7c..8173c240 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -216,6 +216,21 @@ class AppServiceProvider extends ServiceProvider return true; }); + Validator::extend('greater_than_field', function($attribute, $value, $parameters, $validator) + { + $validator->addReplacer('greater_than_field', function($message, $attribute, $rule, $parameters) use ($validator) { + return sprintf("%s should be greather than %s", $attribute, $parameters[0]); + }); + $data = $validator->getData(); + if(is_null($value) || intval($value) == 0 ) return true; + if(isset($data[$parameters[0]])){ + $compare_to = $data[$parameters[0]]; + return intval($compare_to) < intval($value); + } + return true; + }); + + Validator::extend('valid_epoch', function($attribute, $value, $parameters, $validator) { $validator->addReplacer('valid_epoch', function($message, $attribute, $rule, $parameters) use ($validator) { @@ -223,6 +238,16 @@ class AppServiceProvider extends ServiceProvider }); return intval($value) > 0; }); + + Validator::extend('hex_color', function($attribute, $value, $parameters, $validator) + { + $validator->addReplacer('hex_color', function($message, $attribute, $rule, $parameters) use ($validator) { + return sprintf("%s should be a valid hex color value", $attribute); + }); + if(strlen($value) != 6) return false; + if(!ctype_xdigit($value)) return false; + return true; + }); } /** diff --git a/app/Security/SummitScopes.php b/app/Security/SummitScopes.php index 1350df17..f61ea9c0 100644 --- a/app/Security/SummitScopes.php +++ b/app/Security/SummitScopes.php @@ -35,5 +35,7 @@ final class SummitScopes const WritePromoCodeData = '%s/promo-codes/write'; + const WriteEventTypeData = '%s/event-types/write'; + const WriteSummitSpeakerAssistanceData = '%s/summit-speaker-assistance/write'; } \ No newline at end of file diff --git a/app/Services/Model/ISummitEventTypeService.php b/app/Services/Model/ISummitEventTypeService.php new file mode 100644 index 00000000..80e6470b --- /dev/null +++ b/app/Services/Model/ISummitEventTypeService.php @@ -0,0 +1,34 @@ +tx_manager = $tx_manager; + $this->repository = $repository; + } + + /** + * @param Summit $summit + * @param array $data + * @return SummitEventType + * @throws EntityNotFoundException + * @throws ValidationException + */ + public function addEventType(Summit $summit, array $data) + { + return $this->tx_manager->transaction(function() use($summit, $data){ + + $type = trim($data['name']); + + if($summit->hasEventType($type)){ + throw new ValidationException(sprintf("event type %s already exist on summit id %s", $type, $summit->getId())); + } + + $event_type = SummitEventTypeFactory::build($summit, $data); + + if(is_null($event_type)) + throw new ValidationException(sprintf("class_name %s is invalid", $data['class_name'])); + + return $event_type; + + }); + } +} \ No newline at end of file diff --git a/app/Services/ServicesProvider.php b/app/Services/ServicesProvider.php index 03f1d9eb..f2f5cf0b 100644 --- a/app/Services/ServicesProvider.php +++ b/app/Services/ServicesProvider.php @@ -14,8 +14,10 @@ use App\Services\Model\AttendeeService; use App\Services\Model\IAttendeeService; use App\Services\Model\IMemberService; +use App\Services\Model\ISummitEventTypeService; use App\Services\Model\MemberService; use App\Services\Model\SummitPromoCodeService; +use App\Services\SummitEventTypeService; use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Config; use Illuminate\Support\ServiceProvider; @@ -136,5 +138,11 @@ class ServicesProvider extends ServiceProvider ISummitPromoCodeService::class, SummitPromoCodeService::class ); + + App::singleton + ( + ISummitEventTypeService::class, + SummitEventTypeService::class + ); } } \ No newline at end of file diff --git a/database/seeds/ApiEndpointsSeeder.php b/database/seeds/ApiEndpointsSeeder.php index ab40f420..ca6b892d 100644 --- a/database/seeds/ApiEndpointsSeeder.php +++ b/database/seeds/ApiEndpointsSeeder.php @@ -537,7 +537,7 @@ class ApiEndpointsSeeder extends Seeder ], ), // event types - array( + [ 'name' => 'get-event-types', 'route' => '/api/v1/summits/{id}/event-types', 'http_method' => 'GET', @@ -545,7 +545,16 @@ class ApiEndpointsSeeder extends Seeder sprintf(SummitScopes::ReadSummitData, $current_realm), sprintf(SummitScopes::ReadAllSummitData, $current_realm) ], - ), + ], + [ + 'name' => 'add-event-type', + 'route' => '/api/v1/summits/{id}/event-types', + 'http_method' => 'POST', + 'scopes' => [ + sprintf(SummitScopes::WriteEventTypeData, $current_realm), + sprintf(SummitScopes::WriteSummitData, $current_realm) + ], + ], //tracks array( 'name' => 'get-tracks', diff --git a/tests/OAuth2EventTypesApiTest.php b/tests/OAuth2EventTypesApiTest.php index 8f51a15d..a4d1df3f 100644 --- a/tests/OAuth2EventTypesApiTest.php +++ b/tests/OAuth2EventTypesApiTest.php @@ -78,4 +78,39 @@ final class OAuth2EventTypesApiTest extends ProtectedApiTest $event_types = json_decode($content); $this->assertTrue(!is_null($event_types)); } + + public function testAddEventType($summit_id = 23){ + $params = [ + 'id' => $summit_id, + ]; + + $name = str_random(16).'_eventtype'; + $data = [ + 'name' => $name, + 'class_name' => \models\summit\SummitEventType::ClassName, + ]; + + $headers = [ + "HTTP_Authorization" => " Bearer " . $this->access_token, + "CONTENT_TYPE" => "application/json" + ]; + + $response = $this->action( + "POST", + "OAuth2SummitsEventTypesApiController@addEventTypeBySummit", + $params, + [], + [], + [], + $headers, + json_encode($data) + ); + + $content = $response->getContent(); + $this->assertResponseStatus(201); + $event_type = json_decode($content); + $this->assertTrue(!is_null($event_type)); + return $event_type; + } + } \ No newline at end of file