added new endpoint add location banner

POST /api/v1/summits/{id}/locations/{location_id}/banners
Payload

* title (required|string)
* content (required|string)
* type (required|in:Primary,Secondary)
* enabled (required|boolean)
* class_name (required|in:SummitLocationBanner, ScheduledSummitLocationBanner)

Payload part for ScheduledSummitLocationBanner

* start_date (required|date_format:U)
* end_date (required_with:start_date|date_format:U|after:start_date)

Required Scopes

* '%s/summits/write'
* '%s/locations/write'
* '%s/locations/banners/write'

Change-Id: Ic8de4a81c4d57253bbe25559a5ba7f50d1c7730d
This commit is contained in:
Sebastian Marcet 2018-03-07 19:01:04 -03:00
parent ec4a3474ad
commit dda0d68378
25 changed files with 1359 additions and 25 deletions

View File

@ -12,6 +12,7 @@
* limitations under the License.
**/
use App\Http\Utils\PagingConstants;
use App\Models\Foundation\Summit\Locations\Banners\SummitLocationBannerConstants;
use App\Models\Foundation\Summit\Locations\SummitLocationConstants;
use App\Models\Foundation\Summit\Repositories\ISummitLocationRepository;
use App\Services\Model\ILocationService;
@ -20,6 +21,7 @@ use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Request;
use Illuminate\Support\Facades\Validator;
use libs\utils\HTMLCleaner;
use models\exceptions\EntityNotFoundException;
use models\exceptions\ValidationException;
use models\oauth2\IResourceServerContext;
@ -1553,4 +1555,64 @@ final class OAuth2SummitLocationsApiController extends OAuth2ProtectedController
return $this->error500($ex);
}
}
/**
* Location Banners Endpoints
*/
/**
* @param $summit_id
* @param $location_id
* @return mixed
*/
public function addLocationBanner($summit_id, $location_id){
try {
if(!Request::isJson()) return $this->error403();
$payload = Input::json()->all();
$summit = SummitFinderStrategyFactory::build($this->repository, $this->resource_server_context)->find($summit_id);
if (is_null($summit)) return $this->error404();
$rules = SummitLocationBannerValidationRulesFactory::build($payload);
// Creates a Validator instance and validates the data.
$validation = Validator::make($payload, $rules);
if ($validation->fails()) {
$messages = $validation->messages()->toArray();
return $this->error412
(
$messages
);
}
if(!in_array($payload["class_name"], SummitLocationBannerConstants::$valid_class_names) ){
throw new ValidationException(
sprintf
(
"class_name has an invalid value ( valid values are %s",
implode(", ", SummitLocationBannerConstants::$valid_class_names)
)
);
}
$banner = $this->location_service->addLocationBanner($summit, $location_id, HTMLCleaner::cleanData($payload, ['title', 'content']));
return $this->created(SerializerRegistry::getInstance()->getSerializer($banner)->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);
}
}
}

View File

@ -0,0 +1,77 @@
<?php namespace App\Http\Controllers;
/**
* 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 App\Models\Foundation\Summit\Locations\Banners\ScheduledSummitLocationBanner;
use App\Models\Foundation\Summit\Locations\Banners\SummitLocationBanner;
use App\Models\Foundation\Summit\Locations\Banners\SummitLocationBannerConstants;
use models\exceptions\ValidationException;
/**
* Class SummitLocationBannerValidationRulesFactory
* @package App\Http\Controllers
*/
final class SummitLocationBannerValidationRulesFactory
{
/**
* @param array $data
* @param bool $update
* @return array
* @throws ValidationException
*/
public static function build(array $data, $update = false)
{
if(!isset($data['class_name']))
throw new ValidationException('class_name is not set');
$base_rules = [
'title' => 'required|string',
'content' => 'required|string',
'type' => sprintf('required|in:%s', implode(",", SummitLocationBannerConstants::$valid_types)),
'enabled' => 'required|boolean'
];
if($update){
$base_rules = [
'title' => 'sometimes|string',
'content' => 'sometimes|string',
'type' => sprintf('sometimes|in:%s', implode(",", SummitLocationBannerConstants::$valid_types)),
'enabled' => 'sometimes|boolean'
];
}
switch($data['class_name']){
case SummitLocationBanner::ClassName: {
return $base_rules;
}
break;
case ScheduledSummitLocationBanner::ClassName: {
$extended_rules = [
'start_date' => 'required|date_format:U',
'end_date' => 'required_with:start_date|date_format:U|after:start_date',
];
if($update){
$extended_rules = [
'start_date' => 'sometimes|date_format:U',
'end_date' => 'required_with:start_date|date_format:U|after:start_date',
];
}
return array_merge($base_rules, $extended_rules);
}
default:
throw new ValidationException("invalid class_name");
break;
}
return [];
}
}

View File

@ -348,6 +348,15 @@ Route::group([
Route::delete('', [ 'middleware' => 'auth.user:administrators|summit-front-end-administrators', 'uses' => 'OAuth2SummitLocationsApiController@deleteLocation']);
Route::get('/events/published','OAuth2SummitLocationsApiController@getLocationPublishedEvents')->where('location_id', 'tbd|[0-9]+');
Route::get('/events','OAuth2SummitLocationsApiController@getLocationEvents')->where('location_id', 'tbd|[0-9]+');
// location banners
Route::group(['prefix' => 'banners'], function () {
Route::get('', 'OAuth2SummitLocationsApiController@getLocationBanners');
Route::post('', [ 'middleware' => 'auth.user:administrators|summit-front-end-administrators', 'uses' => 'OAuth2SummitLocationsApiController@addLocationBanner']);
Route::group(['prefix' => '{banner_id}'], function () {
Route::put('', [ 'middleware' => 'auth.user:administrators|summit-front-end-administrators', 'uses' => 'OAuth2SummitLocationsApiController@updateLocationBanner']);
Route::delete('', [ 'middleware' => 'auth.user:administrators|summit-front-end-administrators', 'uses' => 'OAuth2SummitLocationsApiController@deleteLocationBanner']);
});
});
});
});

