From 8b33381d0cfca9d41e688d21d1797e1cfd6684e5 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Thu, 24 Jun 2021 00:04:58 +0200 Subject: [PATCH] create draft --- Controller/ApiController.php | 261 ++++++++++++++++++++++ Controller/BackendController.php | 66 ++++++ Models/Contract.php | 136 +++++++++++ Models/ContractMapper.php | 102 +++++++++ Models/ContractType.php | 129 +++++++++++ Models/ContractTypeL11n.php | 147 ++++++++++++ Models/ContractTypeL11nMapper.php | 57 +++++ Models/ContractTypeMapper.php | 72 ++++++ Models/NullContract.php | 39 ++++ Models/NullContractType.php | 39 ++++ Models/NullContractTypeL11n.php | 39 ++++ Models/PermissionState.php | 30 +++ Theme/Backend/Lang/Navigation.en.lang.php | 1 + Theme/Backend/Lang/en.lang.php | 19 ++ Theme/Backend/contract-list.tpl.php | 99 ++++++++ Theme/Backend/contract-single.tpl.php | 46 ++++ 16 files changed, 1282 insertions(+) create mode 100644 Models/Contract.php create mode 100644 Models/ContractMapper.php create mode 100644 Models/ContractType.php create mode 100644 Models/ContractTypeL11n.php create mode 100644 Models/ContractTypeL11nMapper.php create mode 100644 Models/ContractTypeMapper.php create mode 100644 Models/NullContract.php create mode 100644 Models/NullContractType.php create mode 100644 Models/NullContractTypeL11n.php create mode 100644 Models/PermissionState.php create mode 100644 Theme/Backend/Lang/en.lang.php create mode 100644 Theme/Backend/contract-list.tpl.php create mode 100644 Theme/Backend/contract-single.tpl.php diff --git a/Controller/ApiController.php b/Controller/ApiController.php index 94dc7f7..d89b25d 100644 --- a/Controller/ApiController.php +++ b/Controller/ApiController.php @@ -15,6 +15,10 @@ declare(strict_types=1); namespace Modules\ContractManagement\Controller; use Modules\Admin\Models\NullAccount; +use Modules\ContractManagement\Models\ContractType; +use Modules\ContractManagement\Models\ContractTypeMapper; +use Modules\ContractManagement\Models\ContractTypeL11n; +use Modules\ContractManagement\Models\ContractTypeL11nMapper; use phpOMS\Message\Http\HttpResponse; use phpOMS\Message\Http\RequestStatusCode; use phpOMS\Message\NotificationLevel; @@ -22,6 +26,11 @@ use phpOMS\Message\RequestAbstract; use phpOMS\Message\ResponseAbstract; use phpOMS\Model\Message\FormValidation; use phpOMS\Utils\Parser\Markdown\Markdown; +use phpOMS\Message\Http\HttpRequest; +use Modules\Media\Models\PathSettings; +use Modules\ContractManagement\Models\Contract; +use Modules\ContractManagement\Models\NullContractType; +use Modules\ContractManagement\Models\ContractMapper; /** * Api controller for the contracts module. @@ -33,4 +42,256 @@ use phpOMS\Utils\Parser\Markdown\Markdown; */ final class ApiController extends Controller { + /** + * Api method to create a contract + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param mixed $data Generic data + * + * @return void + * + * @api + * + * @since 1.0.0 + */ + public function apiContractCreate(RequestAbstract $request, ResponseAbstract $response, $data = null) : void + { + if (!empty($val = $this->validateContractCreate($request))) { + $response->set('contract_create', new FormValidation($val)); + $response->header->status = RequestStatusCode::R_400; + + return; + } + + $contract = $this->createContractFromRequest($request); + $this->createModel($request->header->account, $contract, ContractMapper::class, 'contract', $request->getOrigin()); + $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Contract', 'Contract successfully created', $contract); + } + + /** + * Validate contract create request + * + * @param RequestAbstract $request Request + * + * @return array + * + * @since 1.0.0 + */ + private function validateContractCreate(RequestAbstract $request) : array + { + $val = []; + if (($val['title'] = empty($request->getData('title'))) + || ($val['start'] = empty($request->getData('start'))) + || ($val['duration'] = empty($request->getData('duration'))) + || ($val['type'] = empty($request->getData('type'))) + ) { + return $val; + } + + return []; + } + + /** + * Method to create item l11n from request. + * + * @param RequestAbstract $request Request + * + * @return Contract + * + * @since 1.0.0 + */ + private function createContractFromRequest(RequestAbstract $request) : Contract + { + $contract = new Contract(); + $contract->title = (string) ($request->getData('title') ?? ''); + $contract->description = (string) ($request->getData('description') ?? ''); + $contract->type = new NullContractType((int) ($request->getData('type') ?? 0)); + $contract->start = new \DateTime($request->getData('start') ?? 'now'); + + if (!empty($request->getData('end'))) { + $contract->end = new \DateTime($request->getData('end')); + } + + return $contract; + } + + /** + * Api method to create a contract document + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param mixed $data Generic data + * + * @return void + * + * @api + * + * @since 1.0.0 + */ + public function apiContractDocumentCreate(RequestAbstract $request, ResponseAbstract $response, $data = null) : void + { + $uploadedFiles = $request->getFiles() ?? []; + + if (empty($uploadedFiles)) { + $this->fillJsonResponse($request, $response, NotificationLevel::ERROR, 'Contract', 'Invalid contract image', $uploadedFiles); + $response->header->status = RequestStatusCode::R_400; + + return; + } + + $uploaded = $this->app->moduleManager->get('Media')->uploadFiles( + $request->getData('name') ?? '', + $uploadedFiles, + $request->header->account, + __DIR__ . '/../../../Modules/Media/Files/Modules/ContractManagement/Contracts/' . ($request->getData('contract_title') ?? '0'), + '/Modules/ContractManagement/Contracts/' . ($request->getData('contract_title') ?? '0'), + $request->getData('type') ?? '', + '', + '', + PathSettings::FILE_PATH + ); + + $this->createModelRelation( + $request->header->account, + (int) $request->getData('contract'), + \reset($uploaded)->getId(), + ContractMapper::class, 'files', '', $request->getOrigin() + ); + + $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Image', 'Image successfully updated', $uploaded); + } + + /** + * Api method to create item attribute type + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param mixed $data Generic data + * + * @return void + * + * @api + * + * @since 1.0.0 + */ + public function apiContractTypeCreate(RequestAbstract $request, ResponseAbstract $response, $data = null) : void + { + if (!empty($val = $this->validateContractTypeCreate($request))) { + $response->set('contract_type_create', new FormValidation($val)); + $response->header->status = RequestStatusCode::R_400; + + return; + } + + $contractType = $this->createContractTypeFromRequest($request); + $contractType->setL11n($request->getData('title'), $request->getData('language')); + $this->createModel($request->header->account, $contractType, ContractTypeMapper::class, 'contract_type', $request->getOrigin()); + + $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Contract type', 'Contract type successfully created', $contractType); + } + + /** + * Method to create item attribute from request. + * + * @param RequestAbstract $request Request + * + * @return ContractType + * + * @since 1.0.0 + */ + private function createContractTypeFromRequest(RequestAbstract $request) : ContractType + { + $contractType = new ContractType(); + $contractType->name = (string) ($request->getData('name') ?? ''); + + return $contractType; + } + + /** + * Validate item attribute create request + * + * @param RequestAbstract $request Request + * + * @return array + * + * @since 1.0.0 + */ + private function validateContractTypeCreate(RequestAbstract $request) : array + { + $val = []; + if (($val['name'] = empty($request->getData('name'))) + || ($val['title'] = empty($request->getData('title'))) + ) { + return $val; + } + + return []; + } + + /** + * Api method to create item l11n type + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param mixed $data Generic data + * + * @return void + * + * @api + * + * @since 1.0.0 + */ + public function apiContractTypeL11nCreate(RequestAbstract $request, ResponseAbstract $response, $data = null) : void + { + if (!empty($val = $this->validateContractTypeL11nCreate($request))) { + $response->set('contract_type_create', new FormValidation($val)); + $response->header->status = RequestStatusCode::R_400; + + return; + } + + $itemL11nType = $this->createContractTypeL11nFromRequest($request); + $this->createModel($request->header->account, $itemL11nType, ContractTypeL11nMapper::class, 'contract_type', $request->getOrigin()); + $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Contract type', 'Contract localization type successfully created', $itemL11nType); + } + + /** + * Method to create item l11n type from request. + * + * @param RequestAbstract $request Request + * + * @return ContractTypeL11n + * + * @since 1.0.0 + */ + private function createContractTypeL11nFromRequest(RequestAbstract $request) : ContractTypeL11n + { + $typeL11n = new ContractTypeL11n( + (int) ($request->getData('type') ?? 0), + (string) ($request->getData('title') ?? ''), + $request->getData('language') ?? $request->getLanguage() + ); + + return $typeL11n; + } + + /** + * Validate item l11n type create request + * + * @param RequestAbstract $request Request + * + * @return array + * + * @since 1.0.0 + */ + private function validateContractTypeL11nCreate(RequestAbstract $request) : array + { + $val = []; + if (($val['title'] = empty($request->getData('title')))) { + return $val; + } + + return []; + } } diff --git a/Controller/BackendController.php b/Controller/BackendController.php index 2364ec7..d0c5322 100644 --- a/Controller/BackendController.php +++ b/Controller/BackendController.php @@ -22,6 +22,7 @@ use phpOMS\Message\Http\RequestStatusCode; use phpOMS\Message\RequestAbstract; use phpOMS\Message\ResponseAbstract; use phpOMS\Views\View; +use Modules\ContractManagement\Models\ContractMapper; /** * Backend controller for the contracts module. @@ -35,4 +36,69 @@ use phpOMS\Views\View; */ final class BackendController extends Controller { + /** + * Routing end-point for application behaviour. + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param mixed $data Generic data + * + * @return RenderableInterface + * + * @since 1.0.0 + * @codeCoverageIgnore + */ + public function viewContractList(RequestAbstract $request, ResponseAbstract $response, $data = null) : RenderableInterface + { + $view = new View($this->app->l11nManager, $request, $response); + + $view->setTemplate('/Modules/ContractManagement/Theme/Backend/contract-list'); + $view->addData('nav', $this->app->moduleManager->get('Navigation')->createNavigationMid(1007901001, $request, $response)); + + if ($request->getData('ptype') === 'p') { + $view->setData('contracts', + ContractMapper::with('language', $response->getLanguage()) + ::getBeforePivot((int) ($request->getData('id') ?? 0), null, 25) + ); + } elseif ($request->getData('ptype') === 'n') { + $view->setData('contracts', + ContractMapper::with('language', $response->getLanguage()) + ::getAfterPivot((int) ($request->getData('id') ?? 0), null, 25) + ); + } else { + $view->setData('contracts', + ContractMapper::with('language', $response->getLanguage()) + ::getAfterPivot(0, null, 25) + ); + } + + return $view; + } + + /** + * Routing end-point for application behaviour. + * + * @param RequestAbstract $request Request + * @param ResponseAbstract $response Response + * @param mixed $data Generic data + * + * @return RenderableInterface + * + * @since 1.0.0 + * @codeCoverageIgnore + */ + public function viewContract(RequestAbstract $request, ResponseAbstract $response, $data = null) : RenderableInterface + { + $view = new View($this->app->l11nManager, $request, $response); + + $view->setTemplate('/Modules/ContractManagement/Theme/Backend/contract-single'); + $view->addData('nav', $this->app->moduleManager->get('Navigation')->createNavigationMid(1007901001, $request, $response)); + + $view->addData('contract', ContractMapper::get((int) $request->getData('id'))); + + $editor = new \Modules\Editor\Theme\Backend\Components\Editor\BaseView($this->app->l11nManager, $request, $response); + $view->addData('editor', $editor); + + return $view; + } } diff --git a/Models/Contract.php b/Models/Contract.php new file mode 100644 index 0000000..ba3db57 --- /dev/null +++ b/Models/Contract.php @@ -0,0 +1,136 @@ +createdAt = new \DateTimeImmutable('now'); + } + + /** + * Get id + * + * @return int + * + * @since 1.0.0 + */ + public function getId() : int + { + return $this->id; + } + + /** + * Get files + * + * @return Media[] + * + * @since 1.0.0 + */ + public function getFiles() : array + { + return $this->files; + } + + /** + * Add media to item + * + * @param Media $media Media + * + * @return void + * + * @since 1.0.0 + */ + public function addFile(Media $media) : void + { + $this->files[] = $media; + } + + /** + * {@inheritdoc} + */ + public function toArray() : array + { + return []; + } + + /** + * {@inheritdoc} + */ + public function jsonSerialize() + { + return $this->toArray(); + } +} diff --git a/Models/ContractMapper.php b/Models/ContractMapper.php new file mode 100644 index 0000000..653ae15 --- /dev/null +++ b/Models/ContractMapper.php @@ -0,0 +1,102 @@ + + * @since 1.0.0 + */ + protected static array $columns = [ + 'contractmgmt_contract_id' => ['name' => 'contractmgmt_contract_id', 'type' => 'int', 'internal' => 'id'], + 'contractmgmt_contract_title' => ['name' => 'contractmgmt_contract_title', 'type' => 'string', 'internal' => 'title', 'autocomplete' => true], + 'contractmgmt_contract_description' => ['name' => 'contractmgmt_contract_description', 'type' => 'string', 'internal' => 'description'], + 'contractmgmt_contract_costs' => ['name' => 'contractmgmt_contract_costs', 'type' => 'Serializable', 'internal' => 'costs'], + 'contractmgmt_contract_duration' => ['name' => 'contractmgmt_contract_duration', 'type' => 'int', 'internal' => 'duration'], + 'contractmgmt_contract_warning' => ['name' => 'contractmgmt_contract_warning', 'type' => 'int', 'internal' => 'warning'], + 'contractmgmt_contract_start' => ['name' => 'contractmgmt_contract_start', 'type' => 'DateTime', 'internal' => 'start'], + 'contractmgmt_contract_end' => ['name' => 'contractmgmt_contract_end', 'type' => 'DateTime', 'internal' => 'end'], + 'contractmgmt_contract_responsible' => ['name' => 'contractmgmt_contract_responsible', 'type' => 'int', 'internal' => 'responsible'], + 'contractmgmt_contract_type' => ['name' => 'contractmgmt_contract_type', 'type' => 'int', 'internal' => 'type'], + 'contractmgmt_contract_created_at' => ['name' => 'contractmgmt_contract_created_at', 'type' => 'DateTimeImmutable', 'internal' => 'createdAt'], + ]; + + /** + * Primary table. + * + * @var string + * @since 1.0.0 + */ + protected static string $table = 'contractmgmt_contract'; + + /** + * Primary field name. + * + * @var string + * @since 1.0.0 + */ + protected static string $primaryField = 'contractmgmt_contract_id'; + + /** + * Created at. + * + * @var string + * @since 1.0.0 + */ + protected static string $createdAt = 'contractmgmt_contract_created_at'; + + /** + * Has one relation. + * + * @var array + * @since 1.0.0 + */ + protected static array $ownsOne = [ + 'type' => [ + 'mapper' => ContractTypeMapper::class, + 'external' => 'contractmgmt_contract_type', + ], + ]; + + /** + * Has many relation. + * + * @var array + * @since 1.0.0 + */ + protected static array $hasMany = [ + 'files' => [ + 'mapper' => MediaMapper::class, /* mapper of the related object */ + 'table' => 'contractmgmt_contract_media', /* table of the related object, null if no relation table is used (many->1) */ + 'external' => 'contractmgmt_contract_media_media', + 'self' => 'contractmgmt_contract_media_contract', + ], + ]; +} diff --git a/Models/ContractType.php b/Models/ContractType.php new file mode 100644 index 0000000..0f60880 --- /dev/null +++ b/Models/ContractType.php @@ -0,0 +1,129 @@ +setL11n($name); + } + } + + /** + * Get id + * + * @return int + * + * @since 1.0.0 + */ + public function getId() : int + { + return $this->id; + } + + /** + * Set l11n + * + * @param string|ContractTypeL11n $l11n Tag article l11n + * @param string $lang Language + * + * @return void + * + * @since 1.0.0 + */ + public function setL11n($l11n, string $lang = ISO639x1Enum::_EN) : void + { + if ($l11n instanceof ContractTypeL11n) { + $this->l11n = $l11n; + } elseif (\is_string($l11n)) { + $this->l11n = new ContractTypeL11n(); + $this->l11n->title = $l11n; + $this->l11n->setLanguage($lang); + } elseif ($this->l11n instanceof ContractTypeL11n && \is_string($l11n)) { + $this->l11n->title = $l11n; + } + } + + /** + * @return string + * + * @since 1.0.0 + */ + public function getL11n() : string + { + return $this->l11n instanceof ContractTypeL11n ? $this->l11n->title : $this->l11n; + } + + /** + * {@inheritdoc} + */ + public function toArray() : array + { + return []; + } + + /** + * {@inheritdoc} + */ + public function jsonSerialize() + { + return $this->toArray(); + } +} diff --git a/Models/ContractTypeL11n.php b/Models/ContractTypeL11n.php new file mode 100644 index 0000000..7092063 --- /dev/null +++ b/Models/ContractTypeL11n.php @@ -0,0 +1,147 @@ +type = $type; + $this->title = $title; + $this->language = $language; + } + + /** + * Get id + * + * @return int + * + * @since 1.0.0 + */ + public function getId() : int + { + return $this->id; + } + + /** + * Get attribute type + * + * @return int|ContractType + * + * @since 1.0.0 + */ + public function getType() : int | ContractType + { + return $this->type; + } + + /** + * Set type. + * + * @param int $type Type id + * + * @return void + * + * @since 1.0.0 + */ + public function setType(int $type) : void + { + $this->type = $type; + } + + /** + * Set language + * + * @param string $language Language + * + * @return void + * + * @since 1.0.0 + */ + public function setLanguage(string $language) : void + { + $this->language = $language; + } + + /** + * {@inheritdoc} + */ + public function toArray() : array + { + return []; + } + + /** + * {@inheritdoc} + */ + public function jsonSerialize() + { + return $this->toArray(); + } +} diff --git a/Models/ContractTypeL11nMapper.php b/Models/ContractTypeL11nMapper.php new file mode 100644 index 0000000..4a2bc42 --- /dev/null +++ b/Models/ContractTypeL11nMapper.php @@ -0,0 +1,57 @@ + + * @since 1.0.0 + */ + protected static array $columns = [ + 'contractmgmt_type_l11n_id' => ['name' => 'contractmgmt_type_l11n_id', 'type' => 'int', 'internal' => 'id'], + 'contractmgmt_type_l11n_title' => ['name' => 'contractmgmt_type_l11n_title', 'type' => 'string', 'internal' => 'title', 'autocomplete' => true], + 'contractmgmt_type_l11n_type' => ['name' => 'contractmgmt_type_l11n_type', 'type' => 'int', 'internal' => 'type'], + 'contractmgmt_type_l11n_lang' => ['name' => 'contractmgmt_type_l11n_lang', 'type' => 'string', 'internal' => 'language'], + ]; + + /** + * Primary table. + * + * @var string + * @since 1.0.0 + */ + protected static string $table = 'contractmgmt_type_l11n'; + + /** + * Primary field name. + * + * @var string + * @since 1.0.0 + */ + protected static string $primaryField = 'contractmgmt_type_l11n_id'; +} diff --git a/Models/ContractTypeMapper.php b/Models/ContractTypeMapper.php new file mode 100644 index 0000000..e4e5c10 --- /dev/null +++ b/Models/ContractTypeMapper.php @@ -0,0 +1,72 @@ + + * @since 1.0.0 + */ + protected static array $columns = [ + 'contractmgmt_type_id' => ['name' => 'contractmgmt_type_id', 'type' => 'int', 'internal' => 'id'], + 'contractmgmt_type_name' => ['name' => 'contractmgmt_type_name', 'type' => 'string', 'internal' => 'name', 'autocomplete' => true], + ]; + + /** + * Has many relation. + * + * @var array + * @since 1.0.0 + */ + protected static array $hasMany = [ + 'l11n' => [ + 'mapper' => ContractTypeL11nMapper::class, + 'table' => 'contractmgmt_type_l11n', + 'self' => 'contractmgmt_type_l11n_type', + 'column' => 'title', + 'conditional' => true, + 'external' => null, + ] + ]; + + /** + * Primary table. + * + * @var string + * @since 1.0.0 + */ + protected static string $table = 'contractmgmt_type'; + + /** + * Primary field name. + * + * @var string + * @since 1.0.0 + */ + protected static string $primaryField = 'contractmgmt_type_id'; +} diff --git a/Models/NullContract.php b/Models/NullContract.php new file mode 100644 index 0000000..87a990f --- /dev/null +++ b/Models/NullContract.php @@ -0,0 +1,39 @@ +id = $id; + parent::__construct(); + } +} diff --git a/Models/NullContractType.php b/Models/NullContractType.php new file mode 100644 index 0000000..f990d81 --- /dev/null +++ b/Models/NullContractType.php @@ -0,0 +1,39 @@ +id = $id; + parent::__construct(); + } +} diff --git a/Models/NullContractTypeL11n.php b/Models/NullContractTypeL11n.php new file mode 100644 index 0000000..fbcb6a9 --- /dev/null +++ b/Models/NullContractTypeL11n.php @@ -0,0 +1,39 @@ +id = $id; + parent::__construct(); + } +} diff --git a/Models/PermissionState.php b/Models/PermissionState.php new file mode 100644 index 0000000..a7e0c63 --- /dev/null +++ b/Models/PermissionState.php @@ -0,0 +1,30 @@ + [ 'Create' => 'Create', 'Contract' => 'Contract', + 'Contracts' => 'Contracts', 'List' => 'List', ]]; diff --git a/Theme/Backend/Lang/en.lang.php b/Theme/Backend/Lang/en.lang.php new file mode 100644 index 0000000..d7a021d --- /dev/null +++ b/Theme/Backend/Lang/en.lang.php @@ -0,0 +1,19 @@ + [ + 'Contract' => 'Contract', + 'Contracts' => 'Contracts', + 'Title' => 'Title', +]]; diff --git a/Theme/Backend/contract-list.tpl.php b/Theme/Backend/contract-list.tpl.php new file mode 100644 index 0000000..0da1544 --- /dev/null +++ b/Theme/Backend/contract-list.tpl.php @@ -0,0 +1,99 @@ +getData('contracts') ?? []; + +$previous = empty($contracts) ? '{/prefix}contract/list' : '{/prefix}contract/list?{?}&id=' . \reset($contracts)->getId() . '&ptype=p'; +$next = empty($contracts) ? '{/prefix}contract/list' : '{/prefix}contract/list?{?}&id=' . \end($contracts)->getId() . '&ptype=n'; + +$now = new \DateTime('now'); + +echo $this->getData('nav')->render(); ?> + +
+
+
+
getHtml('Contracts'); ?>
+ + + + + $value) : + $url = UriFactory::build('{/prefix}contract/single?{?}&id=' . $value->getId()); + + $type = 'ok'; + if ($value->end->getTimestamp() < $now->getTimestamp() && $value->end->getTimestamp() + 129600 > $now->getTimestamp()) { + $type = 'error'; + } elseif ($value->end->getTimestamp() < $now->getTimestamp()) { + $type = 'info'; + } elseif ($value->end->getTimestamp() + 129600 < $now->getTimestamp()) { + $type = 'warning'; + } + ?> + +
getHtml('ID', '0', '0'); ?> + + + + getHtml('Title'); ?> + + + + getHtml('End'); ?> + + + +
getId(); ?> + printHtml($value->title); ?> + end !== null ? $value->end->format('Y-m-d') : ''; ?> + +
+ +
+
+
diff --git a/Theme/Backend/contract-single.tpl.php b/Theme/Backend/contract-single.tpl.php new file mode 100644 index 0000000..33c5d8e --- /dev/null +++ b/Theme/Backend/contract-single.tpl.php @@ -0,0 +1,46 @@ +getData('contract'); + +echo $this->getData('nav')->render(); ?> + +
+
+ +
+
+ request->uri->fragment === 'c-tab-1' ? ' checked' : ''; ?>> +
+
+
+
+ + request->uri->fragment === 'c-tab-2' ? ' checked' : ''; ?>> +
+
+
+
+
+