Skip to content

Commit b6d8151

Browse files
committed
Merge remote-tracking branch 'refs/remotes/gitdedalo/v7_developer' into v7_developer
2 parents 9c222f2 + f87584b commit b6d8151

18 files changed

Lines changed: 1019 additions & 122 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ vendor/*
8787
#composer.json
8888
#composer.lock
8989
composer.phar
90+
composer.local.json
9091
/var
9192

9293
# Ignore paths that contain generated content.

config/sample.config.php

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@
132132

133133

134134
// required files
135+
// version. Info about current version and build
136+
include(DEDALO_CORE_PATH . '/base/version.inc');
135137
// logger. Logger class
136138
include(DEDALO_CORE_PATH . '/logger/class.logger.php');
137139
// core_functions. Basic common functions (before session start)
@@ -142,21 +144,36 @@
142144
include(DEDALO_CONFIG_PATH . '/config_db.php');
143145
// dd_tipos. List of main Dédalo resolved tipos
144146
include(DEDALO_CORE_PATH . '/base/dd_tipos.php');
145-
// version. Info about current version and build
146-
include(DEDALO_CORE_PATH . '/base/version.inc');
147147

148148

149149

150-
// SESSIONS
150+
// sessions
151151
define('DEDALO_SESSIONS_PATH', dirname(DEDALO_ROOT_PATH, 2) . '/sessions');
152152

153-
if (session_status()!==PHP_SESSION_ACTIVE) {
153+
// handler: files | redis | memcached | postgresql | user
154+
if (!defined('DEDALO_SESSION_HANDLER')) {
155+
define('DEDALO_SESSION_HANDLER', 'files');
156+
}
154157

155-
// HANDLER
156-
$SESSION_HANDLER = 'files'; // files | memcached | user | postgresql
157-
define('DEDALO_SESSION_HANDLER', $SESSION_HANDLER);
158+
// save_path: define explicitly if needed (e.g. redis)
159+
if (!defined('DEDALO_SESSION_SAVE_PATH')) {
160+
switch (DEDALO_SESSION_HANDLER) {
161+
case 'redis':
162+
define('DEDALO_SESSION_SAVE_PATH', 'tcp://127.0.0.1:6379');
163+
break;
164+
case 'memcached':
165+
define('DEDALO_SESSION_SAVE_PATH', '127.0.0.1:11211');
166+
break;
167+
case 'files':
168+
default:
169+
define('DEDALO_SESSION_SAVE_PATH', DEDALO_SESSIONS_PATH);
170+
break;
171+
}
172+
}
173+
174+
if (session_status()!==PHP_SESSION_ACTIVE && !defined('DEDALO_RR_WORKER')) {
158175

159-
// LIFETIME
176+
// lifetime
160177
// Set max duration of dedalo user session
161178
// Use ini directive to set session.gc_maxlifetime (Garbage Collection lifetime)
162179
// Use session_cache_expire to set duration of session
@@ -165,16 +182,20 @@
165182
$session_duration_hours = $session_duration_hours ?? 8;
166183
$timeout_seconds = intval($session_duration_hours*3600); // in seconds
167184

185+
// session name
186+
$sesion_name = 'dedalo_'.DEDALO_ENTITY;
187+
168188
// session start
169189
$cookie_secure = (DEDALO_PROTOCOL==='https://');
170190
$cookie_samesite = (DEVELOPMENT_SERVER===true) ? 'Lax' : 'Strict';
191+
171192
session_start_manager([
172-
'save_handler' => 'files',
193+
'save_handler' => DEDALO_SESSION_HANDLER,
173194
'timeout_seconds' => $timeout_seconds,
174-
'save_path' => DEDALO_SESSIONS_PATH,
195+
'save_path' => DEDALO_SESSION_SAVE_PATH,
175196
// 'additional_save_path' => false, bool optional
176197
'prevent_session_lock' => defined('PREVENT_SESSION_LOCK') ? PREVENT_SESSION_LOCK : false,
177-
'session_name' => 'dedalo_'.DEDALO_ENTITY,
198+
'session_name' => $sesion_name,
178199
// cookie params
179200
'cookie_secure' => $cookie_secure, // Only https (true | false)
180201
'cookie_samesite' => $cookie_samesite // (None | Lax | Strict)

core/api/v1/common/class.dd_utils_api.php

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1543,6 +1543,105 @@ public static function get_process_status(object $rqo) {
15431543

15441544

15451545

1546+
/**
1547+
* GET_PROCESS_STATUS_POLL
1548+
* One-shot polling version of get_process_status.
1549+
* Reads the process file once and returns a standard JSON response.
1550+
* Unlike the SSE version (`get_process_status`), this method does NOT use
1551+
* streaming, die(), or blocking loops — making it compatible with both
1552+
* PHP-FPM and RoadRunner worker contexts.
1553+
*
1554+
* The client calls this endpoint repeatedly via setInterval to monitor
1555+
* process progress with the same functional result as SSE at 1s update_rate.
1556+
*
1557+
* @param object $rqo
1558+
* {
1559+
* options: {
1560+
* pid: int Process ID
1561+
* pfile: string Process output file name
1562+
* }
1563+
* }
1564+
* @return object $response
1565+
* {
1566+
* result: bool
1567+
* pid: int
1568+
* pfile: string
1569+
* is_running: bool
1570+
* data: object { msg, counter, total, ... }
1571+
* time: string Current server time
1572+
* errors: array
1573+
* }
1574+
*/
1575+
public static function get_process_status_poll(object $rqo) : object {
1576+
1577+
// session unlock
1578+
session_write_close();
1579+
1580+
// response
1581+
$response = new stdClass();
1582+
$response->result = false;
1583+
$response->msg = 'Error. Request failed ['.__FUNCTION__.']';
1584+
$response->errors = [];
1585+
1586+
// only logged users can access process status
1587+
if (login::is_logged()!==true) {
1588+
$response->msg = 'Authentication error: please login';
1589+
$response->errors[] = 'Not logged';
1590+
return $response;
1591+
}
1592+
1593+
// options
1594+
$pfile = $rqo->options->pfile ?? null;
1595+
$pid = $rqo->options->pid ?? null;
1596+
1597+
// mandatory vars
1598+
if (empty($pfile) || empty($pid)) {
1599+
$response->msg = 'Error: pfile and pid are mandatory';
1600+
$response->errors[] = 'Missing pfile or pid';
1601+
return $response;
1602+
}
1603+
1604+
// process
1605+
$process = new process();
1606+
$process->setPid($pid);
1607+
$process->setFile(process::get_process_path() .'/'. $pfile);
1608+
1609+
// read process info (one shot)
1610+
$is_running = $process->status(); // bool
1611+
$array_data = $process->read(); // array
1612+
1613+
// decode last line
1614+
$value = isset($array_data[0])
1615+
? (json_decode($array_data[0]) ?? $array_data[0])
1616+
: '';
1617+
1618+
$data = is_object($value)
1619+
? $value
1620+
: (object)['msg' => $value];
1621+
1622+
// clean up finished process
1623+
if ($is_running===false) {
1624+
processes::delete_process_item(
1625+
$pid,
1626+
logged_user_id()
1627+
);
1628+
}
1629+
1630+
// response
1631+
$response->result = true;
1632+
$response->pid = $pid;
1633+
$response->pfile = $pfile;
1634+
$response->is_running = $is_running;
1635+
$response->data = $data;
1636+
$response->time = date("Y-m-d H:i:s");
1637+
$response->errors = [];
1638+
1639+
1640+
return $response;
1641+
}//end get_process_status_poll
1642+
1643+
1644+
15461645
/**
15471646
* STOP_PROCESS
15481647
* @param object $rqo

core/common/js/ui.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,7 @@ export const ui = {
485485

486486
// css
487487
const ar_css = [
488+
'mini',
488489
instance.model + '_mini' // add suffix '_mini'
489490
]
490491
wrapper.classList.add(...ar_css)

core/component_common/js/component_common.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1573,7 +1573,7 @@ export const get_dataframe = async function(options) {
15731573
const mode = options.mode
15741574
const lang = options.lang
15751575

1576-
const request_config = self.context.request_config || null
1576+
const request_config = self.context?.request_config || null
15771577

15781578
// original_dataframe_ddo
15791579
const original_dataframe_ddo = request_config

core/component_relation_common/trait.search_component_relation_common.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ protected static function extract_normalized_relation_q(object $query_object) :
6060
// remove the id property from q_raw if exists
6161
if (is_object($q_raw) && isset($q_raw->id)) {
6262
unset($q_raw->id);
63+
} elseif (is_array($q_raw)) {
64+
foreach ($q_raw as $item) {
65+
if (is_object($item) && isset($item->id)) {
66+
unset($item->id);
67+
}
68+
}
6369
}
6470

6571
// For unification, all non string are JSON encoded

core/component_select/js/component_select.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,4 +167,76 @@ component_select.prototype.add_new_element = async function(target_section_tipo)
167167

168168

169169

170+
/**
171+
* BUILD_CHANGED_DATA_ITEM
172+
* Parses the select value and builds a frozen changed_data_item object.
173+
* Used by edit views (via handle_select_change) and search view (directly).
174+
* @param HTMLSelectElement select
175+
* @param int|null id
176+
* @return object {changed_data_item, parsed_value}
177+
*/
178+
export const build_changed_data_item = function(select, id=null) {
179+
180+
// parse select value from JSON string to object locator
181+
const parsed_value = (select.value.length > 0)
182+
? JSON.parse(select.value)
183+
: null
184+
185+
// add id to parsed_value if available
186+
if (parsed_value && id) {
187+
parsed_value.id = id
188+
}
189+
190+
// build changed_data_item
191+
const changed_data_item = Object.freeze({
192+
action : (parsed_value != null) ? 'update' : 'remove',
193+
id : id,
194+
value : parsed_value // object locator or null expected
195+
})
196+
197+
return {
198+
changed_data_item : changed_data_item,
199+
parsed_value : parsed_value
200+
}
201+
}//end build_changed_data_item
202+
203+
204+
205+
/**
206+
* HANDLE_SELECT_CHANGE
207+
* Common change handler for component_select across all edit views.
208+
* Parses the select value, builds changed_data_item, sets changed_data,
209+
* and saves via change_value. Returns parsed_value for view-specific hooks.
210+
* @param object self - Component instance
211+
* @param HTMLSelectElement select - The select DOM element
212+
* @param int|null id - Entry id from data
213+
* @return object|null parsed_value - The parsed locator or null
214+
*/
215+
export const handle_select_change = async function(self, select, id=null) {
216+
217+
// resolve id from current data if not provided
218+
// (when component was initially empty, the closure id is null,
219+
// but after first save the entry gets an id from the API)
220+
if (id === null) {
221+
id = self.data.entries?.[0]?.id ?? null
222+
}
223+
224+
// build changed_data_item (parse + freeze)
225+
const {changed_data_item, parsed_value} = build_changed_data_item(select, id)
226+
227+
// fix instance changed_data
228+
self.set_changed_data(changed_data_item)
229+
230+
// force to save on every change
231+
await self.change_value({
232+
changed_data : [changed_data_item],
233+
refresh : false,
234+
remove_dialog : false
235+
})
236+
237+
return parsed_value
238+
}//end handle_select_change
239+
240+
241+
170242
// @license-end