View File

@ -36,6 +36,8 @@ use App\ModelSerializers\Marketplace\SpokenLanguageSerializer;
use App\ModelSerializers\Marketplace\SupportChannelTypeSerializer;
use App\ModelSerializers\Software\OpenStackComponentSerializer;
use App\ModelSerializers\Software\OpenStackReleaseSerializer;
use App\ModelSerializers\Summit\ScheduledSummitLocationBannerSerializer;
use App\ModelSerializers\Summit\SummitLocationBannerSerializer;
use Libs\ModelSerializers\IModelSerializer;
use ModelSerializers\ChatTeams\ChatTeamInvitationSerializer;
use ModelSerializers\ChatTeams\ChatTeamMemberSerializer;
@ -128,14 +130,15 @@ final class SerializerRegistry
$this->registry['SponsorSummitRegistrationPromoCode'] = SponsorSummitRegistrationPromoCodeSerializer::class;
$this->registry['PresentationSpeakerSummitAssistanceConfirmationRequest'] = PresentationSpeakerSummitAssistanceConfirmationRequestSerializer::class;
// locations
$this->registry['SummitVenue'] = SummitVenueSerializer::class;
$this->registry['SummitVenueRoom'] = SummitVenueRoomSerializer::class;
$this->registry['SummitVenueFloor'] = SummitVenueFloorSerializer::class;
$this->registry['SummitExternalLocation'] = SummitExternalLocationSerializer::class;
$this->registry['SummitHotel'] = SummitHotelSerializer::class;
$this->registry['SummitAirport'] = SummitAirportSerializer::class;
$this->registry['SummitLocationImage'] = SummitLocationImageSerializer::class;
$this->registry['SummitVenue'] = SummitVenueSerializer::class;
$this->registry['SummitVenueRoom'] = SummitVenueRoomSerializer::class;
$this->registry['SummitVenueFloor'] = SummitVenueFloorSerializer::class;
$this->registry['SummitExternalLocation'] = SummitExternalLocationSerializer::class;
$this->registry['SummitHotel'] = SummitHotelSerializer::class;
$this->registry['SummitAirport'] = SummitAirportSerializer::class;
$this->registry['SummitLocationImage'] = SummitLocationImageSerializer::class;
$this->registry['SummitLocationBanner'] = SummitLocationBannerSerializer::class;
$this->registry['ScheduledSummitLocationBanner'] = ScheduledSummitLocationBannerSerializer::class;
// member
$this->registry['Member'] = [
self::SerializerType_Public => PublicMemberSerializer::class,

View File

@ -0,0 +1,26 @@
<?php namespace App\ModelSerializers\Summit;
/**
* 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 ScheduledSummitLocationBannerSerializer
* @package App\ModelSerializers\Summit
*/
final class ScheduledSummitLocationBannerSerializer extends SummitLocationBannerSerializer
{
protected static $array_mappings = [
'StartDate' => 'start_date:datetime_epoch',
'EndDate' => 'end_date:datetime_epoch',
];
}

View File

@ -0,0 +1,48 @@
<?php namespace App\ModelSerializers\Summit;
/**
* 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 App\Models\Foundation\Summit\Locations\Banners\SummitLocationBanner;
use ModelSerializers\SilverStripeSerializer;
/**
* Class SummitLocationBannerSerializer
* @package App\ModelSerializers\Summit
*/
class SummitLocationBannerSerializer extends SilverStripeSerializer
{
protected static $array_mappings = [
'Title' => 'title:json_string',
'Content' => 'content:json_string',
'Type' => 'type:json_string',
'Enabled' => 'enabled:json_boolean',
'LocationId' => 'location_id:json_int',
'ClassName' => 'class_name:json_string',
];
/**
* @param null $expand
* @param array $fields
* @param array $relations
* @param array $params
* @return array
*/
public function serialize($expand = null, array $fields = array(), array $relations = array(), array $params = array() )
{
$values = parent::serialize($expand, $fields, $relations, $params);
$banner = $this->object;
if(!$banner instanceof SummitLocationBanner) return [];
return $values;
}
}

View File

@ -684,8 +684,8 @@ class SummitEvent extends SilverstripeBaseModel
sprintf
(
'start/end datetime must be between summit start/end datetime! (%s - %s)',
$summit->getLocalBeginDate(),
$summit->getLocalEndDate()
$summit->getLocalBeginDate()->format('Y-m-d H:i:s'),
$summit->getLocalEndDate()->format('Y-m-d H:i:s')
)
);

View File

