2020-06-09 15:57:16 +02:00
< ? php
namespace App\Controller\Component ;
use Cake\Controller\Component ;
use Cake\Error\Debugger ;
2020-08-07 21:47:04 +02:00
use Cake\Utility\Hash ;
2020-06-09 15:57:16 +02:00
use Cake\Utility\Inflector ;
2021-11-04 08:10:32 +01:00
use Cake\Utility\Text ;
2021-01-14 11:33:51 +01:00
use Cake\View\ViewBuilder ;
2021-08-26 16:12:55 +02:00
use Cake\ORM\TableRegistry ;
2022-10-28 09:13:15 +02:00
use Cake\ORM\Query ;
2021-11-08 15:56:39 +01:00
use Cake\Routing\Router ;
2021-06-23 11:12:14 +02:00
use Cake\Http\Exception\MethodNotAllowedException ;
use Cake\Http\Exception\NotFoundException ;
2021-11-09 08:59:17 +01:00
use Cake\Collection\Collection ;
2021-11-10 09:06:39 +01:00
use App\Utility\UI\IndexSetting ;
2020-06-09 15:57:16 +02:00
class CRUDComponent extends Component
{
2021-06-28 23:27:21 +02:00
public $components = [ 'RestResponse' ];
2020-06-09 15:57:16 +02:00
public function initialize ( array $config ) : void
{
$this -> Controller = $this -> getController ();
$this -> Table = $config [ 'table' ];
$this -> request = $config [ 'request' ];
$this -> TableAlias = $this -> Table -> getAlias ();
2020-12-07 14:17:10 +01:00
$this -> ObjectAlias = Inflector :: singularize ( $this -> TableAlias );
2020-09-28 01:25:07 +02:00
$this -> MetaFields = $config [ 'MetaFields' ];
$this -> MetaTemplates = $config [ 'MetaTemplates' ];
2020-06-09 15:57:16 +02:00
}
2020-06-19 00:34:51 +02:00
public function index ( array $options ) : void
2020-06-09 15:57:16 +02:00
{
2020-06-19 00:34:51 +02:00
if ( ! empty ( $options [ 'quickFilters' ])) {
if ( empty ( $options [ 'filters' ])) {
$options [ 'filters' ] = [];
}
$options [ 'filters' ][] = 'quickFilter' ;
2021-11-10 15:28:09 +01:00
} else {
$options [ 'quickFilters' ] = [];
2020-06-19 00:34:51 +02:00
}
2021-02-26 10:36:06 +01:00
$options [ 'filters' ][] = 'filteringLabel' ;
2021-08-30 15:11:21 +02:00
if ( $this -> taggingSupported ()) {
$options [ 'filters' ][] = 'filteringTags' ;
}
2021-03-10 09:35:26 +01:00
$optionFilters = empty ( $options [ 'filters' ]) ? [] : $options [ 'filters' ];
foreach ( $optionFilters as $i => $filter ) {
$optionFilters [] = " { $filter } != " ;
2022-10-27 15:56:39 +02:00
$optionFilters [] = " { $filter } >= " ;
$optionFilters [] = " { $filter } <= " ;
2021-03-10 09:35:26 +01:00
}
$params = $this -> Controller -> ParamHandler -> harvestParams ( $optionFilters );
2022-03-09 08:55:59 +01:00
$params = $this -> fakeContextFilter ( $options , $params );
2020-06-09 15:57:16 +02:00
$query = $this -> Table -> find ();
2021-06-01 07:47:22 +02:00
if ( ! empty ( $options [ 'filterFunction' ])) {
$query = $options [ 'filterFunction' ]( $query );
}
2021-02-26 10:36:06 +01:00
$query = $this -> setFilters ( $params , $query , $options );
2021-11-10 15:28:09 +01:00
$query = $this -> setQuickFilters ( $params , $query , $options );
2021-11-24 01:30:28 +01:00
if ( ! empty ( $options [ 'conditions' ])) {
$query -> where ( $options [ 'conditions' ]);
}
2020-06-19 00:34:51 +02:00
if ( ! empty ( $options [ 'contain' ])) {
$query -> contain ( $options [ 'contain' ]);
}
2021-08-30 15:11:21 +02:00
if ( $this -> taggingSupported ()) {
$query -> contain ( 'Tags' );
}
2021-06-01 07:47:22 +02:00
if ( ! empty ( $options [ 'fields' ])) {
$query -> select ( $options [ 'fields' ]);
}
2022-09-20 15:31:31 +02:00
if ( ! empty ( $options [ 'order' ])) {
2023-02-20 10:17:20 +01:00
$orderFields = array_keys ( $options [ 'order' ]);
if ( $this -> _validOrderFields ( $orderFields )) {
$query -> order ( $options [ 'order' ]);
$this -> Controller -> paginate [ 'order' ] = $options [ 'order' ];
}
2022-09-20 15:31:31 +02:00
}
2020-06-09 15:57:16 +02:00
if ( $this -> Controller -> ParamHandler -> isRest ()) {
2022-11-04 09:33:39 +01:00
if ( $this -> metaFieldsSupported ()) {
$query = $this -> includeRequestedMetaFields ( $query );
}
2020-06-09 15:57:16 +02:00
$data = $query -> all ();
2021-11-17 15:47:32 +01:00
if ( isset ( $options [ 'hidden' ])) {
$data -> each ( function ( $value , $key ) use ( $options ) {
$hidden = is_array ( $options [ 'hidden' ]) ? $options [ 'hidden' ] : [ $options [ 'hidden' ]];
$value -> setHidden ( $hidden );
return $value ;
});
}
2021-04-30 23:59:53 +02:00
if ( isset ( $options [ 'afterFind' ])) {
2021-11-17 15:47:32 +01:00
$function = $options [ 'afterFind' ];
2022-01-20 11:57:48 +01:00
if ( is_callable ( $function )) {
2022-02-23 09:58:55 +01:00
$data = $data -> map ( function ( $value , $key ) use ( $function ) {
2021-11-17 15:47:32 +01:00
return $function ( $value );
2022-02-23 09:58:55 +01:00
}) -> filter ( function ( $value ) {
return $value !== false ;
2021-11-17 15:47:32 +01:00
});
2021-04-30 23:59:53 +02:00
} else {
2021-11-17 15:47:32 +01:00
$t = $this -> Table ;
2022-02-23 09:58:55 +01:00
$data = $data -> map ( function ( $value , $key ) use ( $t , $function ) {
2021-11-17 15:47:32 +01:00
return $t -> $function ( $value );
2022-02-23 09:58:55 +01:00
}) -> filter ( function ( $value ) {
return $value !== false ;
2021-11-17 15:47:32 +01:00
});
2021-04-30 23:59:53 +02:00
}
}
2022-11-04 09:33:39 +01:00
if ( $this -> metaFieldsSupported ()) {
$metaTemplates = $this -> getMetaTemplates () -> toArray ();
$data = $data -> map ( function ( $value , $key ) use ( $metaTemplates ) {
return $this -> attachMetaTemplatesIfNeeded ( $value , $metaTemplates );
});
}
2021-06-17 08:54:09 +02:00
$this -> Controller -> restResponsePayload = $this -> RestResponse -> viewData ( $data , 'json' );
2020-06-09 15:57:16 +02:00
} else {
2021-11-10 09:06:39 +01:00
if ( $this -> metaFieldsSupported ()) {
$query = $this -> includeRequestedMetaFields ( $query );
}
2022-11-14 15:55:07 +01:00
$this -> setRequestedEntryAmount ();
2022-08-17 14:00:38 +02:00
$data = $this -> Controller -> paginate ( $query , $this -> Controller -> paginate ? ? []);
2021-04-30 23:59:53 +02:00
if ( isset ( $options [ 'afterFind' ])) {
2021-11-17 15:47:32 +01:00
$function = $options [ 'afterFind' ];
2022-01-20 11:57:48 +01:00
if ( is_callable ( $function )) {
2022-02-23 09:58:55 +01:00
$data = $data -> map ( function ( $value , $key ) use ( $function ) {
2021-11-17 15:47:32 +01:00
return $function ( $value );
2022-02-23 09:58:55 +01:00
}) -> filter ( function ( $value ) {
return $value !== false ;
2021-11-17 15:47:32 +01:00
});
2021-04-30 23:59:53 +02:00
} else {
2021-11-17 15:47:32 +01:00
$t = $this -> Table ;
2022-02-23 09:58:55 +01:00
$data = $data -> map ( function ( $value , $key ) use ( $t , $function ) {
2021-11-17 15:47:32 +01:00
return $t -> $function ( $value );
2022-02-23 09:58:55 +01:00
}) -> filter ( function ( $value ) {
return $value !== false ;
2021-11-17 15:47:32 +01:00
});
2021-04-30 23:59:53 +02:00
}
}
2021-08-30 15:11:21 +02:00
$this -> setFilteringContext ( $options [ 'contextFilters' ] ? ? [], $params );
2021-11-10 09:06:39 +01:00
if ( $this -> metaFieldsSupported ()) {
$data = $data -> toArray ();
$metaTemplates = $this -> getMetaTemplates () -> toArray ();
foreach ( $data as $i => $row ) {
$data [ $i ] = $this -> attachMetaTemplatesIfNeeded ( $row , $metaTemplates );
}
$this -> Controller -> set ( 'meta_templates' , $metaTemplates );
2023-01-17 09:17:49 +01:00
$this -> Controller -> set ( 'meta_templates_enabled' , array_filter ( $metaTemplates , function ( $template ) {
return $template [ 'enabled' ];
}));
2021-11-10 09:06:39 +01:00
}
2021-11-17 17:04:39 +01:00
if ( true ) { // check if stats are requested
$modelStatistics = [];
if ( $this -> Table -> hasBehavior ( 'Timestamp' )) {
$modelStatistics = $this -> Table -> getActivityStatisticsForModel (
$this -> Table ,
! is_numeric ( $this -> request -> getQuery ( 'statistics_days' )) ? 7 : $this -> request -> getQuery ( 'statistics_days' )
);
}
if ( ! empty ( $options [ 'statisticsFields' ])) {
$statIncludeRemaining = $this -> request -> getQuery ( 'statistics_include_remainging' , true );
if ( is_string ( $statIncludeRemaining )) {
$statIncludeRemaining = $statIncludeRemaining == 'true' ? true : false ;
}
$statIgnoreNull = $this -> request -> getQuery ( 'statistics_ignore_null' , true );
if ( is_string ( $statIgnoreNull )) {
$statIgnoreNull = $statIgnoreNull == 'true' ? true : false ;
}
2022-09-19 01:46:57 +02:00
$statistics_entry_amount = $this -> request -> getQuery ( 'statistics_entry_amount' );
if (
! is_numeric ( $statistics_entry_amount ) ||
intval ( $statistics_entry_amount ) <= 0
) {
$statistics_entry_amount = 5 ;
} else {
$statistics_entry_amount = intval ( $statistics_entry_amount );
}
2021-11-17 17:04:39 +01:00
$statsOptions = [
2022-09-19 01:46:57 +02:00
'limit' => $statistics_entry_amount ,
2021-11-17 17:04:39 +01:00
'includeOthers' => $statIncludeRemaining ,
'ignoreNull' => $statIgnoreNull ,
];
$modelStatistics [ 'usage' ] = $this -> Table -> getStatisticsUsageForModel (
$this -> Table ,
$options [ 'statisticsFields' ],
$statsOptions
);
}
2021-11-15 11:51:47 +01:00
$this -> Controller -> set ( 'modelStatistics' , $modelStatistics );
}
2021-11-10 09:06:39 +01:00
$this -> Controller -> set ( 'model' , $this -> Table );
2020-06-09 15:57:16 +02:00
$this -> Controller -> set ( 'data' , $data );
}
}
2021-03-10 09:43:36 +01:00
public function filtering () : void
{
2021-08-30 15:11:21 +02:00
if ( $this -> taggingSupported ()) {
$this -> Controller -> set ( 'taggingEnabled' , true );
$this -> setAllTags ();
2022-01-20 13:54:17 +01:00
} else {
$this -> Controller -> set ( 'taggingEnabled' , false );
2021-08-30 15:11:21 +02:00
}
2021-11-10 12:07:27 +01:00
if ( $this -> metaFieldsSupported ()) {
$metaTemplates = $this -> getMetaTemplates () -> toArray ();
$this -> Controller -> set ( 'metaFieldsEnabled' , true );
$this -> Controller -> set ( 'metaTemplates' , $metaTemplates );
2022-10-25 10:23:11 +02:00
$typeHandlers = $this -> Table -> getBehavior ( 'MetaFields' ) -> getTypeHandlers ();
$typeHandlersOperators = [];
foreach ( $typeHandlers as $type => $handler ) {
$typeHandlersOperators [ $type ] = $handler :: OPERATORS ;
}
$this -> Controller -> set ( 'typeHandlersOperators' , $typeHandlersOperators );
2022-01-20 13:54:17 +01:00
} else {
$this -> Controller -> set ( 'metaFieldsEnabled' , false );
2021-11-10 12:07:27 +01:00
}
2021-10-21 10:20:07 +02:00
$filters = ! empty ( $this -> Controller -> filterFields ) ? $this -> Controller -> filterFields : [];
2022-10-27 15:56:39 +02:00
$typeMap = $this -> Table -> getSchema () -> typeMap ();
$associatedtypeMap = ! empty ( $this -> Controller -> filterFields ) ? $this -> _getAssociatedTypeMap () : [];
$typeMap = array_merge (
$this -> Table -> getSchema () -> typeMap (),
$associatedtypeMap
);
$typeMap = array_filter ( $typeMap , function ( $field ) use ( $filters ) {
return in_array ( $field , $filters );
}, ARRAY_FILTER_USE_KEY );
$this -> Controller -> set ( 'typeMap' , $typeMap );
2021-03-10 09:43:36 +01:00
$this -> Controller -> set ( 'filters' , $filters );
$this -> Controller -> viewBuilder () -> setLayout ( 'ajax' );
$this -> Controller -> render ( '/genericTemplates/filters' );
}
2021-04-30 23:59:53 +02:00
2021-01-11 12:48:58 +01:00
/**
* getResponsePayload Returns the adaquate response payload based on the request context
*
* @ return false or Array
*/
public function getResponsePayload ()
{
if ( $this -> Controller -> ParamHandler -> isRest ()) {
return $this -> Controller -> restResponsePayload ;
} else if ( $this -> Controller -> ParamHandler -> isAjax () && $this -> request -> is ([ 'post' , 'put' ])) {
2021-01-12 10:16:58 +01:00
return $this -> Controller -> ajaxResponsePayload ;
2021-01-11 12:48:58 +01:00
}
return false ;
}
2020-06-09 15:57:16 +02:00
2022-03-01 15:21:56 +01:00
private function getMetaTemplates ( array $metaTemplateConditions = [])
2020-09-28 01:25:07 +02:00
{
2020-12-07 09:52:35 +01:00
$metaTemplates = [];
2021-11-10 09:02:51 +01:00
if ( ! $this -> metaFieldsSupported ()) {
2021-11-09 08:59:17 +01:00
throw new \Exception ( __ ( " Table { $this -> TableAlias } does not support meta_fields " ));
}
$metaFieldsBehavior = $this -> Table -> getBehavior ( 'MetaFields' );
$metaQuery = $this -> MetaTemplates -> find ();
$metaQuery
-> order ([ 'is_default' => 'DESC' ])
2022-03-01 15:21:56 +01:00
-> where ( array_merge (
$metaTemplateConditions ,
[ 'scope' => $metaFieldsBehavior -> getScope (), ]
))
2021-11-09 08:59:17 +01:00
-> contain ( 'MetaTemplateFields' )
-> formatResults ( function ( \Cake\Collection\CollectionInterface $metaTemplates ) { // Set meta-template && meta-template-fields indexed by their ID
return $metaTemplates
-> map ( function ( $metaTemplate ) {
$metaTemplate -> meta_template_fields = Hash :: combine ( $metaTemplate -> meta_template_fields , '{n}.id' , '{n}' );
return $metaTemplate ;
})
-> indexBy ( 'id' );
});
$metaTemplates = $metaQuery -> all ();
2021-11-03 11:47:10 +01:00
return $metaTemplates ;
2020-09-28 01:25:07 +02:00
}
2020-06-25 01:45:28 +02:00
public function add ( array $params = []) : void
2020-06-09 15:57:16 +02:00
{
$data = $this -> Table -> newEmptyEntity ();
2021-11-23 14:56:25 +01:00
if ( $this -> metaFieldsSupported ()) {
$metaTemplates = $this -> getMetaTemplates ();
$data = $this -> attachMetaTemplatesIfNeeded ( $data , $metaTemplates -> toArray ());
}
2020-06-09 15:57:16 +02:00
if ( $this -> request -> is ( 'post' )) {
2021-01-13 14:18:26 +01:00
$patchEntityParams = [
2021-06-28 14:02:52 +02:00
'associated' => [],
2021-06-28 14:49:38 +02:00
'accessibleFields' => $data -> getAccessibleFieldForNew (),
2021-01-13 14:18:26 +01:00
];
if ( ! empty ( $params [ 'id' ])) {
unset ( $params [ 'id' ]);
}
2020-11-05 10:17:42 +01:00
$input = $this -> __massageInput ( $params );
if ( ! empty ( $params [ 'fields' ])) {
$patchEntityParams [ 'fields' ] = $params [ 'fields' ];
2020-06-25 01:45:28 +02:00
}
2022-02-25 00:30:50 +01:00
if ( isset ( $params [ 'beforeMarshal' ])) {
$input = $params [ 'beforeMarshal' ]( $input );
if ( $input === false ) {
throw new NotFoundException ( __ ( 'Could not save {0} due to the marshaling failing. Your input is bad and you should feel bad.' , $this -> ObjectAlias ));
}
}
2021-11-23 14:56:25 +01:00
if ( $this -> metaFieldsSupported ()) {
$massagedData = $this -> massageMetaFields ( $data , $input , $metaTemplates );
unset ( $input [ 'MetaTemplates' ]); // Avoid MetaTemplates to be overriden when patching entity
$data = $massagedData [ 'entity' ];
}
2020-11-05 10:17:42 +01:00
$data = $this -> Table -> patchEntity ( $data , $input , $patchEntityParams );
2021-10-01 13:19:26 +02:00
if ( isset ( $params [ 'beforeSave' ])) {
$data = $params [ 'beforeSave' ]( $data );
2022-01-17 17:16:03 +01:00
if ( $data === false ) {
throw new NotFoundException ( __ ( 'Could not save {0} due to the input failing to meet expectations. Your input is bad and you should feel bad.' , $this -> ObjectAlias ));
}
2021-10-01 13:19:26 +02:00
}
2020-12-15 10:40:49 +01:00
$savedData = $this -> Table -> save ( $data );
if ( $savedData !== false ) {
2021-10-01 13:19:26 +02:00
if ( isset ( $params [ 'afterSave' ])) {
$params [ 'afterSave' ]( $data );
}
2020-06-19 00:34:51 +02:00
$message = __ ( '{0} added.' , $this -> ObjectAlias );
if ( $this -> Controller -> ParamHandler -> isRest ()) {
2021-01-14 11:33:51 +01:00
$this -> Controller -> restResponsePayload = $this -> RestResponse -> viewData ( $savedData , 'json' );
2020-12-15 10:40:49 +01:00
} else if ( $this -> Controller -> ParamHandler -> isAjax ()) {
2020-08-07 21:47:04 +02:00
if ( ! empty ( $params [ 'displayOnSuccess' ])) {
2021-01-14 11:33:51 +01:00
$displayOnSuccess = $this -> renderViewInVariable ( $params [ 'displayOnSuccess' ], [ 'entity' => $data ]);
2021-06-17 08:54:09 +02:00
$this -> Controller -> ajaxResponsePayload = $this -> RestResponse -> ajaxSuccessResponse ( $this -> ObjectAlias , 'add' , $savedData , $message , [ 'displayOnSuccess' => $displayOnSuccess ]);
2021-01-14 11:33:51 +01:00
} else {
2021-06-17 08:54:09 +02:00
$this -> Controller -> ajaxResponsePayload = $this -> RestResponse -> ajaxSuccessResponse ( $this -> ObjectAlias , 'add' , $savedData , $message );
2020-08-07 21:47:04 +02:00
}
2021-01-14 11:33:51 +01:00
} else {
$this -> Controller -> Flash -> success ( $message );
2020-11-06 10:25:22 +01:00
if ( empty ( $params [ 'redirect' ])) {
2020-11-06 13:21:45 +01:00
$this -> Controller -> redirect ([ 'action' => 'view' , $data -> id ]);
2020-11-06 10:25:22 +01:00
} else {
$this -> Controller -> redirect ( $params [ 'redirect' ]);
}
2020-06-09 15:57:16 +02:00
}
} else {
2020-12-15 10:40:49 +01:00
$this -> Controller -> isFailResponse = true ;
2021-06-30 12:18:58 +02:00
$validationErrors = $data -> getErrors ();
$validationMessage = $this -> prepareValidationMessage ( $validationErrors );
2020-11-06 10:07:25 +01:00
$message = __ (
'{0} could not be added.{1}' ,
$this -> ObjectAlias ,
2022-03-01 09:51:51 +01:00
empty ( $validationMessage ) ? '' : PHP_EOL . __ ( 'Reason: {0}' , $validationMessage )
2020-11-06 10:07:25 +01:00
);
2020-06-19 00:34:51 +02:00
if ( $this -> Controller -> ParamHandler -> isRest ()) {
2021-06-17 08:54:09 +02:00
$this -> Controller -> restResponsePayload = $this -> RestResponse -> viewData ( $message , 'json' );
2020-12-15 10:40:49 +01:00
} else if ( $this -> Controller -> ParamHandler -> isAjax ()) {
2021-06-30 12:18:58 +02:00
$this -> Controller -> ajaxResponsePayload = $this -> RestResponse -> ajaxFailResponse ( $this -> ObjectAlias , 'add' , $data , $message , $validationErrors );
2020-06-09 15:57:16 +02:00
} else {
$this -> Controller -> Flash -> error ( $message );
}
}
}
2022-01-21 13:41:29 +01:00
if ( ! empty ( $params [ 'fields' ])) {
$this -> Controller -> set ( 'fields' , $params [ 'fields' ]);
}
2021-10-08 10:27:40 +02:00
$this -> Controller -> entity = $data ;
2020-06-19 00:34:51 +02:00
$this -> Controller -> set ( 'entity' , $data );
2020-06-09 15:57:16 +02:00
}
2021-07-05 17:15:21 +02:00
public function prepareValidationMessage ( $errors )
2021-06-30 12:18:58 +02:00
{
$validationMessage = '' ;
if ( ! empty ( $errors )) {
if ( count ( $errors ) == 1 ) {
$field = array_keys ( $errors )[ 0 ];
$fieldError = implode ( ', ' , array_values ( $errors [ $field ]));
$validationMessage = __ ( '{0}: {1}' , $field , $fieldError );
} else {
$validationMessage = __ ( 'There has been validation issues with multiple fields' );
}
}
return $validationMessage ;
}
2020-11-06 10:07:25 +01:00
private function prepareValidationError ( $data )
{
$validationMessage = '' ;
if ( ! empty ( $data -> getErrors ())) {
foreach ( $data -> getErrors () as $field => $errorData ) {
$errorMessages = [];
foreach ( $errorData as $key => $value ) {
2021-11-08 14:08:47 +01:00
if ( is_array ( $value )) {
2022-03-01 09:51:51 +01:00
$extracted = Hash :: extract ( $value , " { s}. { s} " );
if ( ! empty ( $extracted )) {
$errorMessages [] = implode ( '& ' , $extracted );
}
2021-11-08 14:08:47 +01:00
} else {
2022-03-01 09:51:51 +01:00
if ( ! empty ( $value )) {
$errorMessages [] = $value ;
}
2021-11-08 14:08:47 +01:00
}
2020-11-06 10:07:25 +01:00
}
2022-03-01 09:51:51 +01:00
if ( ! empty ( $errorMessages )) {
$validationMessage .= __ ( '{0}: {1}' , $field , implode ( ',' , $errorMessages ));
}
2020-11-06 10:07:25 +01:00
}
}
return $validationMessage ;
}
2020-09-28 01:25:07 +02:00
private function saveMetaFields ( $id , $input )
{
2021-01-15 14:40:03 +01:00
$this -> Table -> saveMetaFields ( $id , $input , $this -> Table );
2020-09-28 01:25:07 +02:00
}
2021-11-04 08:10:32 +01:00
// prune empty values and marshall fields
2021-12-08 11:11:46 +01:00
public function massageMetaFields ( $entity , $input , $allMetaTemplates = [])
2021-11-04 08:10:32 +01:00
{
2022-03-01 14:07:20 +01:00
if ( empty ( $input [ 'MetaTemplates' ]) || ! $this -> metaFieldsSupported ()) {
2021-11-05 17:44:37 +01:00
return [ 'entity' => $entity , 'metafields_to_delete' => []];
2021-11-04 08:10:32 +01:00
}
$metaFieldsTable = TableRegistry :: getTableLocator () -> get ( 'MetaFields' );
$metaFieldsIndex = [];
if ( empty ( $metaTemplates )) {
$allMetaTemplates = $this -> getMetaTemplates () -> toArray ();
}
2021-11-09 08:59:17 +01:00
if ( ! empty ( $entity -> meta_fields )) {
foreach ( $entity -> meta_fields as $i => $metaField ) {
$metaFieldsIndex [ $metaField -> id ] = $i ;
}
} else {
$entity -> meta_fields = [];
2021-11-04 08:10:32 +01:00
}
2021-11-05 17:44:37 +01:00
$metaFieldsToDelete = [];
2021-11-04 08:10:32 +01:00
foreach ( $input [ 'MetaTemplates' ] as $template_id => $template ) {
foreach ( $template [ 'meta_template_fields' ] as $meta_template_field_id => $meta_template_field ) {
$rawMetaTemplateField = $allMetaTemplates [ $template_id ][ 'meta_template_fields' ][ $meta_template_field_id ];
foreach ( $meta_template_field [ 'metaFields' ] as $meta_field_id => $meta_field ) {
if ( $meta_field_id == 'new' ) { // create new meta_field
$new_meta_fields = $meta_field ;
foreach ( $new_meta_fields as $new_value ) {
if ( ! empty ( $new_value )) {
$metaField = $metaFieldsTable -> newEmptyEntity ();
$metaFieldsTable -> patchEntity ( $metaField , [
'value' => $new_value ,
2021-11-09 08:59:17 +01:00
'scope' => $this -> Table -> getBehavior ( 'MetaFields' ) -> getScope (),
2021-11-04 08:10:32 +01:00
'field' => $rawMetaTemplateField -> field ,
'meta_template_id' => $rawMetaTemplateField -> meta_template_id ,
'meta_template_field_id' => $rawMetaTemplateField -> id ,
'parent_id' => $entity -> id ,
'uuid' => Text :: uuid (),
]);
$entity -> meta_fields [] = $metaField ;
2021-11-08 14:08:47 +01:00
$entity -> MetaTemplates [ $template_id ] -> meta_template_fields [ $meta_template_field_id ] -> metaFields [] = $metaField ;
2021-11-04 08:10:32 +01:00
}
}
} else {
$new_value = $meta_field [ 'value' ];
2021-11-09 08:59:17 +01:00
if ( ! empty ( $new_value )) { // update meta_field and attach validation errors
2022-02-28 11:08:42 +01:00
if ( isset ( $metaFieldsIndex [ $meta_field_id ])) {
2021-11-08 14:08:47 +01:00
$index = $metaFieldsIndex [ $meta_field_id ];
2022-03-09 12:01:15 +01:00
if ( $entity -> meta_fields [ $index ] -> value != $new_value ) { // nothing to do, value hasn't changed
$metaFieldsTable -> patchEntity ( $entity -> meta_fields [ $index ], [
'value' => $new_value , 'meta_template_field_id' => $rawMetaTemplateField -> id
], [ 'value' ]);
$metaFieldsTable -> patchEntity (
$entity -> MetaTemplates [ $template_id ] -> meta_template_fields [ $meta_template_field_id ] -> metaFields [ $meta_field_id ],
[ 'value' => $new_value , 'meta_template_field_id' => $rawMetaTemplateField -> id ],
[ 'value' ]
);
}
2021-11-08 14:08:47 +01:00
} else { // metafield comes from a second post where the temporary entity has already been created
$metaField = $metaFieldsTable -> newEmptyEntity ();
$metaFieldsTable -> patchEntity ( $metaField , [
'value' => $new_value ,
2021-11-09 08:59:17 +01:00
'scope' => $this -> Table -> getBehavior ( 'MetaFields' ) -> getScope (), // get scope from behavior
2021-11-08 14:08:47 +01:00
'field' => $rawMetaTemplateField -> field ,
'meta_template_id' => $rawMetaTemplateField -> meta_template_id ,
'meta_template_field_id' => $rawMetaTemplateField -> id ,
'parent_id' => $entity -> id ,
'uuid' => Text :: uuid (),
]);
$entity -> meta_fields [] = $metaField ;
$entity -> MetaTemplates [ $template_id ] -> meta_template_fields [ $meta_template_field_id ] -> metaFields [] = $metaField ;
}
2021-11-09 08:59:17 +01:00
} else { // Metafield value is empty, indicating the field should be removed
2021-11-08 14:08:47 +01:00
$index = $metaFieldsIndex [ $meta_field_id ];
2021-11-05 17:44:37 +01:00
$metaFieldsToDelete [] = $entity -> meta_fields [ $index ];
2021-11-04 08:10:32 +01:00
unset ( $entity -> meta_fields [ $index ]);
2021-11-08 14:08:47 +01:00
unset ( $entity -> MetaTemplates [ $template_id ] -> meta_template_fields [ $meta_template_field_id ] -> metaFields [ $meta_field_id ]);
2021-11-04 08:10:32 +01:00
}
}
}
}
}
$entity -> setDirty ( 'meta_fields' , true );
2022-10-26 17:10:04 +02:00
$entity -> _metafields_to_delete = $metaFieldsToDelete ;
2021-11-05 17:44:37 +01:00
return [ 'entity' => $entity , 'metafields_to_delete' => $metaFieldsToDelete ];
2021-11-04 08:10:32 +01:00
}
2020-11-05 10:17:42 +01:00
private function __massageInput ( $params )
{
$input = $this -> request -> getData ();
if ( ! empty ( $params [ 'override' ])) {
foreach ( $params [ 'override' ] as $field => $value ) {
$input [ $field ] = $value ;
}
}
if ( ! empty ( $params [ 'removeEmpty' ])) {
2021-01-12 08:50:01 +01:00
foreach ( $params [ 'removeEmpty' ] as $removeEmptyField ) {
if ( empty ( $input [ $removeEmptyField ])) {
unset ( $input [ $removeEmptyField ]);
}
2020-11-05 10:17:42 +01:00
}
}
return $input ;
}
2020-06-21 21:33:38 +02:00
public function edit ( int $id , array $params = []) : void
2020-06-09 15:57:16 +02:00
{
if ( empty ( $id )) {
2020-06-19 00:34:51 +02:00
throw new NotFoundException ( __ ( 'Invalid {0}.' , $this -> ObjectAlias ));
2020-06-09 15:57:16 +02:00
}
2021-08-26 12:06:12 +02:00
if ( $this -> taggingSupported ()) {
$params [ 'contain' ][] = 'Tags' ;
2021-08-30 15:11:21 +02:00
$this -> setAllTags ();
2021-08-26 12:06:12 +02:00
}
2021-11-10 09:02:51 +01:00
if ( $this -> metaFieldsSupported ()) {
2022-02-25 08:19:01 +01:00
if ( empty ( $params [ 'contain' ])) {
$params [ 'contain' ] = [];
2021-11-04 08:10:32 +01:00
}
2022-02-25 08:19:01 +01:00
if ( is_array ( $params [ 'contain' ])) {
$params [ 'contain' ][] = 'MetaFields' ;
2021-11-04 08:10:32 +01:00
} else {
2022-02-25 08:19:01 +01:00
$params [ 'contain' ] = [ $params [ 'contain' ], 'MetaFields' ];
2021-11-04 08:10:32 +01:00
}
}
2022-12-09 11:56:03 +01:00
$query = $this -> Table -> find () -> where ([ " { $this -> TableAlias } .id " => $id ]);
2022-02-25 08:19:01 +01:00
if ( ! empty ( $params [ 'contain' ])) {
$query -> contain ( $params [ 'contain' ]);
}
2021-11-24 01:30:28 +01:00
if ( ! empty ( $params [ 'conditions' ])) {
2022-01-20 09:00:45 +01:00
$query -> where ( $params [ 'conditions' ]);
2021-11-24 01:30:28 +01:00
}
2022-02-23 08:18:08 +01:00
$data = $query -> first ();
2022-01-21 13:41:29 +01:00
if ( isset ( $params [ 'afterFind' ])) {
$data = $params [ 'afterFind' ]( $data , $params );
}
2021-11-24 01:30:28 +01:00
if ( empty ( $data )) {
throw new NotFoundException ( __ ( 'Invalid {0}.' , $this -> ObjectAlias ));
}
2021-11-11 14:51:51 +01:00
if ( $this -> metaFieldsSupported ()) {
$metaTemplates = $this -> getMetaTemplates ();
$data = $this -> attachMetaTemplatesIfNeeded ( $data , $metaTemplates -> toArray ());
}
2020-06-09 15:57:16 +02:00
if ( $this -> request -> is ([ 'post' , 'put' ])) {
2021-01-13 14:18:26 +01:00
$patchEntityParams = [
'associated' => []
];
2020-11-05 10:17:42 +01:00
$input = $this -> __massageInput ( $params );
if ( ! empty ( $params [ 'fields' ])) {
$patchEntityParams [ 'fields' ] = $params [ 'fields' ];
2020-06-25 01:45:28 +02:00
}
2022-02-25 00:30:50 +01:00
if ( isset ( $params [ 'beforeMarshal' ])) {
$input = $params [ 'beforeMarshal' ]( $input );
if ( $input === false ) {
throw new NotFoundException ( __ ( 'Could not save {0} due to the marshaling failing. Your input is bad and you should feel bad.' , $this -> ObjectAlias ));
}
}
2021-11-10 09:02:51 +01:00
if ( $this -> metaFieldsSupported ()) {
2021-11-09 08:59:17 +01:00
$massagedData = $this -> massageMetaFields ( $data , $input , $metaTemplates );
unset ( $input [ 'MetaTemplates' ]); // Avoid MetaTemplates to be overriden when patching entity
$data = $massagedData [ 'entity' ];
$metaFieldsToDelete = $massagedData [ 'metafields_to_delete' ];
}
2020-11-06 10:07:25 +01:00
$data = $this -> Table -> patchEntity ( $data , $input , $patchEntityParams );
2021-10-01 13:19:26 +02:00
if ( isset ( $params [ 'beforeSave' ])) {
$data = $params [ 'beforeSave' ]( $data );
2022-02-25 00:30:50 +01:00
if ( $data === false ) {
throw new NotFoundException ( __ ( 'Could not save {0} due to the input failing to meet expectations. Your input is bad and you should feel bad.' , $this -> ObjectAlias ));
}
2021-10-01 13:19:26 +02:00
}
2020-12-10 15:18:02 +01:00
$savedData = $this -> Table -> save ( $data );
if ( $savedData !== false ) {
2021-11-10 09:02:51 +01:00
if ( $this -> metaFieldsSupported () && ! empty ( $metaFieldsToDelete )) {
2022-11-09 13:58:02 +01:00
foreach ( $metaFieldsToDelete as $k => $v ) {
if ( $v === null ) {
unset ( $metaFieldsToDelete [ $k ]);
}
}
if ( ! empty ( $metaFieldsToDelete )) {
$this -> Table -> MetaFields -> unlink ( $savedData , $metaFieldsToDelete );
}
2021-11-05 17:44:37 +01:00
}
2021-10-01 13:19:26 +02:00
if ( isset ( $params [ 'afterSave' ])) {
$params [ 'afterSave' ]( $data );
}
2021-01-13 14:15:53 +01:00
$message = __ ( '{0} `{1}` updated.' , $this -> ObjectAlias , $savedData -> { $this -> Table -> getDisplayField ()});
2020-06-19 00:34:51 +02:00
if ( $this -> Controller -> ParamHandler -> isRest ()) {
2021-01-15 14:40:03 +01:00
$this -> Controller -> restResponsePayload = $this -> RestResponse -> viewData ( $savedData , 'json' );
2020-12-10 15:18:02 +01:00
} else if ( $this -> Controller -> ParamHandler -> isAjax ()) {
2021-06-17 08:54:09 +02:00
$this -> Controller -> ajaxResponsePayload = $this -> RestResponse -> ajaxSuccessResponse ( $this -> ObjectAlias , 'edit' , $savedData , $message );
2020-06-09 15:57:16 +02:00
} else {
$this -> Controller -> Flash -> success ( $message );
2020-11-06 10:25:22 +01:00
if ( empty ( $params [ 'redirect' ])) {
$this -> Controller -> redirect ([ 'action' => 'view' , $id ]);
} else {
$this -> Controller -> redirect ( $params [ 'redirect' ]);
}
2020-06-09 15:57:16 +02:00
}
} else {
2021-06-30 12:18:58 +02:00
$validationErrors = $data -> getErrors ();
2021-11-08 14:08:47 +01:00
$validationMessage = $this -> prepareValidationError ( $data );
2020-11-06 10:07:25 +01:00
$message = __ (
2021-07-05 17:15:21 +02:00
'{0} could not be modified.{1}' ,
$this -> ObjectAlias ,
2022-03-01 09:51:51 +01:00
empty ( $validationMessage ) ? '' : PHP_EOL . __ ( 'Reason: {0}' , $validationMessage )
2020-11-06 10:07:25 +01:00
);
2020-06-19 00:34:51 +02:00
if ( $this -> Controller -> ParamHandler -> isRest ()) {
2020-12-10 15:18:02 +01:00
} else if ( $this -> Controller -> ParamHandler -> isAjax ()) {
2021-06-30 12:18:58 +02:00
$this -> Controller -> ajaxResponsePayload = $this -> RestResponse -> ajaxFailResponse ( $this -> ObjectAlias , 'edit' , $data , $message , $validationErrors );
2020-11-06 10:07:25 +01:00
} else {
$this -> Controller -> Flash -> error ( $message );
2020-06-09 15:57:16 +02:00
}
}
}
2022-01-21 13:41:29 +01:00
if ( ! empty ( $params [ 'fields' ])) {
$this -> Controller -> set ( 'fields' , $params [ 'fields' ]);
}
2021-10-08 10:27:40 +02:00
$this -> Controller -> entity = $data ;
2020-06-19 00:34:51 +02:00
$this -> Controller -> set ( 'entity' , $data );
2020-06-09 15:57:16 +02:00
}
2020-12-08 09:07:48 +01:00
public function attachMetaData ( $id , $data )
{
2021-11-10 09:02:51 +01:00
if ( ! $this -> metaFieldsSupported ()) {
2021-11-09 08:59:17 +01:00
throw new \Exception ( __ ( " Table { $this -> TableAlias } does not support meta_fields " ));
2020-12-08 09:07:48 +01:00
}
2021-11-09 08:59:17 +01:00
$metaFieldScope = $this -> Table -> getBehavior ( 'MetaFields' ) -> getScope ();
2021-02-26 10:36:06 +01:00
$query = $this -> MetaTemplates -> find () -> where ([ 'MetaTemplates.scope' => $metaFieldScope ]);
$query -> contain ([ 'MetaTemplateFields.MetaFields' => function ( $q ) use ( $id , $metaFieldScope ) {
return $q -> where ([ 'MetaFields.scope' => $metaFieldScope , 'MetaFields.parent_id' => $id ]);
}]);
$query
-> order ([ 'MetaTemplates.is_default' => 'DESC' ])
-> order ([ 'MetaTemplates.name' => 'ASC' ]);
$metaTemplates = $query -> all () -> toArray ();
$metaTemplates = $this -> pruneEmptyMetaTemplates ( $metaTemplates );
2020-12-08 09:07:48 +01:00
$data [ 'metaTemplates' ] = $metaTemplates ;
return $data ;
}
2021-02-26 10:36:06 +01:00
public function pruneEmptyMetaTemplates ( $metaTemplates )
{
foreach ( $metaTemplates as $i => $metaTemplate ) {
foreach ( $metaTemplate [ 'meta_template_fields' ] as $j => $metaTemplateField ) {
if ( empty ( $metaTemplateField [ 'meta_fields' ])) {
unset ( $metaTemplates [ $i ][ 'meta_template_fields' ][ $j ]);
}
}
if ( empty ( $metaTemplates [ $i ][ 'meta_template_fields' ])) {
unset ( $metaTemplates [ $i ]);
}
}
return $metaTemplates ;
}
2021-11-03 11:47:10 +01:00
public function getMetaFields ( $id )
2020-09-28 01:25:07 +02:00
{
2021-11-10 09:02:51 +01:00
if ( ! $this -> metaFieldsSupported ()) {
2021-11-09 08:59:17 +01:00
throw new \Exception ( __ ( " Table { $this -> TableAlias } does not support meta_fields " ));
2020-09-28 13:14:45 +02:00
}
2020-09-28 01:25:07 +02:00
$query = $this -> MetaFields -> find ();
2021-11-09 08:59:17 +01:00
$query -> where ([ 'MetaFields.scope' => $this -> Table -> getBehavior ( 'MetaFields' ) -> getScope (), 'MetaFields.parent_id' => $id ]);
2020-09-28 01:25:07 +02:00
$metaFields = $query -> all ();
2021-11-03 11:47:10 +01:00
$data = [];
2021-11-04 08:10:32 +01:00
foreach ( $metaFields as $metaField ) {
2021-11-03 11:47:10 +01:00
if ( empty ( $data [ $metaField -> meta_template_id ][ $metaField -> meta_template_field_id ])) {
$data [ $metaField -> meta_template_id ][ $metaField -> meta_template_field_id ] = [];
}
2022-08-23 14:50:13 +02:00
$data [ $metaField -> meta_template_id ][ $metaField -> meta_template_field_id ][ $metaField -> id ] = $metaField ;
2021-11-03 11:47:10 +01:00
}
return $data ;
}
2021-12-08 11:11:46 +01:00
public function attachMetaTemplates ( $data , $metaTemplates , $pruneEmptyDisabled = true )
2021-11-03 11:47:10 +01:00
{
2021-12-08 11:11:46 +01:00
$this -> MetaTemplates = TableRegistry :: getTableLocator () -> get ( 'MetaTemplates' );
2021-11-09 08:59:17 +01:00
$metaFields = [];
if ( ! empty ( $data -> id )) {
2021-12-08 11:11:46 +01:00
$metaFields = $this -> getMetaFields ( $data -> id );
2021-11-09 08:59:17 +01:00
}
2021-11-03 11:47:10 +01:00
foreach ( $metaTemplates as $i => $metaTemplate ) {
if ( isset ( $metaFields [ $metaTemplate -> id ])) {
foreach ( $metaTemplate -> meta_template_fields as $j => $meta_template_field ) {
if ( isset ( $metaFields [ $metaTemplate -> id ][ $meta_template_field -> id ])) {
$metaTemplates [ $metaTemplate -> id ] -> meta_template_fields [ $j ][ 'metaFields' ] = $metaFields [ $metaTemplate -> id ][ $meta_template_field -> id ];
} else {
$metaTemplates [ $metaTemplate -> id ] -> meta_template_fields [ $j ][ 'metaFields' ] = [];
}
}
2021-12-08 11:11:46 +01:00
} else {
if ( ! empty ( $pruneEmptyDisabled ) && ! $metaTemplate -> enabled ) {
unset ( $metaTemplates [ $i ]);
}
2022-11-11 15:30:55 +01:00
continue ;
2021-12-08 11:11:46 +01:00
}
$newestTemplate = $this -> MetaTemplates -> getNewestVersion ( $metaTemplate );
if ( ! empty ( $newestTemplate ) && ! empty ( $metaTemplates [ $i ])) {
$metaTemplates [ $i ][ 'hasNewerVersion' ] = $newestTemplate ;
2021-11-03 11:47:10 +01:00
}
2022-11-04 09:33:39 +01:00
$metaTemplates [ $metaTemplate -> id ][ 'meta_template_fields' ] = $metaTemplates [ $metaTemplate -> id ][ 'meta_template_fields' ];
2020-09-28 01:25:07 +02:00
}
2022-11-04 09:33:39 +01:00
$metaTemplates = $metaTemplates ;
2022-08-23 14:50:13 +02:00
$data [ 'MetaTemplates' ] = $metaTemplates ;
2020-09-28 01:25:07 +02:00
return $data ;
}
2021-11-10 09:06:39 +01:00
protected function includeRequestedMetaFields ( $query )
{
$user = $this -> Controller -> ACL -> getUser ();
$tableSettings = IndexSetting :: getTableSetting ( $user , $this -> Table );
if ( empty ( $tableSettings [ 'visible_meta_column' ])) {
return $query ;
}
$containConditions = [ 'OR' => []];
$requestedMetaFields = [];
foreach ( $tableSettings [ 'visible_meta_column' ] as $template_id => $fields ) {
$containConditions [ 'OR' ][] = [
'meta_template_id' => $template_id ,
'meta_template_field_id IN' => array_map ( 'intval' , $fields ),
];
foreach ( $fields as $field ) {
$requestedMetaFields [] = [ 'template_id' => $template_id , 'meta_template_field_id' => intval ( $field )];
}
}
$this -> Controller -> set ( 'requestedMetaFields' , $requestedMetaFields );
return $query -> contain ([
'MetaFields' => [
'conditions' => $containConditions
]
]);
}
2022-10-28 09:13:15 +02:00
protected function setRequestedEntryAmount ()
{
$user = $this -> Controller -> ACL -> getUser ();
$tableSettings = IndexSetting :: getTableSetting ( $user , $this -> Table );
if ( ! empty ( $tableSettings [ 'number_of_element' ])) {
$this -> Controller -> paginate [ 'limit' ] = intval ( $tableSettings [ 'number_of_element' ]);
}
}
2020-06-19 00:34:51 +02:00
public function view ( int $id , array $params = []) : void
2020-06-09 15:57:16 +02:00
{
if ( empty ( $id )) {
2020-06-19 00:34:51 +02:00
throw new NotFoundException ( __ ( 'Invalid {0}.' , $this -> ObjectAlias ));
2020-06-09 15:57:16 +02:00
}
2021-08-26 12:06:12 +02:00
if ( $this -> taggingSupported ()) {
$params [ 'contain' ][] = 'Tags' ;
2021-08-26 16:12:55 +02:00
$this -> setAllTags ();
2021-08-26 12:06:12 +02:00
}
2021-11-10 09:02:51 +01:00
if ( $this -> metaFieldsSupported ()) {
2021-11-04 15:21:03 +01:00
if ( ! empty ( $this -> request -> getQuery ( 'full' ))) {
$params [ 'contain' ][ 'MetaFields' ] = [ 'MetaTemplateFields' => 'MetaTemplates' ];
} else {
$params [ 'contain' ][] = 'MetaFields' ;
}
}
2021-08-26 12:06:12 +02:00
2020-06-09 15:57:16 +02:00
$data = $this -> Table -> get ( $id , $params );
2022-02-04 00:16:24 +01:00
if ( empty ( $data )) {
throw new NotFoundException ( __ ( 'Invalid {0}.' , $this -> ObjectAlias ));
}
2022-03-09 09:27:37 +01:00
if ( $this -> metaFieldsSupported () && ! empty ( $data [ 'meta_fields' ])) {
2022-03-01 15:21:56 +01:00
$usedMetaTemplateIDs = array_values ( array_unique ( Hash :: extract ( $data [ 'meta_fields' ], '{n}.meta_template_id' )));
$data = $this -> attachMetaTemplatesIfNeeded ( $data , null , [
'id IN' => $usedMetaTemplateIDs
]);
}
2021-06-01 07:47:22 +02:00
if ( isset ( $params [ 'afterFind' ])) {
$data = $params [ 'afterFind' ]( $data );
}
2022-02-04 00:16:24 +01:00
if ( empty ( $data )) {
throw new NotFoundException ( __ ( 'Invalid {0}.' , $this -> ObjectAlias ));
}
2020-06-09 15:57:16 +02:00
if ( $this -> Controller -> ParamHandler -> isRest ()) {
2021-06-17 08:54:09 +02:00
$this -> Controller -> restResponsePayload = $this -> RestResponse -> viewData ( $data , 'json' );
2020-06-09 15:57:16 +02:00
}
2020-06-19 00:34:51 +02:00
$this -> Controller -> set ( 'entity' , $data );
2020-06-09 15:57:16 +02:00
}
2022-03-01 15:21:56 +01:00
public function attachMetaTemplatesIfNeeded ( $data , array $metaTemplates = null , array $metaTemplateConditions = [])
2021-11-09 08:59:17 +01:00
{
2021-11-10 09:02:51 +01:00
if ( ! $this -> metaFieldsSupported ()) {
2021-11-09 08:59:17 +01:00
return $data ;
}
2021-11-10 09:06:39 +01:00
if ( ! is_null ( $metaTemplates )) {
2021-11-11 14:51:51 +01:00
// We might be in the case where $metaTemplates gets re-used in a while loop
2021-11-10 09:06:39 +01:00
// We deep copy the meta-template so that the data attached is not preserved for the next iteration
$metaTemplates = array_map ( function ( $metaTemplate ) {
$tmpEntity = $this -> MetaTemplates -> newEntity ( $metaTemplate -> toArray ());
2021-11-11 14:51:51 +01:00
$tmpEntity [ 'meta_template_fields' ] = Hash :: combine ( $tmpEntity [ 'meta_template_fields' ], '{n}.id' , '{n}' ); // newEntity resets array indexing, see https://github.com/cakephp/cakephp/blob/32e3c532fea8abe2db8b697f07dfddf4dfc134ca/src/ORM/Marshaller.php#L369
2021-11-10 09:06:39 +01:00
return $tmpEntity ;
}, $metaTemplates );
} else {
2022-03-01 15:21:56 +01:00
$metaTemplates = $this -> getMetaTemplates ( $metaTemplateConditions ) -> toArray ();
2021-11-10 09:06:39 +01:00
}
$data = $this -> attachMetaTemplates ( $data , $metaTemplates );
2021-11-09 08:59:17 +01:00
return $data ;
}
2021-11-24 01:30:28 +01:00
public function delete ( $id = false , $params = []) : void
2020-06-09 15:57:16 +02:00
{
2021-06-23 11:12:14 +02:00
if ( $this -> request -> is ( 'get' )) {
if ( ! empty ( $id )) {
2022-01-20 09:00:45 +01:00
$query = $this -> Table -> find () -> where ([ $this -> Table -> getAlias () . '.id' => $id ]);
2021-11-24 01:30:28 +01:00
if ( ! empty ( $params [ 'conditions' ])) {
2022-01-20 09:00:45 +01:00
$query -> where ( $params [ 'conditions' ]);
2021-11-24 01:30:28 +01:00
}
if ( ! empty ( $params [ 'contain' ])) {
2022-01-20 09:00:45 +01:00
$query -> contain ( $params [ 'contain' ]);
2021-11-24 01:30:28 +01:00
}
2022-01-20 09:00:45 +01:00
$data = $query -> first ();
2021-11-24 01:30:28 +01:00
if ( empty ( $data )) {
throw new NotFoundException ( __ ( 'Invalid {0}.' , $this -> ObjectAlias ));
}
2021-06-23 11:12:14 +02:00
$this -> Controller -> set ( 'id' , $data [ 'id' ]);
$this -> Controller -> set ( 'data' , $data );
$this -> Controller -> set ( 'bulkEnabled' , false );
} else {
$this -> Controller -> set ( 'bulkEnabled' , true );
}
} else if ( $this -> request -> is ( 'post' ) || $this -> request -> is ( 'delete' )) {
$ids = $this -> getIdsOrFail ( $id );
$isBulk = count ( $ids ) > 1 ;
$bulkSuccesses = 0 ;
foreach ( $ids as $id ) {
2022-01-20 09:00:45 +01:00
$query = $this -> Table -> find () -> where ([ $this -> Table -> getAlias () . '.id' => $id ]);
2021-11-24 01:30:28 +01:00
if ( ! empty ( $params [ 'conditions' ])) {
2022-01-20 09:00:45 +01:00
$query -> where ( $params [ 'conditions' ]);
2021-11-24 01:30:28 +01:00
}
if ( ! empty ( $params [ 'contain' ])) {
2022-01-20 09:00:45 +01:00
$query -> contain ( $params [ 'contain' ]);
2021-11-24 01:30:28 +01:00
}
2022-01-20 09:00:45 +01:00
$data = $query -> first ();
2022-01-18 15:35:55 +01:00
if ( isset ( $params [ 'beforeSave' ])) {
2022-01-21 13:41:29 +01:00
try {
$data = $params [ 'beforeSave' ]( $data );
2022-02-08 08:42:25 +01:00
if ( $data === false ) {
throw new NotFoundException ( __ ( 'Could not save {0} due to the input failing to meet expectations. Your input is bad and you should feel bad.' , $this -> ObjectAlias ));
}
2022-01-21 13:41:29 +01:00
} catch ( Exception $e ) {
$data = false ;
2022-01-20 09:00:45 +01:00
}
2021-11-24 01:30:28 +01:00
}
2022-01-18 16:24:24 +01:00
if ( ! empty ( $data )) {
$success = $this -> Table -> delete ( $data );
$success = true ;
} else {
$success = false ;
}
2021-06-23 11:12:14 +02:00
if ( $success ) {
$bulkSuccesses ++ ;
2020-06-09 15:57:16 +02:00
}
}
2021-06-23 11:12:14 +02:00
$message = $this -> getMessageBasedOnResult (
$bulkSuccesses == count ( $ids ),
$isBulk ,
__ ( '{0} deleted.' , $this -> ObjectAlias ),
__ ( 'All {0} have been deleted.' , Inflector :: pluralize ( $this -> ObjectAlias )),
__ ( 'Could not delete {0}.' , $this -> ObjectAlias ),
2021-11-04 08:10:32 +01:00
__ (
'{0} / {1} {2} have been deleted.' ,
2021-06-23 11:12:14 +02:00
$bulkSuccesses ,
count ( $ids ),
Inflector :: pluralize ( $this -> ObjectAlias )
)
);
2021-11-08 15:54:37 +01:00
$additionalData = [];
if ( $bulkSuccesses > 0 ) {
$additionalData [ 'redirect' ] = Router :: url ([ 'controller' => $this -> Controller -> getName (), 'action' => 'index' ]);
}
$this -> setResponseForController ( 'delete' , $bulkSuccesses , $message , $data , null , $additionalData );
2020-06-09 15:57:16 +02:00
}
$this -> Controller -> set ( 'scope' , 'users' );
$this -> Controller -> viewBuilder () -> setLayout ( 'ajax' );
$this -> Controller -> render ( '/genericTemplates/delete' );
}
2021-11-04 08:10:32 +01:00
public function tag ( $id = false ) : void
2021-08-26 12:06:12 +02:00
{
if ( ! $this -> taggingSupported ()) {
throw new Exception ( " Table { $this -> TableAlias } does not support tagging " );
}
if ( $this -> request -> is ( 'get' )) {
2021-08-26 16:12:55 +02:00
$this -> setAllTags ();
2021-11-04 08:10:32 +01:00
if ( ! empty ( $id )) {
2021-08-26 12:06:12 +02:00
$params = [
'contain' => 'Tags' ,
];
$entity = $this -> Table -> get ( $id , $params );
$this -> Controller -> set ( 'id' , $entity -> id );
$this -> Controller -> set ( 'data' , $entity );
$this -> Controller -> set ( 'bulkEnabled' , false );
} else {
$this -> Controller -> set ( 'bulkEnabled' , true );
}
} else if ( $this -> request -> is ( 'post' ) || $this -> request -> is ( 'delete' )) {
$ids = $this -> getIdsOrFail ( $id );
$isBulk = count ( $ids ) > 1 ;
$bulkSuccesses = 0 ;
foreach ( $ids as $id ) {
$params = [
'contain' => 'Tags' ,
];
$entity = $this -> Table -> get ( $id , $params );
$input = $this -> request -> getData ();
2021-08-31 15:21:28 +02:00
$tagsToAdd = json_decode ( $input [ 'tag_list' ]);
// patching will mirror tag in the DB, however, we only want to add tags
$input [ 'tags' ] = array_merge ( $tagsToAdd , $entity -> tags );
2021-08-26 12:06:12 +02:00
$patchEntityParams = [
'fields' => [ 'tags' ],
];
$entity = $this -> Table -> patchEntity ( $entity , $input , $patchEntityParams );
$savedData = $this -> Table -> save ( $entity );
$success = true ;
if ( $success ) {
$bulkSuccesses ++ ;
}
}
$message = $this -> getMessageBasedOnResult (
$bulkSuccesses == count ( $ids ),
$isBulk ,
2021-08-26 16:12:55 +02:00
__ ( '{0} tagged with `{1}`.' , $this -> ObjectAlias , $input [ 'tag_list' ]),
2021-08-26 12:06:12 +02:00
__ ( 'All {0} have been tagged.' , Inflector :: pluralize ( $this -> ObjectAlias )),
2021-08-26 16:12:55 +02:00
__ ( 'Could not tag {0} with `{1}`.' , $this -> ObjectAlias , $input [ 'tag_list' ]),
2021-11-04 08:10:32 +01:00
__ (
'{0} / {1} {2} have been tagged.' ,
2021-08-26 12:06:12 +02:00
$bulkSuccesses ,
count ( $ids ),
Inflector :: pluralize ( $this -> ObjectAlias )
)
);
$this -> setResponseForController ( 'tag' , $bulkSuccesses , $message , $savedData );
}
$this -> Controller -> viewBuilder () -> setLayout ( 'ajax' );
$this -> Controller -> render ( '/genericTemplates/tagForm' );
}
2021-11-04 08:10:32 +01:00
public function untag ( $id = false ) : void
2021-08-26 12:06:12 +02:00
{
if ( ! $this -> taggingSupported ()) {
throw new Exception ( " Table { $this -> TableAlias } does not support tagging " );
}
if ( $this -> request -> is ( 'get' )) {
2021-08-26 16:12:55 +02:00
$this -> setAllTags ();
2021-11-04 08:10:32 +01:00
if ( ! empty ( $id )) {
2021-08-26 12:06:12 +02:00
$params = [
'contain' => 'Tags' ,
];
$entity = $this -> Table -> get ( $id , $params );
$this -> Controller -> set ( 'id' , $entity -> id );
$this -> Controller -> set ( 'data' , $entity );
$this -> Controller -> set ( 'bulkEnabled' , false );
} else {
$this -> Controller -> set ( 'bulkEnabled' , true );
}
} else if ( $this -> request -> is ( 'post' ) || $this -> request -> is ( 'delete' )) {
$ids = $this -> getIdsOrFail ( $id );
$isBulk = count ( $ids ) > 1 ;
$bulkSuccesses = 0 ;
foreach ( $ids as $id ) {
$params = [
'contain' => 'Tags' ,
];
$entity = $this -> Table -> get ( $id , $params );
$input = $this -> request -> getData ();
2021-08-31 15:21:28 +02:00
$tagsToRemove = json_decode ( $input [ 'tag_list' ]);
// patching will mirror tag in the DB, however, we only want to remove tags
$input [ 'tags' ] = array_filter ( $entity -> tags , function ( $existingTag ) use ( $tagsToRemove ) {
2021-10-01 15:13:18 +02:00
return ! in_array ( $existingTag -> name , $tagsToRemove );
2021-08-31 15:21:28 +02:00
});
2021-08-26 12:06:12 +02:00
$patchEntityParams = [
'fields' => [ 'tags' ],
];
$entity = $this -> Table -> patchEntity ( $entity , $input , $patchEntityParams );
$savedData = $this -> Table -> save ( $entity );
$success = true ;
if ( $success ) {
$bulkSuccesses ++ ;
}
}
$message = $this -> getMessageBasedOnResult (
$bulkSuccesses == count ( $ids ),
$isBulk ,
2021-08-26 16:12:55 +02:00
__ ( '{0} untagged with `{1}`.' , $this -> ObjectAlias , implode ( ', ' , $tagsToRemove )),
2021-08-26 12:06:12 +02:00
__ ( 'All {0} have been untagged.' , Inflector :: pluralize ( $this -> ObjectAlias )),
2021-08-26 16:12:55 +02:00
__ ( 'Could not untag {0} with `{1}`.' , $this -> ObjectAlias , $input [ 'tag_list' ]),
2021-11-04 08:10:32 +01:00
__ (
'{0} / {1} {2} have been untagged.' ,
2021-08-26 12:06:12 +02:00
$bulkSuccesses ,
count ( $ids ),
Inflector :: pluralize ( $this -> ObjectAlias )
)
);
$this -> setResponseForController ( 'tag' , $bulkSuccesses , $message , $entity );
}
$this -> Controller -> viewBuilder () -> setLayout ( 'ajax' );
$this -> Controller -> render ( '/genericTemplates/tagForm' );
}
public function viewTags ( int $id , array $params = []) : void
{
if ( ! $this -> taggingSupported ()) {
throw new Exception ( " Table { $this -> TableAlias } does not support tagging " );
}
if ( empty ( $id )) {
throw new NotFoundException ( __ ( 'Invalid {0}.' , $this -> ObjectAlias ));
}
$params [ 'contain' ][] = 'Tags' ;
$data = $this -> Table -> get ( $id , $params );
if ( isset ( $params [ 'afterFind' ])) {
$data = $params [ 'afterFind' ]( $data );
}
if ( $this -> Controller -> ParamHandler -> isRest ()) {
$this -> Controller -> restResponsePayload = $this -> RestResponse -> viewData ( $data , 'json' );
}
$this -> Controller -> set ( 'entity' , $data );
2021-08-26 16:12:55 +02:00
$this -> setAllTags ();
2021-08-26 12:06:12 +02:00
$this -> Controller -> viewBuilder () -> setLayout ( 'ajax' );
$this -> Controller -> render ( '/genericTemplates/tag' );
}
2021-11-08 15:54:37 +01:00
public function setResponseForController ( $action , $success , $message , $data = [], $errors = null , $additionalData = [])
2021-06-23 11:12:14 +02:00
{
if ( $success ) {
if ( $this -> Controller -> ParamHandler -> isRest ()) {
$this -> Controller -> restResponsePayload = $this -> RestResponse -> viewData ( $data , 'json' );
} elseif ( $this -> Controller -> ParamHandler -> isAjax ()) {
2021-11-09 09:15:19 +01:00
if ( ! empty ( $additionalData [ 'redirect' ])) { // If a redirection occurs, we need to make sure the flash message gets displayed
2021-11-08 15:54:37 +01:00
$this -> Controller -> Flash -> success ( $message );
}
$this -> Controller -> ajaxResponsePayload = $this -> RestResponse -> ajaxSuccessResponse ( $this -> ObjectAlias , $action , $data , $message , $additionalData );
2021-06-23 11:12:14 +02:00
} else {
$this -> Controller -> Flash -> success ( $message );
$this -> Controller -> redirect ( $this -> Controller -> referer ());
}
} else {
if ( $this -> Controller -> ParamHandler -> isRest ()) {
$this -> Controller -> restResponsePayload = $this -> RestResponse -> viewData ( $data , 'json' );
} elseif ( $this -> Controller -> ParamHandler -> isAjax ()) {
2022-02-21 11:48:41 +01:00
if ( ! empty ( $additionalData [ 'redirect' ])) { // If a redirection occurs, we need to make sure the flash message gets displayed
2021-11-08 15:54:37 +01:00
$this -> Controller -> Flash -> error ( $message );
}
2021-06-23 11:12:14 +02:00
$this -> Controller -> ajaxResponsePayload = $this -> RestResponse -> ajaxFailResponse ( $this -> ObjectAlias , $action , $data , $message , ! is_null ( $errors ) ? $errors : $data -> getErrors ());
} else {
$this -> Controller -> Flash -> error ( $message );
$this -> Controller -> redirect ( $this -> Controller -> referer ());
}
}
}
private function getMessageBasedOnResult ( $isSuccess , $isBulk , $messageSingleSuccess , $messageBulkSuccess , $messageSingleFailure , $messageBulkFailure )
{
if ( $isSuccess ) {
$message = $isBulk ? $messageBulkSuccess : $messageSingleSuccess ;
} else {
$message = $isBulk ? $messageBulkFailure : $messageSingleFailure ;
}
return $message ;
}
/**
* getIdsOrFail
*
* @ param mixed $id
* @ return Array The ID converted to a list or the list of provided IDs from the request
* @ throws NotFoundException when no ID could be found
*/
2021-11-04 08:10:32 +01:00
public function getIdsOrFail ( $id = false ) : array
2021-06-23 11:12:14 +02:00
{
$params = $this -> Controller -> ParamHandler -> harvestParams ([ 'ids' ]);
if ( ! empty ( $params [ 'ids' ])) {
$params [ 'ids' ] = json_decode ( $params [ 'ids' ]);
}
$ids = [];
if ( empty ( $id )) {
if ( empty ( $params [ 'ids' ])) {
throw new NotFoundException ( __ ( 'Invalid {0}.' , $this -> ObjectAlias ));
}
$ids = $params [ 'ids' ];
} else {
$id = $this -> getInteger ( $id );
if ( ! is_null ( $id )) {
$ids = [ $id ];
} else {
throw new NotFoundException ( __ ( 'Invalid {0}.' , $this -> ObjectAlias ));
}
}
return $ids ;
}
private function getInteger ( $value )
{
return is_numeric ( $value ) ? intval ( $value ) : null ;
}
2020-06-09 15:57:16 +02:00
protected function massageFilters ( array $params ) : array
{
$massagedFilters = [
'simpleFilters' => [],
'relatedFilters' => []
];
if ( ! empty ( $params )) {
foreach ( $params as $param => $paramValue ) {
2020-06-21 21:33:38 +02:00
if ( strpos ( $param , '.' ) !== false ) {
2020-06-19 00:34:51 +02:00
$param = explode ( '.' , $param );
if ( $param [ 0 ] === $this -> Table -> getAlias ()) {
2020-06-09 15:57:16 +02:00
$massagedFilters [ 'simpleFilters' ][ implode ( '.' , $param )] = $paramValue ;
} else {
2020-06-19 00:34:51 +02:00
$massagedFilters [ 'relatedFilters' ][ implode ( '.' , $param )] = $paramValue ;
2020-06-09 15:57:16 +02:00
}
} else {
2020-06-21 21:33:38 +02:00
$massagedFilters [ 'simpleFilters' ][ $param ] = $paramValue ;
2020-06-09 15:57:16 +02:00
}
}
}
return $massagedFilters ;
}
2021-11-10 15:28:09 +01:00
public function setQuickFilters ( array $params , \Cake\ORM\Query $query , array $options ) : \Cake\ORM\Query
2020-06-09 15:57:16 +02:00
{
2021-11-10 15:28:09 +01:00
$quickFilterFields = $options [ 'quickFilters' ];
2020-06-09 15:57:16 +02:00
$queryConditions = [];
2021-01-15 16:58:46 +01:00
$this -> Controller -> set ( 'quickFilter' , empty ( $quickFilterFields ) ? [] : $quickFilterFields );
2021-11-10 15:28:09 +01:00
if ( $this -> metaFieldsSupported () && ! empty ( $options [ 'quickFilterForMetaField' ][ 'enabled' ])) {
$this -> Controller -> set ( 'quickFilterForMetaField' , [
'enabled' => $options [ 'quickFilterForMetaField' ][ 'enabled' ] ? ? false ,
'wildcard_search' => $options [ 'quickFilterForMetaField' ][ 'enabled' ] ? ? false ,
]);
}
2020-06-09 15:57:16 +02:00
if ( ! empty ( $params [ 'quickFilter' ]) && ! empty ( $quickFilterFields )) {
2021-01-15 11:49:20 +01:00
$this -> Controller -> set ( 'quickFilterValue' , $params [ 'quickFilter' ]);
2021-11-04 15:21:03 +01:00
$queryConditions = $this -> genQuickFilterConditions ( $params , $quickFilterFields );
2021-11-10 15:28:09 +01:00
if ( $this -> metaFieldsSupported () && ! empty ( $options [ 'quickFilterForMetaField' ][ 'enabled' ])) {
$searchValue = ! empty ( $options [ 'quickFilterForMetaField' ][ 'wildcard_search' ]) ? " % { $params [ 'quickFilter' ] } % " : $params [ 'quickFilter' ];
$metaFieldConditions = $this -> Table -> buildMetaFieldQuerySnippetForMatchingParent ([ 'value' => $searchValue ]);
$queryConditions [] = $metaFieldConditions ;
}
2021-10-28 09:00:20 +02:00
$query -> where ([ 'OR' => $queryConditions ]);
} else {
$this -> Controller -> set ( 'quickFilterValue' , '' );
}
return $query ;
}
2021-10-28 09:27:30 +02:00
public function genQuickFilterConditions ( array $params , array $quickFilterFields ) : array
2021-10-28 09:00:20 +02:00
{
$queryConditions = [];
foreach ( $quickFilterFields as $filterField ) {
if ( is_array ( $filterField )) {
reset ( $filterField );
$filterFieldName = array_key_first ( $filterField );
if ( ! empty ( $filterField [ $filterFieldName ])) {
$queryConditions [ $filterFieldName . ' LIKE' ] = '%' . $params [ 'quickFilter' ] . '%' ;
2021-01-15 11:32:47 +01:00
} else {
$queryConditions [ $filterField ] = $params [ 'quickFilter' ];
}
2021-10-28 09:00:20 +02:00
} else {
$queryConditions [ $filterField ] = $params [ 'quickFilter' ];
}
2020-06-09 15:57:16 +02:00
}
2021-10-28 09:00:20 +02:00
return $queryConditions ;
2020-06-09 15:57:16 +02:00
}
2021-02-26 10:36:06 +01:00
protected function setFilters ( $params , \Cake\ORM\Query $query , array $options ) : \Cake\ORM\Query
2020-06-09 15:57:16 +02:00
{
2021-02-26 10:36:06 +01:00
$filteringLabel = ! empty ( $params [ 'filteringLabel' ]) ? $params [ 'filteringLabel' ] : '' ;
unset ( $params [ 'filteringLabel' ]);
2021-08-30 15:11:21 +02:00
$filteringTags = ! empty ( $params [ 'filteringTags' ]) && $this -> taggingSupported () ? $params [ 'filteringTags' ] : '' ;
unset ( $params [ 'filteringTags' ]);
2021-02-26 10:36:06 +01:00
$customFilteringFunction = '' ;
2022-02-21 11:51:05 +01:00
$chosenFilter = [];
2021-02-26 10:36:06 +01:00
if ( ! empty ( $options [ 'contextFilters' ][ 'custom' ])) {
foreach ( $options [ 'contextFilters' ][ 'custom' ] as $filter ) {
if ( $filter [ 'label' ] == $filteringLabel ) {
$customFilteringFunction = $filter ;
$chosenFilter = $filter ;
break ;
2020-06-09 15:57:16 +02:00
}
}
}
2021-02-26 10:36:06 +01:00
2021-03-10 09:36:45 +01:00
$activeFilters = [];
2021-02-26 10:36:06 +01:00
if ( ! empty ( $customFilteringFunction [ 'filterConditionFunction' ])) {
$query = $customFilteringFunction [ 'filterConditionFunction' ]( $query );
2021-03-10 09:36:45 +01:00
$activeFilters [ 'filteringLabel' ] = $filteringLabel ;
2021-02-26 10:36:06 +01:00
} else {
if ( ! empty ( $chosenFilter )) {
$params = $this -> massageFilters ( $chosenFilter [ 'filterCondition' ]);
} else {
$params = $this -> massageFilters ( $params );
}
if ( ! empty ( $params [ 'simpleFilters' ])) {
foreach ( $params [ 'simpleFilters' ] as $filter => $filterValue ) {
if ( $filter === 'quickFilter' ) {
continue ;
}
2021-11-10 12:07:27 +01:00
$activeFilters [ $filter ] = $filterValue ;
2021-02-26 10:36:06 +01:00
if ( is_array ( $filterValue )) {
$query -> where ([( $filter . ' IN' ) => $filterValue ]);
} else {
$query = $this -> setValueCondition ( $query , $filter , $filterValue );
}
}
}
if ( ! empty ( $params [ 'relatedFilters' ])) {
foreach ( $params [ 'relatedFilters' ] as $filter => $filterValue ) {
2021-03-10 09:36:45 +01:00
$activeFilters [ $filter ] = $filterValue ;
2021-02-26 10:36:06 +01:00
$filterParts = explode ( '.' , $filter );
$query = $this -> setNestedRelatedCondition ( $query , $filterParts , $filterValue );
}
2020-06-09 15:57:16 +02:00
}
}
2021-08-30 15:11:21 +02:00
if ( $this -> taggingSupported () && ! empty ( $filteringTags )) {
$activeFilters [ 'filteringTags' ] = $filteringTags ;
$query = $this -> setTagFilters ( $query , $filteringTags );
}
2021-11-10 12:07:27 +01:00
if ( $this -> metaFieldsSupported ()) {
$filteringMetaFields = $this -> getMetaFieldFiltersFromQuery ();
if ( ! empty ( $filteringMetaFields )) {
$activeFilters [ 'filteringMetaFields' ] = $filteringMetaFields ;
}
$query = $this -> setMetaFieldFilters ( $query , $filteringMetaFields );
}
2021-03-10 09:36:45 +01:00
$this -> Controller -> set ( 'activeFilters' , $activeFilters );
2020-06-09 15:57:16 +02:00
return $query ;
}
2020-11-20 11:09:24 +01:00
2021-11-10 12:07:27 +01:00
protected function setMetaFieldFilters ( $query , $metaFieldFilters )
{
2021-11-10 15:28:09 +01:00
$metaFieldConditions = $this -> Table -> buildMetaFieldQuerySnippetForMatchingParent ( $metaFieldFilters );
$query -> where ( $metaFieldConditions );
return $query ;
2021-11-10 12:07:27 +01:00
}
2021-08-30 15:11:21 +02:00
protected function setTagFilters ( $query , $tags )
{
$modelAlias = $this -> Table -> getAlias ();
$subQuery = $this -> Table -> find ( 'tagged' , [
2021-10-01 15:13:18 +02:00
'name' => $tags ,
2021-11-03 11:47:10 +01:00
'OperatorAND' => true
2021-08-30 15:11:21 +02:00
]) -> select ( $modelAlias . '.id' );
2021-09-01 16:12:56 +02:00
return $query -> where ([ $modelAlias . '.id IN' => $subQuery ]);
2021-08-30 15:11:21 +02:00
}
2021-11-24 09:12:39 +01:00
// FIXME: Adding related condition with association having `through` setup might include duplicate in the result set
// We should probably rely on `innerJoinWith` and perform deduplication via `distinct`
// Or grouping by primary key for the main model (however this is not optimal/efficient/clean)
2021-02-24 11:11:29 +01:00
protected function setNestedRelatedCondition ( $query , $filterParts , $filterValue )
{
$modelName = $filterParts [ 0 ];
if ( count ( $filterParts ) == 2 ) {
$fieldName = implode ( '.' , $filterParts );
$query = $this -> setRelatedCondition ( $query , $modelName , $fieldName , $filterValue );
} else {
$filterParts = array_slice ( $filterParts , 1 );
2021-11-04 08:10:32 +01:00
$query = $query -> matching ( $modelName , function ( \Cake\ORM\Query $q ) use ( $filterParts , $filterValue ) {
2021-02-24 11:11:29 +01:00
return $this -> setNestedRelatedCondition ( $q , $filterParts , $filterValue );
});
}
return $query ;
}
protected function setRelatedCondition ( $query , $modelName , $fieldName , $filterValue )
{
2021-11-04 08:10:32 +01:00
return $query -> matching ( $modelName , function ( \Cake\ORM\Query $q ) use ( $fieldName , $filterValue ) {
2021-02-24 11:11:29 +01:00
return $this -> setValueCondition ( $q , $fieldName , $filterValue );
});
}
protected function setValueCondition ( $query , $fieldName , $value )
{
if ( strlen ( trim ( $value , '%' )) === strlen ( $value )) {
return $query -> where ([ $fieldName => $value ]);
} else {
2021-03-10 09:37:22 +01:00
return $query -> where ( function ( $exp , \Cake\ORM\Query $q ) use ( $fieldName , $value ) {
return $exp -> like ( $fieldName , $value );
});
2021-02-24 11:11:29 +01:00
}
}
2020-12-08 15:08:12 +01:00
protected function setFilteringContext ( $contextFilters , $params )
2020-12-07 16:06:01 +01:00
{
2020-12-08 15:08:12 +01:00
$filteringContexts = [];
2021-12-14 15:08:28 +01:00
if (
! isset ( $contextFilters [ '_all' ]) ||
! isset ( $contextFilters [ '_all' ][ 'enabled' ]) ||
! empty ( $contextFilters [ '_all' ][ 'enabled' ])
) {
$filteringContexts [] = [
'label' => ! empty ( $contextFilters [ '_all' ][ 'text' ]) ? h ( $contextFilters [ '_all' ][ 'text' ]) : __ ( 'All' )
];
2020-12-08 15:08:12 +01:00
}
if ( ! empty ( $contextFilters [ 'fields' ])) {
foreach ( $contextFilters [ 'fields' ] as $field ) {
$contextsFromField = $this -> getFilteringContextFromField ( $field );
foreach ( $contextsFromField as $contextFromField ) {
2021-01-14 15:30:16 +01:00
if ( is_bool ( $contextFromField )) {
$contextFromFieldText = sprintf ( '%s: %s' , $field , $contextFromField ? 'true' : 'false' );
} else {
2021-03-15 22:47:13 +01:00
$contextFromFieldText = sprintf ( '%s: %s' , $field , $contextFromField );
2021-01-14 15:30:16 +01:00
}
2020-12-08 15:08:12 +01:00
$filteringContexts [] = [
2021-01-14 15:30:16 +01:00
'label' => Inflector :: humanize ( $contextFromFieldText ),
2020-12-08 15:08:12 +01:00
'filterCondition' => [
$field => $contextFromField
]
];
}
2020-12-07 16:06:01 +01:00
}
}
2020-12-08 15:08:12 +01:00
if ( ! empty ( $contextFilters [ 'custom' ])) {
$filteringContexts = array_merge ( $filteringContexts , $contextFilters [ 'custom' ]);
}
$this -> Controller -> set ( 'filteringContexts' , $filteringContexts );
2020-12-07 16:06:01 +01:00
}
2022-03-09 08:55:59 +01:00
/**
* Create a fake filtering label set to the filter to be used by default if the request does not supply one
* This fake filtering label will then be used to set approriate filters on the query
*
* @ param array $options CRUD options
* @ param array $params Collected params from the request
* @ return array
*/
protected function fakeContextFilter ( $options , $params ) : array
{
if ( empty ( $params [ 'filteringLabel' ]) && ! empty ( $options [ 'contextFilters' ][ 'custom' ])) {
foreach ( $options [ 'contextFilters' ][ 'custom' ] as $contextFilter ) {
2022-10-25 14:50:11 +02:00
if ( ! empty ( $contextFilter [ 'default' ]) && empty ( $params )) {
2022-03-09 08:55:59 +01:00
$params [ 'filteringLabel' ] = $contextFilter [ 'label' ];
$this -> Controller -> set ( 'fakeFilteringLabel' , $contextFilter [ 'label' ]);
break ;
}
}
}
return $params ;
}
2021-03-10 09:38:41 +01:00
public function setParentConditionsForMetaFields ( $query , array $metaConditions )
2021-02-26 10:36:06 +01:00
{
$metaTemplates = $this -> MetaFields -> MetaTemplates -> find ( 'list' , [
'keyField' => 'name' ,
'valueField' => 'id'
]) -> where ([ 'name IN' => array_keys ( $metaConditions )]) -> all () -> toArray ();
$fieldsConditions = [];
foreach ( $metaConditions as $templateName => $templateConditions ) {
$metaTemplateID = isset ( $metaTemplates [ $templateName ]) ? $metaTemplates [ $templateName ] : - 1 ;
foreach ( $templateConditions as $conditions ) {
$conditions [ 'meta_template_id' ] = $metaTemplateID ;
$fieldsConditions [] = $conditions ;
}
}
$matchingMetaQuery = $this -> getParentIDQueryForMetaANDConditions ( $fieldsConditions );
return $query -> where ([ 'id IN' => $matchingMetaQuery ]);
}
private function getParentIDQueryForMetaANDConditions ( array $metaANDConditions )
{
if ( empty ( $metaANDConditions )) {
throw new Exception ( 'Invalid passed conditions' );
}
foreach ( $metaANDConditions as $i => $conditions ) {
2021-11-09 08:59:17 +01:00
$metaANDConditions [ $i ][ 'scope' ] = $this -> Table -> getBehavior ( 'MetaFields' ) -> getScope ();
2021-02-26 10:36:06 +01:00
}
$firstCondition = $this -> prefixConditions ( 'MetaFields' , $metaANDConditions [ 0 ]);
$conditionsToJoin = array_slice ( $metaANDConditions , 1 );
$query = $this -> MetaFields -> find ()
-> select ( 'parent_id' )
-> where ( $firstCondition );
foreach ( $conditionsToJoin as $i => $conditions ) {
$joinedConditions = $this -> prefixConditions ( " m { $i } " , $conditions );
$joinedConditions [] = " m { $i } .parent_id = MetaFields.parent_id " ;
$query -> rightJoin (
[ " m { $i } " => 'meta_fields' ],
$joinedConditions
);
}
return $query ;
}
private function prefixConditions ( string $prefix , array $conditions )
{
$prefixedConditions = [];
foreach ( $conditions as $condField => $condValue ) {
$prefixedConditions [ " ${ prefix}.${condField } " ] = $condValue ;
}
return $prefixedConditions ;
}
2021-08-26 12:06:12 +02:00
public function taggingSupported ()
{
return $this -> Table -> behaviors () -> has ( 'Tag' );
}
2021-11-10 09:02:51 +01:00
public function metaFieldsSupported ()
{
return $this -> Table -> hasBehavior ( 'MetaFields' );
}
2021-08-26 16:12:55 +02:00
public function setAllTags ()
{
$this -> Tags = TableRegistry :: getTableLocator () -> get ( 'Tags.Tags' );
$allTags = $this -> Tags -> find () -> all () -> toList ();
$this -> Controller -> set ( 'allTags' , $allTags );
}
2020-12-07 14:17:10 +01:00
public function toggle ( int $id , string $fieldName = 'enabled' , array $params = []) : void
{
if ( empty ( $id )) {
throw new NotFoundException ( __ ( 'Invalid {0}.' , $this -> ObjectAlias ));
}
$data = $this -> Table -> get ( $id , $params );
2022-01-26 16:10:33 +01:00
if ( isset ( $params [ 'afterFind' ])) {
$data = $params [ 'afterFind' ]( $data , $params );
}
2020-12-07 14:17:10 +01:00
if ( $this -> request -> is ([ 'post' , 'put' ])) {
2020-12-10 17:18:17 +01:00
if ( isset ( $params [ 'force_state' ])) {
$data -> { $fieldName } = $params [ 'force_state' ];
} else {
$data -> { $fieldName } = ! $data -> { $fieldName };
}
2020-12-10 15:18:02 +01:00
$savedData = $this -> Table -> save ( $data );
if ( $savedData !== false ) {
2021-11-04 08:10:32 +01:00
$message = __ (
'{0} field {1}. (ID: {2} {3})' ,
2020-12-07 14:17:10 +01:00
$fieldName ,
$data -> { $fieldName } ? __ ( 'enabled' ) : __ ( 'disabled' ),
2020-12-10 15:18:02 +01:00
Inflector :: humanize ( $this -> ObjectAlias ),
$data -> id
2020-12-07 14:17:10 +01:00
);
if ( $this -> Controller -> ParamHandler -> isRest ()) {
$this -> Controller -> restResponsePayload = $this -> RestResponse -> viewData ( $data , 'json' );
} else if ( $this -> Controller -> ParamHandler -> isAjax ()) {
2021-06-17 08:54:09 +02:00
$this -> Controller -> ajaxResponsePayload = $this -> RestResponse -> ajaxSuccessResponse ( $this -> ObjectAlias , 'toggle' , $savedData , $message );
2020-12-07 14:17:10 +01:00
} else {
$this -> Controller -> Flash -> success ( $message );
if ( empty ( $params [ 'redirect' ])) {
$this -> Controller -> redirect ([ 'action' => 'view' , $id ]);
} else {
$this -> Controller -> redirect ( $params [ 'redirect' ]);
}
}
} else {
2021-06-30 12:18:58 +02:00
$validationErrors = $data -> getErrors ();
$validationMessage = $this -> prepareValidationMessage ( $validationErrors );
2020-12-07 14:17:10 +01:00
$message = __ (
'{0} could not be modified.{1}' ,
$this -> ObjectAlias ,
2022-03-01 09:51:51 +01:00
empty ( $validationMessage ) ? '' : ' ' . __ ( 'Reason: {0}' , $validationMessage )
2020-12-07 14:17:10 +01:00
);
if ( $this -> Controller -> ParamHandler -> isRest ()) {
} else if ( $this -> Controller -> ParamHandler -> isAjax ()) {
2022-10-25 10:23:11 +02:00
$this -> Controller -> ajaxResponsePayload = $this -> RestResponse -> ajaxFailResponse ( $this -> ObjectAlias , 'toggle' , [], $message , $validationErrors );
2020-12-07 14:17:10 +01:00
} else {
$this -> Controller -> Flash -> error ( $message );
if ( empty ( $params [ 'redirect' ])) {
$this -> Controller -> redirect ([ 'action' => 'view' , $id ]);
} else {
$this -> Controller -> redirect ( $params [ 'redirect' ]);
}
}
}
}
$this -> Controller -> set ( 'entity' , $data );
$this -> Controller -> set ( 'fieldName' , $fieldName );
$this -> Controller -> viewBuilder () -> setLayout ( 'ajax' );
$this -> Controller -> render ( '/genericTemplates/toggle' );
}
2020-11-20 11:09:24 +01:00
public function toggleEnabled ( int $id , array $path , string $fieldName = 'enabled' ) : bool
{
if ( empty ( $id )) {
throw new NotFoundException ( __ ( 'Invalid {0}.' , $this -> ObjectAlias ));
}
$data = $this -> Table -> get ( $id );
if ( $this -> request -> is ( 'post' )) {
$data [ $fieldName ] = $data [ $fieldName ] ? true : false ;
$this -> Table -> save ( $data );
2021-06-17 08:54:09 +02:00
$this -> Controller -> restResponsePayload = $this -> RestResponse -> viewData ([ 'value' => $data [ $fieldName ]], 'json' );
2020-11-20 11:09:24 +01:00
} else {
if ( $this -> Controller -> ParamHandler -> isRest ()) {
2021-06-17 08:54:09 +02:00
$this -> Controller -> restResponsePayload = $this -> RestResponse -> viewData ([ 'value' => $data [ $fieldName ]], 'json' );
2020-11-20 11:09:24 +01:00
} else {
$this -> Controller -> set ( 'fieldName' , $fieldName );
$this -> Controller -> set ( 'currentValue' , $data [ $fieldName ]);
$this -> Controller -> set ( 'path' , $path );
$this -> Controller -> render ( '/genericTemplates/ajaxForm' );
}
}
}
2020-12-07 16:06:01 +01:00
2021-01-11 12:48:58 +01:00
private function getFilteringContextFromField ( $field )
2020-12-07 16:06:01 +01:00
{
2021-01-11 12:48:58 +01:00
$exploded = explode ( '.' , $field );
if ( count ( $exploded ) > 1 ) {
$model = $exploded [ 0 ];
$subField = $exploded [ 1 ];
2021-01-19 12:28:46 +01:00
$association = $this -> Table -> associations () -> get ( $model );
$associationType = $association -> type ();
if ( $associationType == 'oneToMany' ) {
$fieldToExtract = $subField ;
$associatedTable = $association -> getTarget ();
$query = $associatedTable -> find () -> rightJoin (
[ $this -> Table -> getAlias () => $this -> Table -> getTable ()],
[ sprintf ( '%s.id = %s.%s' , $this -> Table -> getAlias (), $associatedTable -> getAlias (), $association -> getForeignKey ())]
)
2021-11-04 08:10:32 +01:00
-> where ([
[ " ${ field } IS NOT " => NULL ]
]);
2021-01-19 12:28:46 +01:00
} else if ( $associationType == 'manyToOne' ) {
2021-01-14 16:34:21 +01:00
$fieldToExtract = sprintf ( '%s.%s' , Inflector :: singularize ( strtolower ( $model )), $subField );
2021-01-19 12:28:46 +01:00
$query = $this -> Table -> find () -> contain ( $model );
} else {
throw new Exception ( " Association ${ associationType } not supported in CRUD Component " );
2021-01-14 16:34:21 +01:00
}
2021-01-11 12:48:58 +01:00
} else {
2021-01-18 16:13:10 +01:00
$fieldToExtract = $field ;
$query = $this -> Table -> find ();
2021-01-11 12:48:58 +01:00
}
2021-01-18 16:13:10 +01:00
return $query -> select ([ $field ])
-> distinct ()
2022-06-08 11:56:09 +02:00
-> all ()
2021-01-18 16:13:10 +01:00
-> extract ( $fieldToExtract )
-> toList ();
2020-12-07 16:06:01 +01:00
}
2021-01-14 11:33:51 +01:00
2021-11-10 12:07:27 +01:00
private function getMetaFieldFiltersFromQuery () : array
{
$filters = [];
foreach ( $this -> request -> getQueryParams () as $filterName => $value ) {
$prefix = '_metafield' ;
if ( substr ( $filterName , 0 , strlen ( $prefix )) === $prefix ) {
$dissected = explode ( '_' , substr ( $filterName , strlen ( $prefix )));
if ( count ( $dissected ) == 3 ) { // Skip if template_id or template_field_id not provided
$filters [] = [
'meta_template_id' => intval ( $dissected [ 1 ]),
'meta_template_field_id' => intval ( $dissected [ 2 ]),
'value' => $value ,
];
}
}
}
return $filters ;
}
2021-01-14 11:33:51 +01:00
private function renderViewInVariable ( $templateRelativeName , $data )
{
$builder = new ViewBuilder ();
$builder -> disableAutoLayout () -> setTemplate ( " { $this -> TableAlias } / { $templateRelativeName } " );
$view = $builder -> build ( $data );
return $view -> render ();
}
2022-10-27 15:56:39 +02:00
protected function _getAssociatedTypeMap () : array
{
$typeMap = [];
foreach ( $this -> Controller -> filterFields as $filter ) {
$exploded = explode ( '.' , $filter );
if ( count ( $exploded ) > 1 ) {
$model = $exploded [ 0 ];
$subField = $exploded [ 1 ];
if ( $model == $this -> Table -> getAlias ()) {
$typeMap [ $filter ] = $this -> Table -> getSchema () -> typeMap ()[ $subField ] ? ? 'text' ;
} else {
$association = $this -> Table -> associations () -> get ( $model );
$associatedTable = $association -> getTarget ();
$typeMap [ $filter ] = $associatedTable -> getSchema () -> typeMap ()[ $subField ] ? ? 'text' ;
}
}
}
return $typeMap ;
}
2023-02-20 10:17:20 +01:00
protected function _validOrderFields ( $fields ) : bool
{
if ( ! is_array ( $fields )) {
$fields = [ $fields ];
}
foreach ( $fields as $field ) {
$exploded = explode ( '.' , $field );
if ( count ( $exploded ) > 1 ) {
$model = $exploded [ 0 ];
$subField = $exploded [ 1 ];
if ( $model == $this -> Table -> getAlias ()) {
if ( empty ( $this -> Table -> getSchema () -> typeMap ()[ $subField ])) {
return false ;
}
} else {
$association = $this -> Table -> associations () -> get ( $model );
$associatedTable = $association -> getTarget ();
if ( empty ( $associatedTable -> getSchema () -> typeMap ()[ $subField ])) {
return false ;
}
}
} else {
if ( empty ( $this -> Table -> getSchema () -> typeMap ()[ $field ])) {
return false ;
}
}
}
return true ;
}
2020-06-09 15:57:16 +02:00
}