core/component_select/js/render_search_component_select.js

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
// imports
88
import {event_manager} from '../../common/js/event_manager.js'
99
import {ui} from '../../common/js/ui.js'
10+
import {build_changed_data_item} from './component_select.js'
1011

1112

1213

@@ -143,18 +144,13 @@ const get_content_value = (i, current_value, self) => {
143144
// change event
144145
select.addEventListener('change', function(){
145146

146-
const parsed_value = (select.value.length>0) ? JSON.parse(select.value) : null
147-
148-
const changed_data_item = Object.freeze({
149-
action : (parsed_value != null) ? 'update' : 'remove',
150-
id : (parsed_value != null) ? parsed_value.id : null,
151-
value : parsed_value
152-
})
147+
// build changed_data_item from select value
148+
// read id dynamically from self.data (not from stale closure)
149+
const current_id = self.data.entries?.[i]?.id ?? null
150+
const {changed_data_item} = build_changed_data_item(select, current_id)
153151

154152
// update the instance data (previous to save)
155153
self.update_data_value(changed_data_item)
156-
// set data.changed_data. The change_data to the instance
157-
// self.data.changed_data = changed_data
158154
// publish search. Event to update the dom elements of the instance
159155
event_manager.publish('change_search_element', self)
160156
})//end event change

0 commit comments

Comments
 (0)