@ -0,0 +1,145 @@
<?php namespace App\Models\Foundation\Summit\Factories;
/**
* 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 App\Models\Foundation\Summit\Locations\Banners\ScheduledSummitLocationBanner;
use App\Models\Foundation\Summit\Locations\Banners\SummitLocationBanner;
use models\exceptions\ValidationException;
use models\summit\Summit;
use models\summit\SummitAbstractLocation;
/**
* Class SummitLocationBannerFactory
* @package App\Models\Foundation\Summit\Factories
*/
final class SummitLocationBannerFactory
{
const MinBannerDisplayMinutes = 1;
/**
* @param Summit $summit
* @param SummitAbstractLocation $location
* @param array $data
* @return ScheduledSummitLocationBanner|SummitLocationBanner|null
* @throws ValidationException
*/
public static function build(Summit $summit, SummitAbstractLocation $location, array $data){
if(!isset($data['class_name']))
throw new ValidationException("missing class_name param");
$banner = null;
switch($data['class_name']){
case SummitLocationBanner::ClassName :{
$banner = self::populateSummitLocationBanner($summit, $location, new SummitLocationBanner, $data);
}
break;
case ScheduledSummitLocationBanner::ClassName :{
$banner = self::populateScheduledSummitLocationBanner($summit, $location, new ScheduledSummitLocationBanner, $data);
}
}
return $banner;
}
/**
* @param Summit $summit
* @param SummitAbstractLocation $location
* @param SummitLocationBanner $banner
* @param array $data
* @return SummitLocationBanner
*/
private static function populateSummitLocationBanner(Summit $summit, SummitAbstractLocation $location, SummitLocationBanner $banner, array $data){
if(isset($data['title']))
$banner->setTitle(trim($data['title']));
if(isset($data['content']))
$banner->setContent(trim($data['content']));
if(isset($data['type']))
$banner->setType(trim($data['type']));
if(isset($data['enabled']))
$banner->setEnabled(boolval($data['enabled']));
$banner->setLocation($location);
return $banner;
}
/**
* @param Summit $summit
* @param SummitAbstractLocation $location
* @param ScheduledSummitLocationBanner $banner
* @param array $data
* @return ScheduledSummitLocationBanner
* @throws ValidationException
*/
private static function populateScheduledSummitLocationBanner(Summit $summit, SummitAbstractLocation $location, ScheduledSummitLocationBanner $banner, array $data){
self::populateSummitLocationBanner($summit, $location, $banner, $data);
if (isset($data['start_date']) && isset($data['end_date'])) {
$start_datetime = intval($data['start_date']);
$start_datetime = new \DateTime("@$start_datetime");
$start_datetime->setTimezone($summit->getTimeZone());
$end_datetime = intval($data['end_date']);
$end_datetime = new \DateTime("@$end_datetime");
$end_datetime->setTimezone($summit->getTimeZone());
$interval_seconds = $end_datetime->getTimestamp() - $start_datetime->getTimestamp();
$minutes = $interval_seconds / 60;
if ($minutes < self::MinBannerDisplayMinutes)
throw new ValidationException
(
sprintf
(
"schedule banner should last at least %s minutes - current duration %s",
self::MinBannerDisplayMinutes,
$minutes
)
);
// set local time from UTC
$banner->setStartDate($start_datetime);
$banner->setEndDate($end_datetime);
if(!$summit->isTimeFrameInsideSummitDuration($banner->getLocalStartDate(), $banner->getLocalEndDate())){
throw new ValidationException
(
sprintf
(
'start/end datetime must be between summit start/end datetime! (%s - %s)',
$summit->getLocalBeginDate()->format('Y-m-d H:i:s'),
$summit->getLocalEndDate()->format('Y-m-d H:i:s')
)
);
}
}
return $banner;
}
/**
* @param Summit $summit
* @param SummitAbstractLocation $location
* @param SummitLocationBanner $banner
* @param array $data
* @return ScheduledSummitLocationBanner|SummitLocationBanner
*/
public static function populate(Summit $summit, SummitAbstractLocation $location, SummitLocationBanner $banner, array $data){
if($banner instanceof ScheduledSummitLocationBanner){
return self::populateScheduledSummitLocationBanner($summit, $location, $banner, $data);
}
else if($banner instanceof SummitLocationBanner){
return self::populateSummitLocationBanner($summit, $location, $banner, $data);
}
return $banner;
}
}

View File

@ -0,0 +1,126 @@
<?php namespace App\Models\Foundation\Summit\Locations\Banners;
/**
* 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 DateTime;
/**
* @ORM\Entity
* @ORM\Table(name="ScheduledSummitLocationBanner")
* Class ScheduledSummitLocationBanner
* @package App\Models\Foundation\Summit\Locations\Banners
*/
class ScheduledSummitLocationBanner extends SummitLocationBanner
{
const ClassName = 'ScheduledSummitLocationBanner';
/**
* @return string
*/
public function getClassName(){
return ScheduledSummitLocationBanner::ClassName;
}
/**
* @ORM\Column(name="StartDate", type="datetime")
* @var DateTime
*/
protected $start_date;
/**
* @ORM\Column(name="EndDate", type="datetime")
* @var DateTime
*/
protected $end_date;
/**
* @param DateTime $value
* @return $this
*/
public function setStartDate(DateTime $value)
{
$summit = $this->getLocation()->getSummit();
if(!is_null($summit))
{
$value = $summit->convertDateFromTimeZone2UTC($value);
}
$this->start_date = $value;
return $this;
}
/**
* @return DateTime|null
*/
public function getLocalStartDate()
{
$res = null;
if(!empty($this->start_date)) {
$value = clone $this->start_date;
$summit = $this->getLocation()->getSummit();
if(!is_null($summit))
{
$res = $summit->convertDateFromUTC2TimeZone($value);
}
}
return $res;
}
/**
* @return \DateTime|null
*/
public function getStartDate()
{
return $this->start_date;
}
/**
* @param DateTime $value
* @return $this
*/
public function setEndDate(DateTime $value)
{
$summit = $this->getLocation()->getSummit();
if(!is_null($summit))
{
$value = $summit->convertDateFromTimeZone2UTC($value);
}
$this->end_date = $value;
return $this;
}
/**
* @return DateTime|null
*/
public function getLocalEndDate()
{
$res = null;
if(!empty($this->end_date)) {
$value = clone $this->end_date;
$summit = $this->getLocation()->getSummit();
if(!is_null($summit))
{
$res = $summit->convertDateFromUTC2TimeZone($value);
}
}
return $res;
}
/**
* @return \DateTime|null
*/
public function getEndDate()
{
return $this->end_date;
}
}

