Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
19370f6
updated
cedricfrancoys Nov 3, 2024
8830c6f
updated
cedricfrancoys Nov 3, 2024
628afa2
added support for invalid params with default as closure in announcement
cedricfrancoys Nov 7, 2024
677c177
Handle default value set by setting value
lucaslaurent04 Nov 13, 2024
49aebca
Add comments + modify method name from defaultSetting to defaultFromS…
lucaslaurent04 Nov 14, 2024
95e00f6
Refacto simplify
lucaslaurent04 Nov 14, 2024
44ec7cd
Merge pull request #198 from lucaslaurent04/handle-default-value-by-s…
cedricfrancoys Nov 14, 2024
b70eee7
added support for 'force_cascade'
cedricfrancoys Nov 14, 2024
a8efe70
minor refactoring
cedricfrancoys Nov 14, 2024
f42ef84
comments
cedricfrancoys Nov 14, 2024
de01efe
Add Setting type many2one that can save an integer or NULL and refers…
lucaslaurent04 Nov 14, 2024
27359b5
fixed support for defaultFromSetting
cedricfrancoys Nov 15, 2024
5a535e6
syntax
cedricfrancoys Nov 15, 2024
0623c9d
Merge pull request #199 from lucaslaurent04/add-many2one-setting-type
cedricfrancoys Nov 15, 2024
ca6bba3
added get() as alias for get_value()
cedricfrancoys Nov 15, 2024
6e401aa
Merge branch 'dev-2.0' of https://github.com/equalframework/equal int…
cedricfrancoys Nov 15, 2024
64bbc9c
minor refactor
cedricfrancoys Nov 15, 2024
6febfd8
updated translations
cedricfrancoys Nov 15, 2024
c3c1da6
fix DateTime namespace
cedricfrancoys Nov 20, 2024
48b61df
add support for account_registration flag
cedricfrancoys Nov 23, 2024
42d2579
updated
cedricfrancoys Nov 23, 2024
adb4443
updated
cedricfrancoys Nov 23, 2024
0240e65
updated
cedricfrancoys Nov 23, 2024
83095dd
updated
cedricfrancoys Nov 23, 2024
d3a949a
updated
cedricfrancoys Nov 23, 2024
4e5162f
updated
cedricfrancoys Nov 23, 2024
beadce1
updated
cedricfrancoys Nov 23, 2024
7d3d4f2
updated
cedricfrancoys Nov 23, 2024
7bf49bc
updated
cedricfrancoys Nov 23, 2024
4b2bb83
updated
cedricfrancoys Nov 23, 2024
89bc860
updated
cedricfrancoys Nov 23, 2024
7ed9b88
updated
cedricfrancoys Nov 23, 2024
10caa37
updated
cedricfrancoys Nov 23, 2024
904d8d8
updated
cedricfrancoys Nov 23, 2024
715ae53
updated
cedricfrancoys Nov 23, 2024
2147054
updated
cedricfrancoys Nov 23, 2024
8aea696
syntax
cedricfrancoys Nov 24, 2024
b71b225
added support for filtering by level
cedricfrancoys Nov 24, 2024
44a8da0
syntax
cedricfrancoys Nov 29, 2024
da857a6
removed invalid check on target order field
cedricfrancoys Nov 29, 2024
61f2a6f
Fix config generate action to work for use in b2 project
lucaslaurent04 Dec 2, 2024
7e012e0
Merge pull request #201 from lucaslaurent04/fix-config-generate
cedricfrancoys Dec 2, 2024
b7342cb
removed invalid checl on order field
cedricfrancoys Dec 2, 2024
d35f3ff
added isset for accessing
cedricfrancoys Dec 2, 2024
2c753ca
Merge branch 'dev-2.0' of https://github.com/equalframework/equal int…
cedricfrancoys Dec 2, 2024
c903ae1
added error feedback in callonce()
cedricfrancoys Dec 2, 2024
ea68a4b
added error feedback in callonce()
cedricfrancoys Dec 2, 2024
c4b0635
improve handling of exceptions within callonce
cedricfrancoys Dec 2, 2024
0eac4c1
added support for pruning logs based on LOGS_EXPIRY_DELAY
cedricfrancoys Dec 5, 2024
df1388b
fixed doc installation URL
cedricfrancoys Dec 5, 2024
c643e3e
removed (deprecated)
cedricfrancoys Dec 5, 2024
ff10213
sliced pruning to process logs by batch
cedricfrancoys Dec 5, 2024
2af391a
added request threshold
cedricfrancoys Dec 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ or clone with Git :
git clone https://github.com/equalframework/equal.git
```

For more info, see : [http://doc.equal.run/getting-started/installation](http://doc.equal.run/getting-started/installation/)
For more info, see : [https://doc.equal.run/getting-started/install/installation/](https://doc.equal.run/getting-started/install/installation/)

## Contributing
Contributions are what make the open-source community such an amazing place to learn, inspire, and create.
Expand Down
6 changes: 6 additions & 0 deletions config/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,12 @@
"description": "Flag for requesting to store meta data whenever an event occurs (creation, update, deletion or custom event).",
"help": "Keep in mind that enabling logging increases I/O operations and impacts performances."
},
"LOGS_EXPIRY_DELAY": {
"type": "integer",
"default": 12,
"description": "Duration, in months, for retaining logs in the database.",
"help": "This value is use in the logs_prune controller for auto-vacuuming logs."
},
"UPLOAD_MAX_FILE_SIZE": {
"type": "integer",
"usage": "amount/data",
Expand Down
19 changes: 18 additions & 1 deletion eq.lib.php
Original file line number Diff line number Diff line change
Expand Up @@ -1035,7 +1035,24 @@ public static function announce(array $announcement) {
foreach($announcement['params'] as $param => $config) {
// #memo - at some point condition had a clause "|| empty($body[$param])", remember not to alter received data!
if(in_array($param, $missing_params) && isset($config['default'])) {
$body[$param] = $config['default'];
$default_value = $config['default'];
// #memo - array can be used as callable descriptor but are not considered here
if( (is_string($default_value) || is_object($default_value)) && is_callable($default_value)) {
// either a php function (or a function from the global scope) or a closure object
if(is_object($default_value)) {
// default is a closure
$default_value = $default_value();
}
}
elseif(is_string($default_value) && strpos($default_value, '::')) {
list($class_name, $method_name) = explode('::', $default_value);
if(method_exists($class_name, $method_name)) {
/** @var \equal\orm\ObjectManager */
$orm = $container->get('orm');
$default_value = $orm->callonce($class_name, $method_name);
}
}
$body[$param] = $default_value;
}
if(!array_key_exists($param, $body)) {
// ignore optional params without default value (this allows PATCH of objects on specific fields only)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public function adaptOut($value, $usage, $locale='en') {
if(is_null($value)) {
return null;
}
return ($value)?'1':'0';
return ($value) ? '1': '0';
}

}
12 changes: 7 additions & 5 deletions lib/equal/error/Reporter.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public static function handleThrowable($exception) {
$msg = $exception->getMessage();
// retrieve instance and log error
$instance = self::getInstance();
// #todo #bug - $backtrace may contain non json_encodable objects (which leads to an error at file_put_contents)
$backtrace = $exception->getTrace();
if(count($backtrace)) {
$trace = array_shift($backtrace);
Expand Down Expand Up @@ -159,12 +160,13 @@ private function log($code, $msg, $trace) {
'mtime' => substr($time_parts[0], 2, 6),
'level' => qn_debug_code_name($code),
'mode' => qn_debug_mode_name($mode),
'class' => (isset($trace['class']))?$trace['class']:'',
'function' => (isset($trace['function']))?(strlen($trace['function'])?$trace['function'].'()':'[main]'):'',
'file' => (isset($trace['file']))?$trace['file']:'',
'line' => (isset($trace['line']))?$trace['line']:'',
'class' => (isset($trace['class'])) ? $trace['class'] : '',
'function' => (isset($trace['function'])) ? (strlen($trace['function'])?$trace['function'].'()':'[main]') : '',
'file' => (isset($trace['file'])) ? $trace['file'] : '',
'line' => (isset($trace['line'])) ? $trace['line'] : '',
'message' => $msg,
'stack' => (isset($trace['stack']))?$trace['stack']:[]
// #memo - in case of error, forcing trace stack to empty array
'stack' => (isset($trace['stack'])) ? $trace['stack'] : []
];

// append backtrace if required (fatal errors)
Expand Down
12 changes: 8 additions & 4 deletions lib/equal/orm/Collection.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -835,14 +835,14 @@ public function read($fields, $lang=null) {
}
$children_fields = [];
foreach($subfields as $key => $val) {
$children_fields[] = (!is_numeric($key))?$key:$val;
$children_fields[] = (!is_numeric($key)) ? $key : $val;
}
// read all targeted children objects at once
$this->orm->read($target['foreign_object'], $children_ids, $children_fields, ($lang)?$lang:$this->lang);
$this->orm->read($target['foreign_object'], $children_ids, $children_fields, ($lang) ? $lang : $this->lang);
// assign retrieved values to the objects they relate to
foreach($this->objects as $id => $object) {
/** @var Collection */
$children = $target['foreign_object']::ids($this->objects[$id][$field])->read($subfields, ($lang)?$lang:$this->lang);
$children = $target['foreign_object']::ids($this->objects[$id][$field])->read($subfields, ($lang) ? $lang : $this->lang);
if($target['result_type'] == 'many2one') {
// #memo - result might be either null or a Model object (which might contain sub-collections)
$this->objects[$id][$field] = $children->first();
Expand Down Expand Up @@ -973,7 +973,11 @@ public function delete($permanent=false) {
public function transition($transition) {
// retrieve targeted identifiers
$res = $this->orm->transition($this->class, $this->ids(), $transition);
if(count($res)) {
if($res < 0) {
trigger_error("ORM::unexpected error for transition '{$transition}' on '{$this->class}' objects:".$this->orm->getLastError(), EQ_REPORT_WARNING);
throw new \Exception('transition_failed', $res);
}
elseif(count($res)) {
throw new \Exception(serialize($res), EQ_ERROR_NOT_ALLOWED);
}
return $this;
Expand Down
2 changes: 1 addition & 1 deletion lib/equal/orm/DateReference.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public function parse($descriptor) {

if(preg_match('/date\.(this|prev|next)(\((\d*)\))?\.(day|week|month|quarter|semester|year)(\.(first|last|get\((.+)\)))?/', $descriptor, $matches)) {
// init at today
$date = new DateTime();
$date = new \DateTime();

$origin = $matches[1];
$offset = isset($matches[3]) && $matches[3] !== '' ? (int)$matches[3] : 1;
Expand Down
38 changes: 38 additions & 0 deletions lib/equal/orm/Model.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
namespace equal\orm;

use core\setting\Setting;
use equal\services\Container;

/**
Expand Down Expand Up @@ -141,6 +142,7 @@ private function setDefaults($values=[]) {
$container = Container::getInstance();
$orm = $container->get('orm');
$defaults = $this->getDefaults();
$setting_defaults = $this->getSettingDefaults();
// reset fields values
$this->values = [];
$fields = array_keys($this->schema);
Expand All @@ -166,6 +168,32 @@ private function setDefaults($values=[]) {
// default is a method of the class (or parents')
$this->values[$field] = $orm->callonce($this->getType(), $defaults[$field]);
}
elseif($defaults[$field] === 'defaultFromSetting') {
$class_name = get_called_class();

// create the setting code prefix
// @example "core\alert\MessageModel" --> alert.message_model

// split parts into an array
$parts = explode('\\', $class_name);
$package = array_shift($parts);

// use dots instead of backslashes
$class_name = implode('.', $parts);
// convert PascalCase to snake_case
$setting_code_prefix = strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $class_name));

$default = Setting::get_value(
$package,
'default',
"$setting_code_prefix.$field",
$setting_defaults[$field] ?? null
);

if(!is_null($default)) {
$this->values[$field] = $default;
}
}
else {
// default is a scalar value
$this->values[$field] = $defaults[$field];
Expand Down Expand Up @@ -513,6 +541,16 @@ public function getDefaults() {
return $defaults;
}

public function getSettingDefaults() {
$setting_defaults = [];
foreach($this->schema as $field => $definition) {
if(isset($definition['setting_default'])) {
$setting_defaults[$field] = $definition['setting_default'];
}
}
return $setting_defaults;
}

/**
* Provide the list of unique rules (array of combinations of fields).
* This method can be overridden to define a more precise set of unique constraints (i.e when keys are formed of several fields).
Expand Down
60 changes: 38 additions & 22 deletions lib/equal/orm/ObjectManager.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -644,8 +644,9 @@ private function load($class, $ids, $fields, $lang) {
if(!ObjectManager::checkFieldAttributes(self::$mandatory_attributes, $schema, $field)) {
throw new Exception("missing at least one mandatory attribute for field '$field' of class '$class'", QN_ERROR_INVALID_PARAM);
}
$order = (isset($schema[$field]['order']) && isset($schema[$schema[$field]['order']]))?$schema[$field]['order']:'id';
$sort = (isset($schema[$field]['sort']))?$schema[$field]['sort']:'asc';
// #todo - we should check that order field exists in targeted entity
$order = (isset($schema[$field]['order'])) ? $schema[$field]['order'] : 'id';
$sort = (isset($schema[$field]['sort'])) ? $schema[$field]['sort'] : 'asc';
$domain = [
[
[$schema[$field]['foreign_field'], 'in', $ids],
Expand All @@ -661,9 +662,12 @@ private function load($class, $ids, $fields, $lang) {
$domain = $domain_tmp->toArray();
}
// #todo - add support for sorting on m2o fields (for now user needs to use usort)
// #todo - this is invalid, check should point to the target schema (foreign_object)
/*
if($schema[$order]['type'] == 'many2one' || (isset($schema[$order]['result_type']) && $schema[$order]['result_type'] == 'many2one') ) {
$order = 'id';
}
*/
// obtain the ids by searching inside the foreign object's table
$result = $om->db->getRecords(
$om->getObjectTableName($schema[$field]['foreign_object']),
Expand Down Expand Up @@ -844,6 +848,7 @@ private function load($class, $ids, $fields, $lang) {
}
catch(Exception $e) {
trigger_error("ORM::".$e->getMessage(), QN_REPORT_ERROR);
$this->last_error = $e->getMessage();
throw new Exception('unable to load object fields', $e->getCode());
}
}
Expand Down Expand Up @@ -1104,6 +1109,7 @@ private function store($class, $ids, $fields, $lang) {
}
catch (Exception $e) {
trigger_error("ORM::".$e->getMessage(), QN_REPORT_ERROR);
$this->last_error = $e->getMessage();
throw new Exception('unable to store object fields', $e->getCode());
}
}
Expand All @@ -1129,7 +1135,7 @@ public function callonce($class, $method, $ids=[], $values=[], $lang=null, $sign

$result = [];

$lang = ($lang)?$lang:constant('DEFAULT_LANG');
$lang = ($lang) ? $lang : constant('DEFAULT_LANG');
$called_class = $class;
$called_method = $method;

Expand Down Expand Up @@ -1212,14 +1218,16 @@ public function callonce($class, $method, $ids=[], $values=[], $lang=null, $sign
if($res !== null) {
$result = $res;
}
// unstack global object_methods state
$this->object_methods = $object_methods_state;
}
catch(\Exception $e) {
$result = $e->getCode();
// #memo - there is no depth limit so, exceptions must be relayed to caller
// unstack global object_methods state
$this->object_methods = $object_methods_state;
throw $e;
}

// unstack global object_methods state
$this->object_methods = $object_methods_state;

return $result;
}

Expand Down Expand Up @@ -2504,7 +2512,7 @@ public function canTransition($class, $ids, $transition) {
* @param array $ids Array of ids of the objects to delete.
* @param string $transition Name of the requested workflow transition (signal).
*
* @return array Returns an associative array containing invalid fields with their associated error_message_id.
* @return mixed Returns an associative array containing invalid fields with their associated error_message_id.
* An empty array means all fields are valid. In case of error, the method returns a negative integer.
*/
public function transition($class, $ids, $transition) {
Expand All @@ -2514,28 +2522,36 @@ public function transition($class, $ids, $transition) {
if(count($res)) {
return $res;
}
$res = [];
$table_name = $this->getObjectTableName($class);
$model = $this->getStaticInstance($class);
$workflow = $model->getWorkflow();
$lang = constant('DEFAULT_LANG');
// read status field for retrieved objects
$objects = $this->read($class, $ids, ['status']);
foreach($objects as $id => $object) {
// reaching this part means all objects have a status and a workflow in which given transition is defined and valid for requested mutation
$t_descr = $workflow[$object['status']]['transitions'][$transition];
// if a 'onbefore' method is defined for applied transition, call it
if(isset($t_descr['onbefore'])) {
$this->callonce($class, $t_descr['onbefore'], $id);
}
// status field is always writeable (we don't call `update()` to bypass checks)
$this->cache[$table_name][$id][$lang]['status'] = $t_descr['status'];
$this->store($class, (array) $id, ['status'], $lang);
// if a 'onafter' method is defined for applied transition, call it
if(isset($t_descr['onafter'])) {
$this->callonce($class, $t_descr['onafter'], $id);
try {
foreach($objects as $id => $object) {
// reaching this part means all objects have a status and a workflow in which given transition is defined and valid for requested mutation
$t_descr = $workflow[$object['status']]['transitions'][$transition];
// if a 'onbefore' method is defined for applied transition, call it
if(isset($t_descr['onbefore'])) {
$this->callonce($class, $t_descr['onbefore'], $id);
}
// status field is always writeable (we don't call `update()` to bypass checks)
$this->cache[$table_name][$id][$lang]['status'] = $t_descr['status'];
$this->store($class, (array) $id, ['status'], $lang);
// if a 'onafter' method is defined for applied transition, call it
if(isset($t_descr['onafter'])) {
$this->callonce($class, $t_descr['onafter'], $id);
}
}
}
return [];
catch(\Exception $e) {
trigger_error("ORM::".$e->getMessage(), QN_REPORT_WARNING);
$this->last_error = $e->getMessage();
$res = $e->getCode();
}
return $res;
}

/**
Expand Down
Loading