View File

@ -0,0 +1,175 @@
<?php namespace App\Models\Foundation\Summit\Locations\Banners;
/**
* 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 models\summit\SummitAbstractLocation;
use models\utils\SilverstripeBaseModel;
use Doctrine\ORM\Mapping AS ORM;
/**
* @ORM\Entity(repositoryClass="App\Repositories\Summit\DoctrineSummitLocationBannerRepository")
* @ORM\Table(name="SummitLocationBanner")
* @ORM\InheritanceType("JOINED")
* @ORM\DiscriminatorColumn(name="ClassName", type="string")
* @ORM\DiscriminatorMap({"SummitLocationBanner" = "SummitLocationBanner",
* "ScheduledSummitLocationBanner" = "ScheduledSummitLocationBanner"})
* Class SummitLocationBanner
* @package App\Models\Foundation\Summit\Locations\Banners
*/
class SummitLocationBanner extends SilverstripeBaseModel
{
const TypePrimary = 'Primary';
const TypeSecondary = 'Secondary';
const ClassName = 'SummitLocationBanner';
/**
* @return string
*/
public function getClassName(){
return SummitLocationBanner::ClassName;
}
/**
* @ORM\Column(name="Title", type="string")
* @var string
*/
protected $title;
/**
* @ORM\Column(name="Content", type="string")
* @var string
*/
protected $content;
/**
* @ORM\Column(name="Type", type="string")
* @var string
*/
protected $type;
/**
* @ORM\Column(name="Enabled", type="boolean")
* @var bool
*/
protected $enabled;
/**
* @ORM\ManyToOne(targetEntity="models\summit\SummitAbstractLocation", fetch="EXTRA_LAZY")
* @ORM\JoinColumn(name="LocationID", referencedColumnName="ID", onDelete="SET NULL")
* @var SummitAbstractLocation
*/
protected $location;
/**
* @return string
*/
public function getTitle()
{
return $this->title;
}
/**
* @param string $title
*/
public function setTitle($title)
{
$this->title = $title;
}
/**
* @return string
*/
public function getContent()
{
return $this->content;
}
/**
* @param string $content
*/
public function setContent($content)
{
$this->content = $content;
}
/**
* @return string
*/
public function getType()
{
return $this->type;
}
/**
* @param string $type
*/
public function setType($type)
{
$this->type = $type;
}
/**
* @return bool
*/
public function isEnabled()
{
return $this->enabled;
}
/**
* @param bool $enabled
*/
public function setEnabled($enabled)
{
$this->enabled = $enabled;
}
/**
* @return SummitAbstractLocation
*/
public function getLocation()
{
return $this->location;
}
/**
* @param SummitAbstractLocation $location
*/
public function setLocation(SummitAbstractLocation $location)
{
$this->location = $location;
}
public function clearLocation(){
$this->location = null;
}
/**
* @return bool
*/
public function hasLocation(){
return $this->getLocationId() > 0;
}
/**
* @return int
*/
public function getLocationId()
{
try {
return !is_null($this->location)? $this->location->getId():0;
}
catch(\Exception $ex){
return 0;
}
}
}

View File

@ -0,0 +1,24 @@
<?php namespace App\Models\Foundation\Summit\Locations\Banners;
/**
* 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 SummitLocationBannerConstants
* @package App\Models\Foundation\Summit\Locations\Banners
*/
final class SummitLocationBannerConstants
{
public static $valid_class_names = [SummitLocationBanner::ClassName, ScheduledSummitLocationBanner::ClassName];
public static $valid_types = [SummitLocationBanner::TypePrimary, SummitLocationBanner::TypeSecondary];
}

View File

@ -1,5 +1,4 @@
<?php namespace models\summit;
/**
* Copyright 2015 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
@ -12,10 +11,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use App\Models\Foundation\Summit\Locations\Banners\ScheduledSummitLocationBanner;
use App\Models\Foundation\Summit\Locations\Banners\SummitLocationBanner;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Criteria;
use models\exceptions\ValidationException;
use models\utils\SilverstripeBaseModel;
use Doctrine\ORM\Mapping AS ORM;
/**
* @see https://github.com/doctrine/doctrine2/commit/ff28507b88ffd98830c44762
* //@ORM\AssociationOverrides({
@ -43,28 +45,39 @@ class SummitAbstractLocation extends SilverstripeBaseModel
/**
* @ORM\Column(name="Name", type="string")
* @var string
*/
protected $name;
/**
* @ORM\Column(name="Description", type="string")
* @var string
*/
protected $description;
/**
* @ORM\Column(name="LocationType", type="string")
* @var string
*/
protected $type;
/**
* @ORM\Column(name="`Order`", type="integer")
* @var int
*/
protected $order;
/**
* @ORM\OneToMany(targetEntity="App\Models\Foundation\Summit\Locations\Banners\SummitLocationBanner", mappedBy="location", cascade={"persist"}, orphanRemoval=true)
* @var SummitLocationBanner[]
*/
protected $banners;
public static $metadata = [
'name' => 'string',
'description' => 'string',
'type' => [ self::TypeExternal, self::TypeInternal],
'banners' => 'array',
'order' => 'integer',
];
@ -78,7 +91,8 @@ class SummitAbstractLocation extends SilverstripeBaseModel
public function __construct()
{
parent::__construct();
$this->type = self::TypeNone;
$this->type = self::TypeNone;
$this->banners = new ArrayCollection;
}
/**
@ -161,4 +175,87 @@ class SummitAbstractLocation extends SilverstripeBaseModel
{
return false;
}
/**
* @param SummitLocationBanner $banner
* @return $this
* @throws ValidationException
*/
public function addBanner(SummitLocationBanner $banner){
if($banner->getClassName() == SummitLocationBanner::ClassName){
// only one static banner could exist at the same time
foreach ($this->banners as $old_banner){
if($old_banner->getClassName() == SummitLocationBanner::ClassName && $old_banner->isEnabled()){
throw new ValidationException
(
sprintf
('already exist an enabled static banner for location %s', $this->id)
);
}
}
}
if($banner instanceof ScheduledSummitLocationBanner){
// dont overlap enabled ones
$new_start = $banner->getLocalStartDate();
$new_end = $banner->getLocalEndDate();
foreach ($this->banners as $old_banner){
if($old_banner instanceof ScheduledSummitLocationBanner && $old_banner->isEnabled()){
$old_start = $old_banner->getLocalStartDate();
$old_end = $old_banner->getLocalEndDate();
// (StartA <= EndB) and (EndA >= StartB)
if($new_start <= $old_end && $new_end >= $old_start){
// overlap!!!
throw new ValidationException
(
sprintf
(
'schedule time range (%s to %s) overlaps with an existent scheduled time range (%s to %s) - banner id %s',
$new_start->format('Y-m-d H:i:s'),
$new_end->format('Y-m-d H:i:s'),
$old_start->format('Y-m-d H:i:s'),
$old_end->format('Y-m-d H:i:s'),
$old_banner->id
)
);
}
}
}
}
$this->banners->add($banner);
$banner->setLocation($this);
return $this;
}
/**
* @param SummitLocationBanner $banner
* @return $this
*/
public function removeBanner(SummitLocationBanner $banner){
$this->banners->removeElement($banner);
$banner->clearLocation();
return $this;
}
/**
* @param int $banner_id
* @return SummitLocationBanner|null
*/
public function getBannerById($banner_id){
$criteria = Criteria::create();
$criteria->where(Criteria::expr()->eq('id', intval($banner_id)));
$banner = $this->banners->matching($criteria)->first();
return $banner === false ? null : $banner;
}
/**
* @param string $class_name
* @return SummitLocationBanner|null
*/
public function getBannerByClass($class_name){
}
}

View File

@ -0,0 +1,56 @@
<?php namespace App\Models\Foundation\Summit\Repositories;
/**
* 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 models\summit\Summit;
use models\summit\SummitAbstractLocation;
use models\utils\IBaseRepository;
use utils\Filter;
use utils\Order;
use utils\PagingInfo;
use utils\PagingResponse;
/**
* Interface ISummitLocationBannerRepository
* @package App\Models\Foundation\Summit\Repositories
*/
interface ISummitLocationBannerRepository extends IBaseRepository
{
/**
* @param Summit $summit
* @param PagingInfo $paging_info
* @param Filter|null $filter
* @param Order|null $order
* @return PagingResponse
*/
public function getBySummit
(
Summit $summit,
PagingInfo $paging_info,
Filter $filter = null,
Order $order = null
);
/**
* @param SummitAbstractLocation $location
* @param PagingInfo $paging_info
* @param Filter|null $filter
* @param Order|null $order
* @return PagingResponse
*/
public function getBySummitLocation
(
SummitAbstractLocation $location,
PagingInfo $paging_info,
Filter $filter = null,
Order $order = null
);
}

View File

@ -1023,13 +1023,21 @@ class Summit extends SilverstripeBaseModel
*/
public function isEventInsideSummitDuration(SummitEvent $summit_event)
{
$event_start_date = $summit_event->getLocalStartDate();
$event_end_date = $summit_event->getLocalEndDate();
return $this->isTimeFrameInsideSummitDuration($summit_event->getLocalStartDate(), $summit_event->getLocalEndDate());
}
/**
* @param DateTime $start_date
* @param DateTime $end_date
* @return bool
*/
public function isTimeFrameInsideSummitDuration(DateTime $start_date, DateTime $end_date )
{
$summit_start_date = $this->getLocalBeginDate();
$summit_end_date = $this->getLocalEndDate();
return $event_start_date >= $summit_start_date && $event_start_date <= $summit_end_date &&
$event_end_date <= $summit_end_date && $event_end_date >= $event_start_date;
return $start_date >= $summit_start_date && $start_date <= $summit_end_date &&
$end_date <= $summit_end_date && $end_date >= $start_date;
}
/**

View File

@ -13,10 +13,12 @@
**/
use App\Models\Foundation\Summit\Defaults\DefaultSummitEventType;
use App\Models\Foundation\Summit\Events\RSVP\RSVPTemplate;
use App\Models\Foundation\Summit\Locations\Banners\SummitLocationBanner;
use App\Models\Foundation\Summit\Repositories\IDefaultSummitEventTypeRepository;
use App\Models\Foundation\Summit\Repositories\IPresentationSpeakerSummitAssistanceConfirmationRequestRepository;
use App\Models\Foundation\Summit\Repositories\IRSVPTemplateRepository;
use App\Models\Foundation\Summit\Repositories\ISummitEventTypeRepository;
use App\Models\Foundation\Summit\Repositories\ISummitLocationBannerRepository;
use App\Models\Foundation\Summit\Repositories\ISummitLocationRepository;
use App\Models\Foundation\Summit\Repositories\ISummitTrackRepository;
use Illuminate\Support\Facades\App;
@ -27,6 +29,7 @@ use models\main\Company;
use models\main\EmailCreationRequest;
use models\main\File;
use models\main\Group;
use models\main\IOrganizationRepository;
use models\main\Organization;
use models\summit\ISummitRegistrationPromoCodeRepository;
use models\summit\ISummitTicketTypeRepository;
@ -255,7 +258,7 @@ final class RepositoriesProvider extends ServiceProvider
});
App::singleton(
'models\main\IOrganizationRepository',
IOrganizationRepository::class,
function(){
return EntityManager::getRepository(Organization::class);
});
@ -308,5 +311,12 @@ final class RepositoriesProvider extends ServiceProvider
return EntityManager::getRepository(SummitAbstractLocation::class);
}
);
App::singleton(
ISummitLocationBannerRepository::class,
function(){
return EntityManager::getRepository(SummitLocationBanner::class);
}
);
}
}

View File

@ -0,0 +1,191 @@
<?php namespace App\Repositories\Summit;
/**
* 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 App\Models\Foundation\Summit\Locations\Banners\ScheduledSummitLocationBanner;
use App\Models\Foundation\Summit\Locations\Banners\SummitLocationBanner;
use App\Models\Foundation\Summit\Repositories\ISummitLocationBannerRepository;
use App\Repositories\SilverStripeDoctrineRepository;
use Doctrine\ORM\Tools\Pagination\Paginator;
use models\summit\Summit;
use models\summit\SummitAbstractLocation;
use utils\DoctrineInstanceOfFilterMapping;
use utils\Filter;
use utils\Order;
use utils\PagingInfo;
use utils\PagingResponse;
/**
* Class DoctrineSummitLocationBannerRepository
* @package App\Repositories\Summit
*/
final class DoctrineSummitLocationBannerRepository
extends SilverStripeDoctrineRepository
implements ISummitLocationBannerRepository
{
protected function getFilterMappings()
{
return [
'title' => 'b.title:json_string',
'content' => 'b.content:json_string',
'type' => 'b.type:json_string',
'enabled' => 'b.enabled:json_boolean',
'start_date' => 'sb.start_date:datetime_epoch',
'end_date' => 'sb.end_date:datetime_epoch',
'class_name' => new DoctrineInstanceOfFilterMapping(
"b",
[
SummitLocationBanner::ClassName => SummitLocationBanner::class,
ScheduledSummitLocationBanner::ClassName => ScheduledSummitLocationBanner::class,
]
)
];
}
/**
* @return array
*/
protected function getOrderMappings()
{
return [
'id' => 'b.id',
'title' => 'b.title',
'location_id' => 'l.id',
'start_date' => 'sb.start_date',
'end_date' => 'sb.end_date'
];
}
/**
* @return string
*/
protected function getBaseEntity()
{
return SummitLocationBanner::class;
}
/**
* @param Summit $summit
* @param PagingInfo $paging_info
* @param Filter|null $filter
* @param Order|null $order
* @return PagingResponse
*/
public function getBySummit
(
Summit $summit,
PagingInfo $paging_info,
Filter $filter = null,
Order $order = null
)
{
$query = $this->getEntityManager()
->createQueryBuilder()
->select("b")
->from(SummitLocationBanner::class, "b")
->leftJoin(ScheduledSummitLocationBanner::class, 'sb', 'WITH', 'sb.id = b.id')
->leftJoin('b.location', 'l')
->leftJoin('l.summit', 's')
->where("s.id = :summit_id");
$query->setParameter("summit_id", $summit->getId());
if(!is_null($filter)){
$filter->apply2Query($query, $this->getFilterMappings());
}
if (!is_null($order)) {
$order->apply2Query($query, $this->getOrderMappings());
} else {
//default order
$query = $query->addOrderBy("b.id",'ASC');
}
$query = $query
->setFirstResult($paging_info->getOffset())
->setMaxResults($paging_info->getPerPage());
$paginator = new Paginator($query, $fetchJoinCollection = true);
$total = $paginator->count();
$data = [];
foreach($paginator as $entity)
$data[] = $entity;
return new PagingResponse
(
$total,
$paging_info->getPerPage(),
$paging_info->getCurrentPage(),
$paging_info->getLastPage($total),
$data
);
}
/**
* @param SummitAbstractLocation $location
* @param PagingInfo $paging_info
* @param Filter|null $filter
* @param Order|null $order
* @return PagingResponse
*/
public function getBySummitLocation
(
SummitAbstractLocation $location,
PagingInfo $paging_info,
Filter $filter = null,
Order $order = null
)
{
$query = $this->getEntityManager()
->createQueryBuilder()
->select("b")
->from(SummitLocationBanner::class, "b")
->leftJoin(ScheduledSummitLocationBanner::class, 'sb', 'WITH', 'sb.id = b.id')
->leftJoin('b.location', 'l')
->leftJoin('l.summit', 's')
->where("l.id = :location_id");
$query->setParameter("location_id", $location->getId());
if(!is_null($filter)){
$filter->apply2Query($query, $this->getFilterMappings());
}
if (!is_null($order)) {
$order->apply2Query($query, $this->getOrderMappings());
} else {
//default order
$query = $query->addOrderBy("b.id",'ASC');
}
$query = $query
->setFirstResult($paging_info->getOffset())
->setMaxResults($paging_info->getPerPage());
$paginator = new Paginator($query, $fetchJoinCollection = true);
$total = $paginator->count();
$data = [];
foreach($paginator as $entity)
$data[] = $entity;
return new PagingResponse
(
$total,
$paging_info->getPerPage(),
$paging_info->getCurrentPage(),
$paging_info->getLastPage($total),
$data
);
}
}

View File

@ -41,5 +41,7 @@ final class SummitScopes
const WriteLocationsData = '%s/locations/write';
const WriteLocationBannersData = '%s/locations/banners/write';
const WriteSummitSpeakerAssistanceData = '%s/summit-speaker-assistance/write';
}

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\Locations\Banners\SummitLocationBanner;
use models\summit\SummitVenueRoom;
use models\exceptions\EntityNotFoundException;
use models\exceptions\ValidationException;
@ -106,11 +107,42 @@ interface ILocationService
* @param Summit $summit
* @param int $venue_id
* @param int $room_id
* @param array $payload
* @param array $data
* @return SummitVenueRoom
* @throws EntityNotFoundException
* @throws ValidationException
*/
public function updateVenueRoom(Summit $summit, $venue_id, $room_id, array $payload);
public function updateVenueRoom(Summit $summit, $venue_id, $room_id, array $data);
/**
* @param Summit $summit
* @param int $location_id
* @param array $data
* @return SummitLocationBanner
* @throws EntityNotFoundException
* @throws ValidationException
*/
public function addLocationBanner(Summit $summit, $location_id, array $data);
/**
* @param Summit $summit
* @param int $location_id
* @param int $banner_id
* @param array $data
* @return SummitLocationBanner
* @throws EntityNotFoundException
* @throws ValidationException
*/
public function updateLocationBanner(Summit $summit, $location_id, $banner_id, array $data);
/**
* @param Summit $summit
* @param int $location_id
* @param int $banner_id
* @return void
* @throws EntityNotFoundException
* @throws ValidationException
*/
public function deleteLocationBanner(Summit $summit, $location_id, $banner_id);
}

View File

@ -21,8 +21,11 @@ use App\Events\LocationUpdated;
use App\Events\SummitVenueRoomDeleted;
use App\Events\SummitVenueRoomInserted;
use App\Events\SummitVenueRoomUpdated;
use App\Models\Foundation\Summit\Factories\SummitLocationBannerFactory;
use App\Models\Foundation\Summit\Factories\SummitLocationFactory;
use App\Models\Foundation\Summit\Factories\SummitVenueFloorFactory;
use App\Models\Foundation\Summit\Locations\Banners\ScheduledSummitLocationBanner;
use App\Models\Foundation\Summit\Locations\Banners\SummitLocationBanner;
use App\Models\Foundation\Summit\Repositories\ISummitLocationRepository;
use App\Services\Apis\GeoCodingApiException;
use App\Services\Apis\IGeoCodingAPI;
@ -945,4 +948,113 @@ final class LocationService implements ILocationService
);
});
}
/**
* @param Summit $summit
* @param int $location_id
* @param array $data
* @return SummitLocationBanner
* @throws EntityNotFoundException
* @throws ValidationException
*/
public function addLocationBanner(Summit $summit, $location_id, array $data)
{
return $this->tx_service->transaction(function () use ($summit, $location_id, $data) {
$location = $summit->getLocation($location_id);
if(is_null($location)){
throw new EntityNotFoundException
(
trans
(
'not_found_errors.LocationService.addLocationBanner.LocationNotFound',
[
'summit_id' => $summit->getId(),
'location_id' => $location_id,
]
)
);
}
$banner = SummitLocationBannerFactory::build($summit, $location, $data);
if (is_null($banner)) {
throw new ValidationException
(
trans
(
'validation_errors.LocationService.addLocationBanner.InvalidClassName'
)
);
}
$location->addBanner($banner);
return $banner;
});
}
/**
* @param Summit $summit
* @param int $location_id
* @param int $banner_id
* @param array $data
* @return SummitLocationBanner
* @throws EntityNotFoundException
* @throws ValidationException
*/
public function updateLocationBanner(Summit $summit, $location_id, $banner_id, array $data)
{
return $this->tx_service->transaction(function () use ($summit, $location_id, $banner_id, $data) {
});
}
/**
* @param Summit $summit
* @param int $location_id
* @param int $banner_id
* @return void
* @throws EntityNotFoundException
* @throws ValidationException
*/
public function deleteLocationBanner(Summit $summit, $location_id, $banner_id)
{
return $this->tx_service->transaction(function () use ($summit, $location_id, $banner_id) {
$location = $summit->getLocation($location_id);
if(is_null($location)){
throw new EntityNotFoundException
(
trans
(
'not_found_errors.LocationService.deleteLocationBanner.LocationNotFound',
[
'summit_id' => $summit->getId(),
'location_id' => $location_id,
]
)
);
}
$banner = $location->getBannerById($banner_id);
if(is_null($banner)){
throw new EntityNotFoundException
(
trans
(
'not_found_errors.LocationService.deleteLocationBanner.BannerNotFound',
[
'location_id' => $location_id,
'banner_id' => $banner_id,
]
)
);
}
$location->removeBanner($banner);
});
}
}

View File

@ -542,7 +542,7 @@ final class SummitService implements ISummitService
(
sprintf
(
"event should last at lest %s minutes - current duration %s",
"event should last at least %s minutes - current duration %s",
self::MIN_EVENT_MINUTES,
$minutes
)

View File

@ -509,6 +509,47 @@ class ApiEndpointsSeeder extends Seeder
sprintf(SummitScopes::ReadAllSummitData, $current_realm)
],
],
// banners
[
'name' => 'get-location-banners',
'route' => '/api/v1/summits/{id}/locations/{location_id}/banners',
'http_method' => 'GET',
'scopes' => [
sprintf(SummitScopes::ReadSummitData, $current_realm),
sprintf(SummitScopes::ReadAllSummitData, $current_realm)
],
],
[
'name' => 'add-location-banner',
'route' => '/api/v1/summits/{id}/locations/{location_id}/banners',
'http_method' => 'POST',
'scopes' => [
sprintf(SummitScopes::WriteSummitData, $current_realm),
sprintf(SummitScopes::WriteLocationsData, $current_realm),
sprintf(SummitScopes::WriteLocationBannersData, $current_realm)
],
],
[
'name' => 'update-location-banner',
'route' => '/api/v1/summits/{id}/locations/{location_id}/banners/{banner_id}',
'http_method' => 'PUT',
'scopes' => [
sprintf(SummitScopes::WriteSummitData, $current_realm),
sprintf(SummitScopes::WriteLocationsData, $current_realm),
sprintf(SummitScopes::WriteLocationBannersData, $current_realm)
],
],
[
'name' => 'delete-location-banner',
'route' => '/api/v1/summits/{id}/locations/{location_id}/banners/{banner_id}',
'http_method' => 'DELETE',
'scopes' => [
sprintf(SummitScopes::WriteSummitData, $current_realm),
sprintf(SummitScopes::WriteLocationsData, $current_realm),
sprintf(SummitScopes::WriteLocationBannersData, $current_realm)
],
],
// venues
[
'name' => 'get-venues',
'route' => '/api/v1/summits/{id}/locations/venues',

View File

@ -118,11 +118,21 @@ final class ApiScopesSeeder extends Seeder
'short_description' => 'Write Attendees Data',
'description' => 'Grants write access for Attendees Data',
),
array(
[
'name' => sprintf(SummitScopes::WritePromoCodeData, $current_realm),
'short_description' => 'Write Promo Codes Data',
'description' => 'Grants write access for Promo Codes Data',
),
],
[
'name' => sprintf(SummitScopes::WriteLocationsData, $current_realm),
'short_description' => 'Write Summit Locations Data',
'description' => 'Grants write access for Summit Locations Data',
],
[
'name' => sprintf(SummitScopes::WriteLocationBannersData, $current_realm),
'short_description' => 'Write Summit Location Banners Data',
'description' => 'Grants write access for Summit Location Banners Data',
],
];
foreach ($scopes as $scope_info) {

View File

@ -26,4 +26,7 @@ return [
'LocationService.addVenueRoom.VenueNotFound' => 'venue :venue_id not found on summit :summit_id',
'LocationService.updateVenueRoom.FloorNotFound' => 'floor :floor_id does not belongs to venue :venue_id',
'LocationService.updateVenueRoom.VenueNotFound' => 'venue :venue_id not found on summit :summit_id',
'LocationService.addLocationBanner.LocationNotFound' => 'location :location_id not found on summit :summit_id',
'LocationService.deleteLocationBanner.LocationNotFound' => 'location :location_id not found on summit :summit_id',
'LocationService.deleteLocationBanner.BannerNotFound'=> 'banner :banner_id not found on location :location_id',
];

View File

@ -42,4 +42,5 @@ return [
'LocationService.addVenueRoom.InvalidClassName' => 'invalid class name',
'LocationService.addVenueRoom.LocationNameAlreadyExists' => 'there is already another location with same name for summit :summit_id',
'LocationService.updateVenueRoom.LocationNameAlreadyExists' => 'there is already another location with same name for summit :summit_id',
'LocationService.addLocationBanner.InvalidClassName' => 'invalid class name',
];

View File

@ -657,7 +657,6 @@ final class OAuth2SummitLocationsApiTest extends ProtectedApiTest
$this->assertResponseStatus(201);
$location = json_decode($content);
$this->assertTrue(!is_null($location));
$this->assertTrue($location->order == $new_order);
return $location;
}
@ -1039,4 +1038,81 @@ final class OAuth2SummitLocationsApiTest extends ProtectedApiTest
$this->assertTrue(!is_null($room));
return $room;
}
public function testAddLocationBanner($summit_id = 23, $location_id = 315){
$params = [
'id' => $summit_id,
'location_id' => $location_id
];
$data = [
'title' => str_random(16).'_banner_title',
'content' => '<span>title</span>',
'type' => \App\Models\Foundation\Summit\Locations\Banners\SummitLocationBanner::TypePrimary,
'enabled' => true,
'class_name' => \App\Models\Foundation\Summit\Locations\Banners\SummitLocationBanner::ClassName,
];
$headers = [
"HTTP_Authorization" => " Bearer " . $this->access_token,
"CONTENT_TYPE" => "application/json"
];
$response = $this->action(
"POST",
"OAuth2SummitLocationsApiController@addLocationBanner",
$params,
[],
[],
[],
$headers,
json_encode($data)
);
$content = $response->getContent();
$this->assertResponseStatus(201);
$banner = json_decode($content);
$this->assertTrue(!is_null($banner));
return $banner;
}
public function testAddLocationScheduleBanner($summit_id = 23, $location_id = 315){
$params = [
'id' => $summit_id,
'location_id' => $location_id
];
$data = [
'title' => str_random(16).'_banner_title',
'content' => '<span>title</span>',
'type' => \App\Models\Foundation\Summit\Locations\Banners\SummitLocationBanner::TypePrimary,
'enabled' => true,
'class_name' => \App\Models\Foundation\Summit\Locations\Banners\ScheduledSummitLocationBanner::ClassName,
'start_date' => 1509876000,
'end_date' => (1509876000+1000),
];
$headers = [
"HTTP_Authorization" => " Bearer " . $this->access_token,
"CONTENT_TYPE" => "application/json"
];
$response = $this->action(
"POST",
"OAuth2SummitLocationsApiController@addLocationBanner",
$params,
[],
[],
[],
$headers,
json_encode($data)
);
$content = $response->getContent();
$this->assertResponseStatus(201);
$banner = json_decode($content);
$this->assertTrue(!is_null($banner));
return $banner;
}
}