Compare commits

...
Author SHA1 Message Date
Paolo c636b3ce05 Merge branch 'master' into feature-76203/Asynchrone_Tasks 2026-09-30 14:01:05 +02:00
Harald Bamberger ce62713790 Merge branch 'bug-82297/ausweis-date-preview-issue' 2026-09-30 12:11:13 +02:00
Harald Bamberger 34761db664 rename phrase 2026-09-30 10:56:30 +02:00
Harald Bamberger c7131ed775 revert changes to old cis since issue does not occur there 2026-09-30 10:53:01 +02:00
Harald Bamberger b57e1984a8 Merge branch 'master' into bug-82297/ausweis-date-preview-issue 2026-09-30 10:20:28 +02:00
Harald Bamberger acedc11be8 adapt styling detailheader employees 2026-09-24 14:39:38 +02:00
Harald Bamberger 15c1da74a2 CisMenu: do not overwrite calculated url if menuitem is multiple times in menu tree 2026-09-23 16:09:42 +02:00
Harald Bamberger d33b45d7cb Merge branch 'bhdev_backport_compat' 2026-09-23 14:58:26 +02:00
Andreas Österreicher b3b6d847a7 Merge branch 'feature-62260/international_skills_groupheader_filter' 2026-09-23 14:36:13 +02:00
Andreas Österreicher 80365de28c Merge branch 'feature-62602/mehrfachnennungen_innerhalb_einer_kategorie' 2026-09-23 14:35:36 +02:00
Andreas Österreicher 2d74522a4f Merge branch 'feature-54225/reihungstest_ablauf_fuer_quereinsteiger' 2026-09-23 12:28:35 +02:00
ma0048 d00aef66e1 master gemerged 2026-09-23 11:32:01 +02:00
ma0048 241eec3ef2 Merge remote-tracking branch 'origin/master' into feature-54225/reihungstest_ablauf_fuer_quereinsteiger
# Conflicts:
#	cis/testtool/menu.php
#	system/dbupdate_3.4.php
2026-09-23 11:29:43 +02:00
Harald Bamberger 8b44e4475b Merge branch 'master' of github.com:FH-Complete/FHC-Core into bhdev_backport_compat 2026-09-22 16:51:34 +02:00
Harald Bamberger d251987850 backport compat controller and component 2026-09-22 16:50:56 +02:00
ma0048 224cef7fad bug beim Loeschen von Antworten eines Prueflings in reihungstet_administration.php fixed 2026-09-17 13:29:58 +02:00
Paolo d34192f2c3 Merge branch 'master' into feature-76203/Asynchrone_Tasks 2026-09-10 13:19:47 +02:00
Ivymaster 5fff45e302 Added check for date existence 2026-08-27 11:00:43 +02:00
Paolo 4defb96147 Added examples 2026-08-13 10:40:45 +02:00
Paolo 61982b1199 Merge branch 'master' into feature-76203/Asynchrone_Tasks 2026-07-09 11:01:05 +02:00
Andreas Österreicher 9629345473 Merge branch 'master' into feature-62260/international_skills_groupheader_filter 2026-07-08 15:28:23 +02:00
Andreas Österreicher 4d72b0047a Merge branch 'master' into feature-62602/mehrfachnennungen_innerhalb_einer_kategorie 2026-07-08 15:26:41 +02:00
Paolo d50590aefb Better check of the MAX LRTs that a user can schedule 2026-06-23 15:04:54 +02:00
Paolo c40ee917df Refactored main components 2026-06-23 14:59:34 +02:00
Paolo 8126665acd Improvements and updated documentation 2026-06-23 12:33:44 +02:00
Paolo 5826bf5b70 Added check if the user is not opening too many tasks 2026-06-22 16:46:40 +02:00
Paolo fc5ba954fb Added new filters for the LogsViewer app to get logs from the LRTs 2026-06-22 16:29:28 +02:00
Paolo 4e9bf3676d application/controllers/jobs/LongRunTaskExecJob.php now provides a way to kill hanging LRTs 2026-06-22 16:24:51 +02:00
Paolo 31410f0bb9 Merge branch 'master' into feature-76203/Asynchrone_Tasks 2026-06-22 15:48:15 +02:00
Paolo 1136d9dca1 Better output handling 2026-06-22 15:47:39 +02:00
Paolo 967f53d73c Merge branch 'master' into feature-76203/Asynchrone_Tasks 2026-06-19 13:57:49 +02:00
Paolo 8c24292195 2nd draft 2026-06-19 13:54:38 +02:00
Paolo aba680cd52 Long Run Tasks first draft 2026-04-28 16:53:25 +02:00
ma0048 c664c14afc phrase hinzugefuegt 2025-12-18 11:10:08 +01:00
ma0048 9183c4f04c massnahme einmalig flag hinzugefuegt 2025-10-01 12:03:18 +02:00
ma0048 6495f741b9 added missing phrase 2025-07-09 14:02:41 +02:00
ma0048 d3210f0d3d - foreign keys hinzugefuegt
- beim zuruecksetzen der gebiete, wird auch tbl_pruefling_gebiet zurueckgesetzt
2025-02-18 13:51:20 +01:00
ma0048 a0dce925d4 - rt holt sich zusätzlich alle gebiete aus den semestern davor 2025-01-14 10:56:28 +01:00
29 changed files with 1635 additions and 85 deletions
+20
View File
@@ -0,0 +1,20 @@
<?php
/**
* Configs for the Long Run Tasks
*/
if (! defined('BASEPATH')) exit('No direct script access allowed');
// Maximum LRTs for a single user in parallel
$config['lrt_max_number_single_user'] = 10;
// Maximum LRTs for the whole system in parallel
$config['lrt_max_number_system'] = 100;
// Maximum running time in hours for a single LRT before killing it
$config['lrt_max_run_timeout'] = 24;
// List of existing LRT types
$config['lrt_types'] = array('LRTDummy');
+44
View File
@@ -0,0 +1,44 @@
<?php
if (!defined('BASEPATH'))
exit('No direct script access allowed');
class Compat extends Auth_Controller
{
/**
* Constructor
*/
public function __construct()
{
parent::__construct([
'ci' => ['basis/cis:r'],
'legacy' => ['basis/cis:r'],
]);
}
// -----------------------------------------------------------------------------------------------------------------
// Public methods
/**
* @access public
* @return void
*/
public function ci()
{
$this->load->view('CisRouterView/CisRouterView.php',['route' => 'Compat']);
}
/**
* @access public
* @return void
*/
public function legacy()
{
$this->load->view('CisRouterView/CisRouterView.php',['route' => 'Compat']);
}
}
@@ -67,8 +67,11 @@ class CisMenu extends FHCAPI_Controller
private function generateUrlForMenuItem($menuItem)
{
$menuItem->url = $this->menuItemUrlHelper($menuItem);
unset($menuItem->content);
if(!isset($menuItem->url))
{
$menuItem->url = $this->menuItemUrlHelper($menuItem);
unset($menuItem->content);
}
if ($menuItem->childs && count($menuItem->childs)) {
$menuItem->childs = $this->generateUrlsForMenuItems($menuItem->childs);
@@ -83,7 +86,7 @@ class CisMenu extends FHCAPI_Controller
return site_url("/CisVue/Cms/content/" . $menuItem->content_id);
}
if (!$menuItem->content || !mb_strlen($menuItem->content)) {
if (!isset($menuItem->content) || !$menuItem->content || !mb_strlen($menuItem->content)) {
return '';
}
@@ -0,0 +1,38 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
/**
*
*/
class LRT extends Auth_Controller
{
/**
* Object initialization
*/
public function __construct()
{
parent::__construct(
array(
'getRunningLRTs' => self::PERM_LOGGED,
)
);
// Loads libraries
$this->load->library('LongRunTaskLib');
}
/**
*
*/
public function getRunningLRTs()
{
$runningLrtsResult = $this->longruntasklib->getRunningLRTsUser(getAuthUID());
if (isError($runningLrtsResult)) $this->terminateWithJsonError($runningLrtsResult);
$this->outputJson($runningLrtsResult);
}
}
@@ -0,0 +1,112 @@
<?php
if (!defined("BASEPATH")) exit("No direct script access allowed");
/**
* - This controller acts as interface of the LongRunTaskLib that contains
* all the needed functionalities to operate with the Long Run Task system
* that is built on top of the Jobs Queue System
* - This is a Job Queue Worker that gets scheduled LRTs from the queue and executes them
* - Once all the LRTs have been started checks if there are LRTs that are running for too long and kills them
*/
class LongRunTaskExecJob extends JOB_Controller
{
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
// Changes the needed configs for LogLib
$this->LogLibJob->setConfigs(
array(
'dbExecuteUser' => get_class($this),
'requestId' => 'LRT'
)
);
// Loads LongRunTaskLib library
$this->load->library('LongRunTaskLib');
}
/**
* Executes all the new LRTs
*/
public function execEmAll()
{
$this->logInfo('Execute long run tasks started');
// Get all the LRTs that is possible to execute now
$lrtsResult = $this->longruntasklib->getNewLRTs();
// If an error occurred then return it
if (isError($lrtsResult)) return $lrtsResult;
if (hasData($lrtsResult))
{
// For each LRT
foreach (getData($lrtsResult) as $lrt)
{
// Execute the task
$executeLrtResult = $this->longruntasklib->executeLrt($lrt);
if (isError($executeLrtResult)) $this->logError($executeLrtResult);
}
}
$this->logInfo('Execute long run tasks ended');
}
/**
* Kills all the hanging LRTs
*/
public function killHangingLRTs()
{
$this->logInfo('Kill hanging LRTs started');
// Get all the LRTs that is possible to execute now
$lrtsResult = $this->longruntasklib->getHangingLRTs();
// If an error occurred then return it
if (isError($lrtsResult)) return $lrtsResult;
if (hasData($lrtsResult))
{
// For each LRT
foreach (getData($lrtsResult) as $lrt)
{
// Kill the task
$killLrtResult = $this->longruntasklib->killLrt($lrt);
if (isError($killLrtResult)) $this->logError($killLrtResult);
}
}
$this->logInfo('Kill hanging LRTs ended');
}
/**
*
*/
public function checkExecution()
{
$this->logInfo('Check execution long run tasks started');
// Get the related LRT data from the queue
$lrtsResult = $this->longruntasklib->getRunningLRTs();
// If an error occurred then return it
if (isError($lrtsResult)) return $lrtsResult;
// If there are running LRTs
if (hasData($lrtsResult))
{
// For each LRT
foreach (getData($lrtsResult) as $lrt)
{
// Check the LRT execution
$checkExecutionResult = $this->longruntasklib->checkExecution($lrt);
if (isError($checkExecutionResult)) $this->logError($checkExecutionResult);
}
}
$this->logInfo('Check execution long run tasks ended');
}
}
+82
View File
@@ -0,0 +1,82 @@
<?php
if (!defined("BASEPATH")) exit("No direct script access allowed");
/**
* Testing LRT to check if the LRT system is working properly
* This will be called by the LongRunTaskExecJob
*/
class LRTDummy extends LRT_Controller
{
/**
*
*/
public function run($jobid)
{
$this->logInfo('Long run tasks '.get_class($this).' started');
$this->_doIt($jobid);
$this->logInfo('Long run tasks '.get_class($this).' ended');
}
/**
* Loops on the number of seconds provided by the LRT input
* Sleeps every time 1 sec
* Writes the progress
* Writes the output
*/
private function _doIt($jobid)
{
// Get the LRT record related to the provided jobid
$lrtResult = $this->getLrt($jobid);
// If an error occurred or the record has not been found
if (isError($lrtResult))
{
$this->logError($lrtResult);
return;
}
if (!hasData($lrtResult))
{
$this->logError('LRT not found in database');
return;
}
// Get the record
$lrt = getData($lrtResult)[0];
// Get and check the input
$input = json_decode($lrt->{LongRunTaskLib::PROPERTY_INPUT});
if ($input == null)
{
$this->logError('LRT input is not a valid json');
return;
}
// Operation
for ($i = 0; $i < (int)$input->sleep; $i++)
{
sleep(1);
// Set the progress
$setProgressResult = $this->setProgress($jobid, (($i + 1) / (int)$input->sleep) * 100);
if (isError($setProgressResult))
{
$this->logError($setProgressResult);
return;
}
}
$sleepMsg = 'The user '.$lrt->{LongRunTaskLib::PROPERTY_UID}.' slept for '.$input->sleep.' seconds';
$this->logInfo($sleepMsg);
// Set the output
$setOutputResult = $this->setOutput($jobid, $sleepMsg);
if (isError($setOutputResult))
{
$this->logError($setOutputResult);
}
}
}
@@ -0,0 +1,56 @@
<?php
if (!defined('BASEPATH')) exit('No direct script access allowed');
use \stdClass as stdClass;
/**
*
*/
class LRTTest extends Auth_Controller
{
/**
*
*/
public function __construct()
{
parent::__construct(
array(
'index' => 'system/developer:r',
'lrt1min' => 'system/developer:r',
)
);
// Loads LongRunTaskLib library
$this->load->library('LongRunTaskLib');
}
// -----------------------------------------------------------------------------------------------------------------
// Public methods
/**
* Everything has a beginning
*/
public function index()
{
$this->load->view('system/lrtTest.php');
}
/**
*
*/
public function lrt1min()
{
$lrtInput = new stdClass();
$lrtInput->sleep = 1; // Sleep for 1 min
$this->outputJsonSuccess(
$this->longruntasklib->addNewLrtToQueue(
'LRTDummy', // LRT type
getAuthUID(), // UID executer
$lrtInput // LRT input
)
);
}
}
+69
View File
@@ -0,0 +1,69 @@
<?php
if (!defined("BASEPATH")) exit("No direct script access allowed");
/**
* Long Run Task
*
* - This controller acts as interface of the LongRunTaskLib that contains
* all the needed functionalities to operate with the Long Run Task system
* that is built on top of the Jobs Queue System
* - This is an abstract class that provide basic functionalities,
* it has to be extended to broaden its logic
* - Any implementation of a Long Run Task should extends this class to
* properly operate with the LRT system
*/
abstract class LRT_Controller extends JOB_Controller
{
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
// Changes the needed configs for LogLib
$this->LogLibJob->setConfigs(
array(
'dbExecuteUser' => get_class($this),
'requestId' => 'LRT'
)
);
// Loads LongRunTaskLib library
$this->load->library('LongRunTaskLib');
}
//------------------------------------------------------------------------------------------------------------------
// Public methods
abstract public function run($jobid);
//------------------------------------------------------------------------------------------------------------------
// Protected methods
/**
*
*/
protected function getLrt($jobid)
{
return $this->longruntasklib->getLrt($jobid);
}
/**
*
*/
protected function setProgress($jobid, $progress)
{
return $this->longruntasklib->setProgress($jobid, $progress);
}
/**
*
*/
protected function setOutput($jobid, $output)
{
return $this->longruntasklib->setOutuput($jobid, $output);
}
}
+81 -48
View File
@@ -2,6 +2,8 @@
if (!defined('BASEPATH')) exit('No direct script access allowed');
use \stdClass as stdClass;
/**
* Library that contains all the needed functionalities to operate with the Jobs Queue System
*/
@@ -24,12 +26,12 @@ class JobsQueueLib
const PROPERTY_END_TIME = 'endtime';
const PROPERTY_ERROR = 'error';
private $_ci; // CI instance
protected $_ci; // CI instance
/**
* Constructor
*/
public function __construct($authenticate = true)
public function __construct()
{
// Gets CI instance
$this->_ci =& get_instance();
@@ -62,14 +64,29 @@ class JobsQueueLib
*/
public function getOldestJobs($type, $maxAmount = null)
{
$this->_ci->JobsQueueModel->resetQuery();
$this->_ci->JobsQueueModel->addOrder('creationtime', 'ASC');
$types = $type; // default is array
$limit = 0; // default query limit
// If the maximum amount of jobs to retrieve have been specified
if (is_numeric($maxAmount)) $this->_ci->JobsQueueModel->addLimit($maxAmount);
if (is_numeric($maxAmount)) $limit = $maxAmount;
return $this->_ci->JobsQueueModel->loadWhere(array('status' => self::STATUS_NEW, 'type' => $type));
// If it is a string then include it into an array
if (!isEmptyString($type) && is_string($type)) $types = array($type);
// Return the result of the query
return $this->_ci->JobsQueueModel->execReadOnlyQuery('
SELECT jq.*
FROM system.tbl_jobsqueue jq
WHERE jq.type IN ?
AND jq.status = ?
ORDER BY jq.creationtime DESC
LIMIT ?',
array(
$types,
self::STATUS_NEW,
$limit
)
);
}
/**
@@ -77,11 +94,23 @@ class JobsQueueLib
*/
public function getJobsByTypeStatus($type, $status)
{
$this->_ci->JobsQueueModel->resetQuery();
$types = $type; // default is array
$this->_ci->JobsQueueModel->addOrder('creationtime', 'DESC');
// If it is a string then include it into an array
if (!isEmptyString($type) && is_string($type)) $types = array($type);
return $this->_ci->JobsQueueModel->loadWhere(array('status' => $status, 'type' => $type));
// Return the result of the query
return $this->_ci->JobsQueueModel->execReadOnlyQuery('
SELECT jq.*
FROM system.tbl_jobsqueue jq
WHERE jq.type IN ?
AND jq.status = ?
ORDER BY jq.creationtime DESC',
array(
$types,
$status
)
);
}
/**
@@ -253,8 +282,30 @@ class JobsQueueLib
return array($job);
}
//------------------------------------------------------------------------------------------------------------------
// Protected methods
/**
* Checks if the given job contains a valid type
*/
protected function _checkJobType($type, $types)
{
return $this->_inArray($type, $types, self::PROPERTY_TYPE);
}
//------------------------------------------------------------------------------------------------------------------
// Private methods
/**
* Drop not allowed properties from the given job
*/
private function _dropNotAllowedPropertiesNewJob(&$job)
{
unset($job->{self::PROPERTY_JOBID});
unset($job->{self::PROPERTY_CREATIONTIME});
unset($job->{self::PROPERTY_TYPE});
}
/**
* Checks the job object structure when needed for insert
@@ -281,34 +332,6 @@ class JobsQueueLib
return false; // better sorry than wrong
}
/**
* Checks the job object structure when needed for update
*/
private function _checkUpdateJobStructure(&$job)
{
// If job is a valid object
if (is_object($job) && property_exists($job, self::PROPERTY_JOBID)) return true; // it is valid!
// If not object then object it!
if (!is_object($job)) $job = new stdClass();
// If an error property was not already previously stored then store an error message in job object
if (!property_exists($job, self::PROPERTY_ERROR))
{
$job->{self::PROPERTY_ERROR} = 'The structure of the provided job is not valid';
}
return false; // better sorry than wrong
}
/**
* Checks if the given job contains a valid type
*/
private function _checkJobType($type, $types)
{
return $this->_inArray($type, $types, self::PROPERTY_TYPE);
}
/**
* Checks if the given job contains a valid status
*/
@@ -333,6 +356,26 @@ class JobsQueueLib
return $found;
}
/**
* Checks the job object structure when needed for update
*/
private function _checkUpdateJobStructure(&$job)
{
// If job is a valid object
if (is_object($job) && property_exists($job, self::PROPERTY_JOBID)) return true; // it is valid!
// If not object then object it!
if (!is_object($job)) $job = new stdClass();
// If an error property was not already previously stored then store an error message in job object
if (!property_exists($job, self::PROPERTY_ERROR))
{
$job->{self::PROPERTY_ERROR} = 'The structure of the provided job is not valid';
}
return false; // better sorry than wrong
}
/**
* Search in an array the given value
* The elements of the given array are objects
@@ -354,16 +397,6 @@ class JobsQueueLib
return $found;
}
/**
* Drop not allowed properties from the given job
*/
private function _dropNotAllowedPropertiesNewJob(&$job)
{
unset($job->{self::PROPERTY_JOBID});
unset($job->{self::PROPERTY_CREATIONTIME});
unset($job->{self::PROPERTY_TYPE});
}
/**
* Drop not allowed properties from the given job
*/
+354
View File
@@ -0,0 +1,354 @@
<?php
if (!defined('BASEPATH')) exit('No direct script access allowed');
require_once 'JobsQueueLib.php';
/**
* Library that contains all the needed functionalities to operate with the Long Run Tasks
*/
class LongRunTaskLib extends JobsQueueLib
{
// Config names
const CFG_LRT_MAX_NUMBER_SYSTEM = 'lrt_max_number_system';
const CFG_LRT_TYPES = 'lrt_types';
const CFG_LRT_MAX_RUN = 'lrt_max_run_timeout';
const CFG_LRT_MAX_NUMBER_USER = 'lrt_max_number_single_user';
// LRT object properties
const PROPERTY_UID = 'uid';
const PROPERTY_PID = 'pid';
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
// Loads the Long Run Tasks configs
$this->_ci->config->load('lrt');
}
//------------------------------------------------------------------------------------------------------------------
// Public methods used by the LongRunTaskExecJob
/**
* Get the oldest added LRTs to the queue having status = new
* The maximum number of returned queued LRTs is limited by:
* number of currently running LRTs - maximum allowed number of LRTs for the system
*/
public function getNewLRTs()
{
// Get all the running LRTs
$runningLrtsResult = $this->getJobsByTypeStatus($this->_ci->config->item(self::CFG_LRT_TYPES), JobsQueueLib::STATUS_RUNNING);
if (isError($runningLrtsResult)) return $runningLrtsResult;
// The number of the currently running LRTs - the maximum LRTs for the system
$max_number_of_lrts = $this->_ci->config->item(self::CFG_LRT_MAX_NUMBER_SYSTEM) - count(getData($runningLrtsResult));
// Get the oldest LRTs added to the queue
return $this->getOldestJobs($this->_ci->config->item(self::CFG_LRT_TYPES), $max_number_of_lrts);
}
/**
* Get all the LRT that are running more then CFG_LRT_MAX_RUN hours
*/
public function getHangingLRTs()
{
// Return the result of the query
return $this->_ci->JobsQueueModel->execReadOnlyQuery('
SELECT jq.*
FROM system.tbl_jobsqueue jq
WHERE jq.type IN ?
AND jq.status = ?
AND jq.starttime < NOW() - INTERVAL \''.$this->_ci->config->item(self::CFG_LRT_MAX_RUN).' hours\'
ORDER BY jq.creationtime DESC',
array(
$this->_ci->config->item(self::CFG_LRT_TYPES),
JobsQueueLib::STATUS_RUNNING
)
);
}
/**
* Get all the LRT that are currently running
*/
public function getRunningLRTs()
{
// Return the result of the query
return $this->_ci->JobsQueueModel->execReadOnlyQuery('
SELECT jq.*
FROM system.tbl_jobsqueue jq
WHERE jq.type IN ?
AND jq.status = ?
ORDER BY jq.creationtime DESC',
array(
$this->_ci->config->item(self::CFG_LRT_TYPES),
JobsQueueLib::STATUS_RUNNING
)
);
}
/**
* Execute a LRT in background
* - Checks if the wanted LRT exists in the applcation/controllers/lrts directory
* - Then executes it in background via CI CLI
* - Stores the LRT pid into the database
*/
public function executeLrt($lrt)
{
$output = array();
// If does _not_ exist a LRT implementation for this LRT type, then return an error
if (!file_exists(APPPATH.'controllers/lrts/'.$lrt->{self::PROPERTY_TYPE}.'.php'))
{
return error('The required LRT implementation has not been found');
}
// Execute the LRT implementation (a CI controller from CLI) providing as parameter the jobid
exec(
// Command
sprintf(
'/usr/bin/php %s/../index.ci.php lrts/%s/run %s > /dev/null 2>&1 & echo $!',
APPPATH,
$lrt->{self::PROPERTY_TYPE},
$lrt->{self::PROPERTY_JOBID}
),
$output // Here goes the output from the standard output and error
);
// If a pid has not been returned
if (isEmptyArray($output) || !is_numeric($output[0])) return error('Not a valid pid has been returned');
// Set the pid, status and starttime of this LRT into the database
$updateLrtResult = $this->_ci->JobsQueueModel->update(
$lrt->{self::PROPERTY_JOBID},
array(
'pid' => $output[0],
'starttime' => 'NOW()',
'status' => self::STATUS_RUNNING
)
);
if (isError($updateLrtResult)) return $updateLrtResult;
}
/**
* Kill the provided LRT
* To avoid to kill a process that is not this LRT,
* since the same PID can be assigned to another process once this ended
*/
public function killLrt($lrt)
{
// Try to get the pid of this LRT from the system
$pid = exec(
sprintf(
'ps -eo pid,cmd | grep "index.ci.php lrts/%s/run %s" | grep -v grep | awk \'{print $1}\'',
$lrt->{self::PROPERTY_TYPE},
$lrt->{self::PROPERTY_JOBID}
)
);
// If the pid is the same then kill the process with a SIGKILL
if ($pid == $lrt->{self::PROPERTY_PID}) exec('kill -9 '.$lrt->{self::PROPERTY_PID}.' > /dev/null 2>&1');
// Set the LRT as failed in any case
$lrtExecFailedResult = $this->_ci->JobsQueueModel->update(
$lrt->{self::PROPERTY_JOBID},
array(
'endtime' => 'NOW()',
'status' => self::STATUS_FAILED
)
);
// If an error occurred then return it
if (isError($lrtExecFailedResult)) return $lrtExecFailedResult;
}
/**
*
*/
public function checkExecution($lrt)
{
// If the LRT stopped running
if (!$this->_isRunning($lrt))
{
// Loads MessageLib library
$this->_ci->load->library('MessageLib');
// Load the BenutzerModel
$this->_ci->load->model('person/Benutzer_model', 'BenutzerModel');
// Get the benutzer for this uid
$benutzerResult = $this->_ci->BenutzerModel->loadWhere(array('uid' => $lrt->{LongRunTaskLib::PROPERTY_UID}));
// If an error occurred then return it
if (isError($benutzerResult)) return $benutzerResult;
// If no benutzer has been found
if (!hasData($benutzerResult)) return error('No benutzer found, uid: '.$lrt->{LongRunTaskLib::PROPERTY_UID});
$benutzer = getData($benutzerResult)[0];
// Sends a message to the user
$messageResult = $this->_ci->messagelib->sendMessageUser(
$benutzer->person_id,
'Long run task ended',
'The long run task '.$lrt->{self::PROPERTY_TYPE}.' ended, output: '.$lrt->{self::PROPERTY_OUTPUT}
);
// If an error occurred then return it
if (isError($messageResult)) return $messageResult;
// Set the LRT as done
$lrtExecOverResult = $this->_ci->JobsQueueModel->update(
$lrt->{self::PROPERTY_JOBID},
array(
'endtime' => 'NOW()',
'status' => self::STATUS_DONE
)
);
// If an error occurred then return it
if (isError($lrtExecOverResult)) return $lrtExecOverResult;
}
}
//------------------------------------------------------------------------------------------------------------------
// Public methods used by the front end applications (standard controllers/end points, ex. controllers/system/LRTTest.php)
/**
* Add a single LRT to the queue
*/
public function addNewLrtToQueue($type, $uid, $lrtInput)
{
// Checks parameters
if (isEmptyString($type)) return error('The provided type parameter is not a valid string');
if (isEmptyString($uid)) return error('The provided uid parameter is not a valid string');
// Get all the running task for this uid
// Return the result of the query
$runningLRTbyUIDResult = $this->_ci->JobsQueueModel->execReadOnlyQuery('
SELECT jq.*
FROM system.tbl_jobsqueue jq
WHERE jq.status IN ?
AND jq.uid = ?
',
array(
array(
self::STATUS_NEW,
self::STATUS_RUNNING
),
$uid
)
);
// If an error occurred then return it
if (isError($runningLRTbyUIDResult)) return $runningLRTbyUIDResult;
// If the running tasks for this user are too many
if (count(getData($runningLRTbyUIDResult)) >= $this->_ci->config->item(self::CFG_LRT_MAX_NUMBER_USER))
{
return error('Too many running tasks for this user');
}
// Convert input to JSON and check it
$jsonLrtInput = json_encode($lrtInput);
if ($jsonLrtInput == null) return error('The provided LRT input is not valid');
// Get all the job types
$dbResult = $this->_ci->JobTypesModel->load();
if (isError($dbResult)) return $dbResult;
$types = getData($dbResult);
// If the given type is not present in database
if (!$this->_checkJobType($type, $types)) return error('The provided type parameter is not valid');
// Get all the job statuses
$dbResult = $this->_ci->JobStatusesModel->load();
if (isError($dbResult)) return $dbResult;
$statuses = getData($dbResult);
// Create an object that represent the new tbl_jobsqueue record with the provided input
$lrt = $this->generateJobs(self::STATUS_NEW, $jsonLrtInput)[0];
// What you asked is what you get!
$lrt->{self::PROPERTY_TYPE} = $type;
$lrt->{self::PROPERTY_UID} = $uid;
// Try to insert the single lrt into database
$dbNewLrtResult = $this->_ci->JobsQueueModel->insert($lrt);
// If an error occurred during while inserting in database
if (isError($dbNewLrtResult)) return $dbNewLrtResult;
return success('LRT added to the queue');
}
/**
* Get all the LRT that are currently running for the given user
*/
public function getRunningLRTsUser($uid)
{
// Return the result of the query
return $this->_ci->JobsQueueModel->execReadOnlyQuery('
SELECT jq.*
FROM system.tbl_jobsqueue jq
WHERE jq.type IN ?
AND jq.status = ?
AND jq.uid = ?
ORDER BY jq.creationtime DESC',
array(
$this->_ci->config->item(self::CFG_LRT_TYPES),
JobsQueueLib::STATUS_RUNNING,
$uid
)
);
}
//------------------------------------------------------------------------------------------------------------------
// Public methods used by the LRT implementation (controllers/ltrs/*, ex. controllers/ltrs/LRTDummy)
/**
* Return a single record from the
*/
public function getLrt($jobid)
{
$this->_ci->JobsQueueModel->resetQuery();
return $this->_ci->JobsQueueModel->loadWhere(array('jobid' => $jobid));
}
/**
*
*/
public function setProgress($jobid, $progress)
{
return $this->_ci->JobsQueueModel->update($jobid, array('progress' => $progress));
}
/**
*
*/
public function setOutuput($jobid, $output)
{
return $this->_ci->JobsQueueModel->update($jobid, array('output' => json_encode($output)));
}
//------------------------------------------------------------------------------------------------------------------
// Private methods
/**
* Return true if the LRT is still running
*/
private function _isRunning($lrt)
{
// Try to get the pid of this LRT from the system
$pid = exec(
sprintf(
'ps -eo pid,cmd | grep "index.ci.php lrts/%s/run %s" | grep -v grep | awk \'{print $1}\'',
$lrt->{self::PROPERTY_TYPE},
$lrt->{self::PROPERTY_JOBID}
)
);
// If the pid is the same then the LRT is still running
return $pid == $lrt->{self::PROPERTY_PID};
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
$includesArray = array(
'title' => 'LRT Test Page',
'axios027' => true,
'bootstrap5' => true,
'fontawesome6' => true,
'vue3' => true,
'navigationcomponent' => true,
'phrases' => array(
'global',
'ui'
),
'customJSModules' => array('public/js/apps/LRTTest.js'),
);
$this->load->view('templates/FHC-Header', $includesArray);
?>
<div id="main">
<!-- Navigation component -->
<core-navigation-cmpt></core-navigation-cmpt>
<div id="content"></div>
</div>
<?php $this->load->view('templates/FHC-Footer', $includesArray); ?>
+1
View File
@@ -632,6 +632,7 @@ elseif (isset($prestudent_id))
$ps_obj->lastStatus = $ps_tmp->status_kurzbz; // letzten Status dem result array hinzufügen
$ps_obj->status_mehrsprachig = $ps_tmp->status_mehrsprachig;
$ps_obj->ausbildungssemester = $ps_tmp->ausbildungssemester;
}
// Falls Status 'Abgewiesene' vorhanden, nach hinten reihen
+33 -15
View File
@@ -29,6 +29,7 @@ require_once('../../include/sprache.class.php');
require_once '../../include/phrasen.class.php';
require_once '../../include/studiengang.class.php';
require_once('../../include/gebiet.class.php');
require_once('../../include/gebiet_pruefling.class.php');
if (!$db = new basis_db())
die('Fehler beim Oeffnen der Datenbankverbindung');
@@ -198,8 +199,8 @@ else if (isset($_SESSION['pruefling_id']))
)
SELECT DISTINCT ON
(gebiet_id, semester, reihung)
SELECT * FROM (SELECT DISTINCT ON
(gebiet_id)
semester,
gebiet_id,
STRING_AGG(studiengang_kz::TEXT, ', ' ORDER BY studiengang_kz) AS studiengang_kz_list,
@@ -226,13 +227,7 @@ else if (isset($_SESSION['pruefling_id']))
JOIN
testtool.tbl_gebiet USING (gebiet_id)
WHERE
(
(prestudent_data.semester= 1 AND tbl_ablauf.semester = 1)
OR
(prestudent_data.semester= 2 AND tbl_ablauf.semester = 2)
OR
(prestudent_data.semester= 3 AND tbl_ablauf.semester IN (1,3))
)
tbl_ablauf.semester <= prestudent_data.semester
AND (
prestudent_data.studienplan_id = tbl_ablauf.studienplan_id
OR
@@ -259,9 +254,7 @@ else if (isset($_SESSION['pruefling_id']))
JOIN
testtool.tbl_gebiet USING (gebiet_id)
WHERE
(prestudent_data.semester= 1 AND tbl_ablauf.semester = 1)
OR
(prestudent_data.semester= 3 AND tbl_ablauf.semester IN (1,3))
tbl_ablauf.semester <= prestudent_data.semester
)
) temp
) temp2
@@ -270,7 +263,7 @@ else if (isset($_SESSION['pruefling_id']))
semester,
gebiet_id,
bezeichnung,
". $bezeichnung_mehrsprachig_sel ."
". $bezeichnung_mehrsprachig_sel ." ) temp3
ORDER BY
semester,
@@ -283,6 +276,8 @@ else if (isset($_SESSION['pruefling_id']))
$lastsemester = '';
$quereinsteiger_stg = '';
$_SESSION['alleGebiete']= [];
$gebiet_pruefling = new gebiet_pruefling();
$quereinsteiger_menu = false;
while($row = $db->db_fetch_object($result))
{
//Jedes Semester in einer eigenen Tabelle anzeigen
@@ -294,8 +289,25 @@ else if (isset($_SESSION['pruefling_id']))
}
$lastsemester = $row->semester;
echo '<table border="0" cellspacing="0" cellpadding="0" id="Gebiet" style="display: visible; border-collapse: separate; border-spacing: 0 3px; margin-top: 5px;">';
echo '<tr><td class="HeaderTesttool">'. ($row->semester == '1' ? $p->t('testtool/basisgebiete') : $p->t('testtool/quereinstiegsgebiete')).'</td></tr>';
if ($row->semester == '1')
{
// Basisgebiete anzeigen
echo '<table border="0" cellspacing="0" cellpadding="0" id="Gebiet" style="display: visible; border-collapse: separate; border-spacing: 0 3px; margin-top: 5px;">';
echo '<tr><td class="HeaderTesttool">' . $p->t('testtool/basisgebiete') . '</td></tr>';
}
else
{
if (!$quereinsteiger_menu)
{
echo '<table border="0" cellspacing="0" cellpadding="0" id="Gebiet" style="display: visible; border-collapse: separate; border-spacing: 0 3px; margin-top: 5px;">';
echo '<tr><td class="HeaderTesttool">' . $p->t('testtool/quereinstiegsgebiete') . '</td></tr>';
$quereinsteiger_menu = true;
}
else
{
echo '<table border="0" cellspacing="0" cellpadding="0" id="Gebiet" style="display: visible; border-collapse: separate; border-spacing: 0 3px; margin-top: 5px;">';
}
}
}
// Bei Quereinstiegsgebieten nach STG clustern und die STG anzeigen
@@ -394,6 +406,12 @@ else if (isset($_SESSION['pruefling_id']))
</td>
<!--<td width="10" class="ItemTesttoolRight" nowrap>&nbsp;</td>-->
</tr>';
if (!$gebiet_pruefling->checkIfExists($_SESSION['pruefling_id'], $row->gebiet_id))
{
$gebiet_pruefling->pruefling_id = $_SESSION['pruefling_id'];
$gebiet_pruefling->gebiet_id = $row->gebiet_id;
$gebiet_pruefling->saveGebietForPruefling();
}
$_SESSION['alleGebiete'][] = $row->gebiet_id;
}
else
+97
View File
@@ -0,0 +1,97 @@
<?php
/* Copyright (C) 2024 Technikum-Wien
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
require_once(dirname(__FILE__).'/basis_db.class.php');
class gebiet_pruefling extends basis_db
{
//Tabellenspalten
public $prueflinggebiet_id;
public $pruefling_id;
public $gebiet_id;
public $insertamum;
public function __construct()
{
parent::__construct();
}
public function loadByPruefling($pruefling_id)
{
$qry = "SELECT *
FROM testtool.tbl_pruefling_gebiet
WHERE pruefling_id = ".$this->db_add_param($pruefling_id, FHC_INTEGER);
if($this->db_query($qry))
{
if($row = $this->db_fetch_object())
{
$this->prueflinggebiet_id = $row->prueflinggebiet_id;
$this->pruefling_id = $row->pruefling_id;
$this->gebiet_id = $row->gebiet_id;
return true;
}
else
{
$this->errormsg = "Pruefling nicht gefunden";
return false;
}
}
else
{
$this->errormsg = "Fehler beim Laden der Gebiete für den Pruefling";
return false;
}
}
public function checkIfExists($pruefling_id, $gebiet_id)
{
$qry = "SELECT 1
FROM testtool.tbl_pruefling_gebiet
WHERE pruefling_id =".$this->db_add_param($pruefling_id, FHC_INTEGER) ." AND gebiet_id =".$this->db_add_param($gebiet_id, FHC_INTEGER);
if ($result = $this->db_query($qry))
{
if ($this->db_num_rows($result) > 0)
return true;
else
return false;
}
}
public function saveGebietForPruefling()
{
$qry = "INSERT INTO testtool.tbl_pruefling_gebiet
(pruefling_id, gebiet_id)
VALUES(".
$this->db_add_param($this->pruefling_id, FHC_INTEGER).','.
$this->db_add_param($this->gebiet_id, FHC_INTEGER).');';
if ($this->db_query($qry))
{
return true;
}
else
{
$this->errormsg = 'Fehler beim Speichern der Daten';
return true;
}
}
}
?>
+9
View File
@@ -0,0 +1,9 @@
export default {
addNew1MinLrt()
{
return {
method: 'post',
url: '/system/LRTTest/lrt1min'
};
},
}
+25
View File
@@ -0,0 +1,25 @@
/**
* Copyright (C) 2025 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
export default {
load() {
return {
method: 'get',
url: '/components/LRT/getRunningLRTs'
};
}
};
+14 -1
View File
@@ -19,6 +19,7 @@ import DeadlineOverview from "../../components/Cis/Abgabetool/DeadlineOverview.j
import Studium from "../../components/Cis/Studium/Studium.js";
import StgOrgLvPlan from "../../components/Cis/LvPlan/StgOrg.js";
import OtherLvPlan from "../../components/Cis/LvPlan/OtherLvPlan.js";
import Compat from "../../components/Cis/Compat.js";
import ApiRouteInfo from '../../api/factory/routeinfo.js';
import {capitalize} from "../../helpers/StringHelpers.js";
@@ -29,6 +30,18 @@ const isMobile = window.matchMedia("(max-width: 767px)").matches;
const router = VueRouter.createRouter({
history: VueRouter.createWebHistory(`/${ciPath}`),
routes: [
{
path: `/Cis/Compat/:mode(ci|legacy)/:path(.*)`,
name: 'Compat',
component: Compat,
props: (route) => {
return {
mode: route.params.mode,
path: route.params.path,
query_string: VueRouter.stringifyQuery(route.query)
};
}
},
{
path: `/Cis/Studium`,
name: 'Studium',
@@ -277,7 +290,7 @@ const app = Vue.createApp({
if (target && this.isInternalRoute(target.href)) {
const url = new URL(target.href)
const path = url.pathname
const path = url.pathname + url.search;
const base = this.$router.options.history.base
const route = path.replace(base, '') || '/'
+48
View File
@@ -0,0 +1,48 @@
/**
* Copyright (C) 2022 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import PluginsPhrasen from '../plugins/Phrasen.js';
import {CoreNavigationCmpt} from '../components/navigation/Navigation.js'
import ApiLrt from "../api/LRTTEst.js";
const lrtTestApp = Vue.createApp({
data: function() {
return {
};
},
components: {
CoreNavigationCmpt,
},
methods: {
addNew1MinLrt() {
this.$api.call(ApiLrt.addNew1MinLrt())
.then(result => {
this.dropdowns.studiensemester_array = result.data;
})
.catch(this.$fhcAlert.handleSystemError);
}
},
template: `
<button type="button" class="btn btn-primary" @click="addNew1MinLrt()">Add a 1 min LRT</button>
`
});
FhcApps.makeExtendable(lrtTestApp);
lrtTestApp.use(PluginsPhrasen).mount('#main');
+93
View File
@@ -0,0 +1,93 @@
export default {
name: "Compat",
props: {
mode: { type: String, required: true },
path: { type: String, required: true },
query_string: { type: String, required: true, default: ''}
},
data: function() {
return {
lastLoadediFrameURL: '',
srcUrl: ''
};
},
computed: {
propsWatchHelper: function() {
return this.mode + '#' + this.path + '#' + this.query_string;
}
},
mounted: function() {
this.srcUrl = this.buildSrcUrl();
},
watch: {
propsWatchHelper: function() {
if(this.lastLoadediFrameURL === '') {
return;
}
let currentiFrameURL = this.$refs.compatiframe ? this.$refs.compatiframe.src : '';
let url = this.buildSrcUrl();
if(this.lastLoadediFrameURL !== url) {
this.srcUrl = url;
if(this.srcUrl === url) {
this.$refs.compatiframe.contentWindow.location.href = url;
}
}
}
},
methods: {
buildSrcUrl: function() {
let url = false;
switch(this.mode) {
case 'ci':
url = FHC_JS_DATA_STORAGE_OBJECT.app_root + 'index.ci.php/' + this.path;
break;
case 'legacy':
url = FHC_JS_DATA_STORAGE_OBJECT.app_root + this.path;
break;
default:
url = false;
}
if(this.query_string !== '' && url) {
url += '?' + this.query_string;
}
return url;
},
loadHandler: function() {
let iframe_href = this.$refs.compatiframe.contentWindow.location.href;
let ci_urlstart = FHC_JS_DATA_STORAGE_OBJECT.app_root + 'index.ci.php/';
let legacy_urlstart = FHC_JS_DATA_STORAGE_OBJECT.app_root;
let routerpath = null;
this.lastLoadediFrameURL = iframe_href;
if(iframe_href.startsWith(ci_urlstart)) {
routerpath = iframe_href.replace(
ci_urlstart, '/Cis/Compat/ci/');
} else if(iframe_href.startsWith(legacy_urlstart)) {
routerpath = iframe_href.replace(
legacy_urlstart, '/Cis/Compat/legacy/');
} else {
return;
}
if(this.$route.fullPath !== routerpath) {
this.$router.push(routerpath);
}
}
},
template: `
<div class="w-100">
<iframe
ref="compatiframe"
v-if="srcUrl"
:src="srcUrl"
@load="loadHandler"
style="width:100%; height:90vh; border:0; display:block;"
></iframe>
<div v-else class="alert alert-warning">Keine URL gefunden.</div>
</div>
`
};
@@ -2,15 +2,16 @@ export default {
props: {
data: {
type: String,
}
},
},
data() {
return {}
return {};
},
template: /*html*/`
template: /*html*/ `
<div class="card">
<div class="card-body">
<span >{{$p.t('profil','fhAusweisStatus',[data])}}</span>
<span v-if="data">{{$p.t('profil','fhAusweisStatus',[data])}}</span>
<span v-else>{{$p.t('profil','fhAusweisStatusNichtAusgegeben')}}</span>
</div>
</div>`,
}
};
@@ -542,10 +542,10 @@ export default {
<template v-if="typeHeader==='mitarbeiter'">
<div class="foto-container col-md-2 d-flex justify-content-start align-items-center w-30 pb-3 gap-3 mt-3 position-relative" style="max-height: 8rem; max-width: 6rem; overflow: hidden;">
<div class="foto-container d-flex flex-column align-items-center h-100 position-relative d-inline-block">
<img
class="d-block w-100 h-100 rounded"
style="height: 84px; object-fit: contain;"
class="d-block rounded"
style="height: 96px;"
alt="Profilbild"
:src="getFotoSrc(headerDataMa.foto)"
/>
@@ -558,15 +558,13 @@ export default {
<template v-if="fotoEditable">
<button
type="button"
class="fotoedit btn btn-outline-dark btn-sm d-flex justify-content-center align-items-center position-absolute start-0"
style="z-index: 104; font-size: 1rem; width: 2.5rem; height: 2.5rem; opacity:0; transition: opacity 0.2s; top:13%;"
class="fotoedit buttonleft btn btn-outline-dark btn-sm d-flex justify-content-center align-items-center position-absolute start-0"
@click="showDeleteModal(person_id)">
<i class="fa fa-xmark"></i>
</button>
<button
type="button"
class="fotoedit btn btn-outline-dark btn-sm d-flex justify-content-center align-items-center position-absolute end-0"
style="z-index: 104; font-size: 1rem; width: 2.5rem; height: 2.5rem; opacity:0; transition: opacity 0.2s; top:13%;"
class="fotoedit buttonright btn btn-outline-dark btn-sm d-flex justify-content-center align-items-center position-absolute end-0"
@click="showModal(person_id)">
<i class="fa fa-pen"></i>
</button>
+69
View File
@@ -0,0 +1,69 @@
/**
* Copyright (C) 2026 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import ApiLRT from '../api/lrt.js';
export default {
name: 'LRTComponent',
props: {
appIdentifier: {
type: String,
required: true
},
navigationPage: {
type: String,
default: 'apps'
}
},
data() {
return {
items: []
};
},
watch: {
navigationPage() {
this.getItems();
}
},
methods: {
getItems() {
this.$api
.call(ApiNavigation.getMenu(this.navigationPage))
.then(result => {
this.items = result.data;
})
.catch(this.$fhcAlert.handleSystemError);
}
},
created() {
this.getItems();
},
template: /* html */`
<ul class="fhc-app-menu">
<li v-for="(menu, key) in items" :key="key">
<a
:href="menu.link"
@click="menu.onClickCall"
:class="{ active: key === appIdentifier }"
>
<i v-if="menu.icon" class="fa fa-fw" :class="'fa-'+ menu.icon" />
{{ menu.description }}
</a>
</li>
<slot />
</ul>`
};
+6 -2
View File
@@ -94,6 +94,7 @@ require_once('dbupdate_3.4/71645_studvw_messagetab_ladezeit.php');
require_once('dbupdate_3.4/75469_le_optional_4_projektarbeit.php');
require_once('dbupdate_3.4/71566_studienordnungsdokument_neuer_organisationseinheitstyp_programm.php');
require_once('dbupdate_3.4/70376_lohnguide.php');
require_once('dbupdate_3.4/76203_Asynchrone_Tasks.php');
require_once('dbupdate_3.4/75888_reihungstest_mehrfachdurchfuehrung.php');
require_once('dbupdate_3.4/76150_perm_other_lv_plan.php');
require_once('dbupdate_3.4/68957_dashboard_bookmark_neue_Spalte_sort.php');
@@ -102,6 +103,8 @@ require_once('dbupdate_3.4/75959_StudVw_Automatische_Tags.php');
require_once('dbupdate_3.4/76160_lvv_favorites.php');
require_once('dbupdate_3.4/77080_tabulator_presets_table.php');
require_once('dbupdate_3.4/78292_gehaltsanpassungtyp.php');
require_once('dbupdate_3.4/54225_reihungstest_ablauf_fuer_quereinsteiger.php');
// *** Pruefung und hinzufuegen der neuen Attribute und Tabellen
echo '<H2>Pruefe Tabellen und Attribute!</H2>';
@@ -253,7 +256,7 @@ $tabellen=array(
"hr.tbl_lohnguide_modellfunktion" => array("modellfunktion_kurzbz", "bezeichnung", "jobfamilie_kurzbz", "aktiv", "sort", "insertvon", "insertamum", "updatevon", "updateamum"),
"hr.tbl_lohnguide_modellstelle" => array("modellstelle_kurzbz", "bezeichnung", "grade", "modellfunktion_kurzbz", "aktiv", "sort", "insertvon", "insertamum", "updatevon", "updateamum"),
"hr.tbl_lohnguide_fachrichtung" => array("fachrichtung_kurzbz", "bezeichnung", "aktiv", "insertvon", "insertamum", "updatevon", "updateamum"),
"hr.tbl_vertragsbestandteil_lohnguide" => array("vertragsbestandteil_id", "stellenbezeichnung", "vordienstzeit", "fachrichtung_kurzbz", "modellstelle_kurzbz", "kommentar_person", "kommentar_modellstelle"),
"hr.tbl_vertragsbestandteil_lohnguide" => array("vertragsbestandteil_id", "stellenbezeichnung", "vordienstzeit", "fachrichtung_kurzbz", "modellstelle_kurzbz", "kommentar_person", "kommentar_modellstelle"),
"lehre.tbl_abschlussbeurteilung" => array("abschlussbeurteilung_kurzbz","bezeichnung","bezeichnung_english","sort"),
"lehre.tbl_abschlusspruefung" => array("abschlusspruefung_id","student_uid","vorsitz","pruefer1","pruefer2","pruefer3","abschlussbeurteilung_kurzbz","akadgrad_id","pruefungstyp_kurzbz","datum","uhrzeit","sponsion","anmerkung","updateamum","updatevon","insertamum","insertvon","ext_id","note","protokoll","endezeit","pruefungsantritt_kurzbz","freigabedatum"),
"lehre.tbl_abschlusspruefung_antritt" => array("pruefungsantritt_kurzbz","bezeichnung","bezeichnung_english","sort"),
@@ -421,6 +424,7 @@ $tabellen=array(
"testtool.tbl_pruefling_frage" => array("prueflingfrage_id","pruefling_id","frage_id","nummer","begintime","endtime"),
"testtool.tbl_frage_sprache" => array("frage_id","sprache","text","bild","audio","insertamum","insertvon","updateamum","updatevon"),
"testtool.tbl_vorschlag_sprache" => array("vorschlag_id","sprache","text","bild","audio","insertamum","insertvon","updateamum","updatevon"),
"testtool.tbl_pruefling_gebiet" => array("prueflinggebiet_id","pruefling_id","gebiet_id","insertamum"),
"system.tbl_app" => array("app"),
"system.tbl_appdaten" => array("appdaten_id","uid","app","appversion","version","bezeichnung","daten","freigabe","insertamum","insertvon","updateamum","updatevon"),
"system.tbl_cronjob" => array("cronjob_id","server_kurzbz","titel","beschreibung","file","last_execute","aktiv","running","jahr","monat","tag","wochentag","stunde","minute","standalone","reihenfolge","updateamum", "updatevon","insertamum","insertvon","variablen"),
@@ -438,7 +442,7 @@ $tabellen=array(
"system.tbl_log" => array("log_id","person_id","zeitpunkt","app","oe_kurzbz","logtype_kurzbz","logdata","insertvon","taetigkeit_kurzbz"),
"system.tbl_logtype" => array("logtype_kurzbz", "data_schema"),
"system.tbl_filters" => array("filter_id","app","dataset_name","filter_kurzbz","person_id","description","sort","default_filter","filter","oe_kurzbz","statistik_kurzbz"),
"system.tbl_jobsqueue" => array("jobid", "type", "creationtime", "status", "input", "output", "starttime", "endtime", "insertvon", "insertamum"),
"system.tbl_jobsqueue" => array("jobid", "type", "creationtime", "status", "input", "output", "starttime", "endtime", "insertvon", "insertamum", "pid", "uid", "progress"),
"system.tbl_jobstatuses" => array("status"),
"system.tbl_jobtriggers" => array("type", "status", "following_type"),
"system.tbl_jobtypes" => array("type", "description"),
@@ -0,0 +1,41 @@
<?php
if (! defined('DB_NAME')) exit('No direct script access allowed');
// Creates table public.tbl_notiz_typ if it doesn't exist and grants privileges
if (!$result = @$db->db_query('SELECT 1 FROM testtool.tbl_pruefling_gebiet LIMIT 1'))
{
$qry = 'CREATE TABLE testtool.tbl_pruefling_gebiet
(
prueflinggebiet_id integer NOT NULL,
pruefling_id integer NOT NULL,
gebiet_id integer NOT NULL,
insertamum timestamp DEFAULT now()
);
ALTER TABLE testtool.tbl_pruefling_gebiet OWNER TO fhcomplete;
ALTER TABLE testtool.tbl_pruefling_gebiet ADD CONSTRAINT pk_tbl_pruefling_gebiet PRIMARY KEY (prueflinggebiet_id);
ALTER TABLE testtool.tbl_pruefling_gebiet ADD CONSTRAINT fk_tbl_pruefling_gebiet_pruefling_id FOREIGN KEY (pruefling_id) REFERENCES testtool.tbl_pruefling(pruefling_id) ON UPDATE CASCADE ON DELETE RESTRICT;
ALTER TABLE testtool.tbl_pruefling_gebiet ADD CONSTRAINT fk_tbl_pruefling_gebiet_gebiet_id FOREIGN KEY (gebiet_id) REFERENCES testtool.tbl_gebiet(gebiet_id) ON UPDATE CASCADE ON DELETE RESTRICT;
CREATE SEQUENCE testtool.tbl_pruefling_gebiet_prueflinggebiet_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE testtool.tbl_pruefling_gebiet_prueflinggebiet_id_seq OWNER TO fhcomplete;
ALTER TABLE testtool.tbl_pruefling_gebiet ALTER COLUMN prueflinggebiet_id SET DEFAULT nextval(\'testtool.tbl_pruefling_gebiet_prueflinggebiet_id_seq\');
GRANT SELECT, UPDATE, INSERT, DELETE ON testtool.tbl_pruefling_gebiet TO web;
GRANT SELECT, UPDATE, INSERT, DELETE ON testtool.tbl_pruefling_gebiet TO vilesci;
GRANT SELECT, UPDATE ON testtool.tbl_pruefling_gebiet_prueflinggebiet_id_seq TO vilesci;
GRANT SELECT, UPDATE ON testtool.tbl_pruefling_gebiet_prueflinggebiet_id_seq TO web;
';
if (!$db->db_query($qry))
echo '<strong>public.tbl_pruefling_gebiet: '.$db->db_last_error().'</strong><br>';
else
echo '<br>public.tbl_pruefling_gebiet table created';
}
@@ -0,0 +1,59 @@
<?php
// Add column pid to system.tbl_jobsqueue
if (!$result = @$db->db_query('SELECT "pid" FROM "system"."tbl_jobsqueue" LIMIT 1'))
{
$qry = 'ALTER TABLE "system"."tbl_jobsqueue" ADD "pid" INT NULL DEFAULT NULL;';
if (!$db->db_query($qry))
echo '<strong>system.tbl_jobsqueue: '.$db->db_last_error().'</strong><br>';
else
echo '<br>Added column pid to table system.tbl_jobsqueue';
}
// Add column uid to system.tbl_jobsqueue
if (!$result = @$db->db_query('SELECT "uid" FROM "system"."tbl_jobsqueue" LIMIT 1'))
{
$qry = 'ALTER TABLE "system"."tbl_jobsqueue" ADD "uid" VARCHAR(32) NULL DEFAULT NULL;';
if (!$db->db_query($qry))
echo '<strong>system.tbl_jobsqueue: '.$db->db_last_error().'</strong><br>';
else
echo '<br>Added column uid to table system.tbl_jobsqueue';
}
// Add column progress to system.tbl_jobsqueue
if (!$result = @$db->db_query('SELECT "progress" FROM "system"."tbl_jobsqueue" LIMIT 1'))
{
$qry = 'ALTER TABLE "system"."tbl_jobsqueue" ADD "progress" NUMERIC(2,1) NULL DEFAULT 0;';
if (!$db->db_query($qry))
echo '<strong>system.tbl_jobsqueue: '.$db->db_last_error().'</strong><br>';
else
echo '<br>Added column progress to table system.tbl_jobsqueue';
}
// Add foreign key fk_jobsqueue_benutzer_uid on system.tbl_jobsqueue.uid with public.tbl_benutzer.uid
if ($result = $db->db_query("SELECT conname FROM pg_constraint WHERE conname = 'fk_jobsqueue_benutzer_uid'"))
{
if ($db->db_num_rows($result) == 0)
{
$qry = 'ALTER TABLE "system"."tbl_jobsqueue" ADD CONSTRAINT "fk_jobsqueue_benutzer_uid" FOREIGN KEY ("uid") REFERENCES "public"."tbl_benutzer" ("uid") ON DELETE RESTRICT ON UPDATE CASCADE;';
if (!$db->db_query($qry))
echo '<strong>system.tbl_jobsqueue: '.$db->db_last_error().'</strong><br>';
else
echo '<br>Created foreign key fk_jobsqueue_benutzer_uid';
}
}
// Add new webservice type in system.tbl_webservicetyp
if ($result = @$db->db_query("SELECT 1 FROM system.tbl_webservicetyp WHERE webservicetyp_kurzbz = 'lrt';"))
{
if ($db->db_num_rows($result) == 0)
{
$qry = "INSERT INTO system.tbl_webservicetyp(webservicetyp_kurzbz, beschreibung) VALUES('lrt', 'Long Run Task');";
if (!$db->db_query($qry))
echo '<strong>system.tbl_webservicetyp '.$db->db_last_error().'</strong><br>';
else
echo ' system.tbl_webservicetyp: Added webservice type "lrt"<br>';
}
}
+78
View File
@@ -1638,6 +1638,84 @@ $filters = array(
}',
'oe_kurzbz' => null,
),
array(
'app' => 'core',
'dataset_name' => 'logs',
'filter_kurzbz' => 'lrts24hours',
'description' => '{Last 24 hours LRTs logs}',
'sort' => 2,
'default_filter' => false,
'filter' => '
{
"name": "All Long Run Tasks logs from the last 24 hours",
"columns": [
{"name": "RequestId"},
{"name": "ExecutionTime"},
{"name": "ExecutedBy"},
{"name": "Description"},
{"name": "Data"}
],
"filters": [
{
"name": "WebserviceType",
"operation": "contains",
"condition": "job"
},
{
"name": "RequestId",
"operation": "contains",
"condition": "LRT"
},
{
"name": "ExecutionTime",
"operation": "lt",
"condition": "24",
"option": "hours"
}
]
}
',
'oe_kurzbz' => null,
),
array(
'app' => 'core',
'dataset_name' => 'logs',
'filter_kurzbz' => 'lrts48hours',
'description' => '{Last 48 hours LRTs logs}',
'sort' => 2,
'default_filter' => false,
'filter' => '
{
"name": "All Long Run Tasks logs from the last 48 hours",
"columns": [
{"name": "RequestId"},
{"name": "ExecutionTime"},
{"name": "ExecutedBy"},
{"name": "Description"},
{"name": "Data"}
],
"filters": [
{
"name": "WebserviceType",
"operation": "contains",
"condition": "job"
},
{
"name": "RequestId",
"operation": "contains",
"condition": "LRT"
},
{
"name": "ExecutionTime",
"operation": "lt",
"condition": "48",
"option": "hours"
}
]
}
',
'oe_kurzbz' => null,
),
);
// Loop through the filters array
+100
View File
@@ -19829,6 +19829,26 @@ array(
)
)
),
array(
'app' => 'international',
'category' => 'international',
'phrase' => 'massnahmeneinmalig',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Diese Maßnahme ist nur einmalig planbar.',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'This measure may only be planned once.',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'international',
'category' => 'international',
@@ -20609,6 +20629,26 @@ array(
)
)
),
array(
'app' => 'international',
'category' => 'international',
'phrase' => 'personenMassnahmen',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'alle Maßnahmen der Personen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => ' all measures of the people',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'international',
'category' => 'international',
@@ -20689,6 +20729,46 @@ array(
)
)
),
array(
'app' => 'international',
'category' => 'international',
'phrase' => 'einmalig',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Nur einmal planbar',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'One-time only',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'international',
'category' => 'international',
'phrase' => 'erroreinmalig',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Diese Maßnahme kann nur einmal geplant werden',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'This measure can only be planned once.',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'gruppenmanagement',
@@ -29363,6 +29443,26 @@ array(
)
)
),
array(
'app' => 'core',
'category' => 'profil',
'phrase' => 'fhAusweisStatusNichtAusgegeben',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Der FH Ausweis wurde noch nicht ausgegeben.',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'The FH ID card has not been issued yet.',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'profil',
+55 -2
View File
@@ -251,6 +251,31 @@ if ($deleteSingleResult)
exit();
}
$qry = "SELECT * FROM testtool.tbl_pruefling_gebiet
WHERE pruefling_id=" . $db->db_add_param($pruefling->pruefling_id) . " AND gebiet_id=" . $db->db_add_param($_POST['gebiet_id']) . "";
if ($db->db_query($qry))
{
while ($row = $db->db_fetch_object())
{
$undo .= " INSERT INTO testtool.tbl_pruefling_gebiet(prueflinggebiet_id,pruefling_id,gebiet_id, insertamum) VALUES (" .
$db->db_add_param($row->prueflinggebiet_id, FHC_INTEGER) . ', ' .
$db->db_add_param($row->pruefling_id, FHC_INTEGER) . ', ' .
$db->db_add_param($row->gebiet_id, FHC_INTEGER) . ', ' .
$db->db_add_param($row->insertamum) . ');';
}
}
else
{
$db->db_query('ROLLBACK');
echo json_encode(array(
'status' => 'fehler',
'msg' => 'Fehler beim Erstellen des UNDO Befehls fuer testtool.tbl_pruefling_gebiet'
));
exit();
}
//Antworten loeschen
$qry = "DELETE FROM testtool.tbl_pruefling_frage where pruefling_id=" . $db->db_add_param($pruefling->pruefling_id, FHC_INTEGER) . " AND
frage_id IN (SELECT frage_id FROM testtool.tbl_frage WHERE gebiet_id=" . $db->db_add_param($_POST['gebiet_id']) . ");
@@ -258,7 +283,9 @@ if ($deleteSingleResult)
DELETE FROM testtool.tbl_antwort
WHERE pruefling_id=" . $db->db_add_param($pruefling->pruefling_id) . " AND
vorschlag_id IN (SELECT vorschlag_id FROM testtool.tbl_vorschlag WHERE frage_id IN
(SELECT frage_id FROM testtool.tbl_frage WHERE gebiet_id=" . $db->db_add_param($_POST['gebiet_id']) . "));";
(SELECT frage_id FROM testtool.tbl_frage WHERE gebiet_id=" . $db->db_add_param($_POST['gebiet_id']) . "));
DELETE FROM testtool.tbl_pruefling_gebiet where pruefling_id=" . $db->db_add_param($pruefling->pruefling_id, FHC_INTEGER) . " AND gebiet_id=" . $db->db_add_param($_POST['gebiet_id']) . ";";
if ($result = $db->db_query($qry))
{
@@ -388,9 +415,35 @@ if ($deleteAllResults)
exit();
}
$qry = "SELECT * FROM testtool.tbl_pruefling_gebiet WHERE pruefling_id=" . $db->db_add_param($pruefling->pruefling_id, FHC_INTEGER) . ";
";
if ($db->db_query($qry))
{
while ($row = $db->db_fetch_object())
{
$undo .= " INSERT INTO testtool.tbl_pruefling_gebiet(prueflinggebiet_id,pruefling_id,gebiet_id,insertamum) VALUES (" .
$db->db_add_param($row->prueflinggebiet_id, FHC_INTEGER) . ', ' .
$db->db_add_param($row->pruefling_id, FHC_INTEGER) . ', ' .
$db->db_add_param($row->gebiet_id, FHC_INTEGER) . ', ' .
$db->db_add_param($row->insertamum) . ');';
}
}
else
{
$db->db_query('ROLLBACK');
echo json_encode(array(
'status' => 'fehler',
'msg' => 'Fehler beim Erstellen des UNDO Befehls fuer testtool.tbl_pruefling_gebiet'
));
exit();
}
//Antworten loeschen
$qry = " DELETE FROM testtool.tbl_pruefling_frage where pruefling_id=".$db->db_add_param($pruefling->pruefling_id).";
DELETE FROM testtool.tbl_antwort WHERE pruefling_id=".$db->db_add_param($pruefling->pruefling_id).";";
DELETE FROM testtool.tbl_antwort where pruefling_id=".$db->db_add_param($pruefling->pruefling_id).";
DELETE FROM testtool.tbl_pruefling_gebiet WHERE pruefling_id=".$db->db_add_param($pruefling->pruefling_id).";";
if ($result = $db->db_query($qry))
{
@@ -402,7 +402,10 @@ if(isset($_POST['deleteteilgebiet']))
DELETE FROM testtool.tbl_antwort
WHERE pruefling_id=".$db->db_add_param($pruefling->pruefling_id)." AND
vorschlag_id IN (SELECT vorschlag_id FROM testtool.tbl_vorschlag WHERE frage_id IN
(SELECT frage_id FROM testtool.tbl_frage WHERE gebiet_id=".$db->db_add_param($_POST['gebiet'])."));";
(SELECT frage_id FROM testtool.tbl_frage WHERE gebiet_id=".$db->db_add_param($_POST['gebiet'])."));
DELETE FROM testtool.tbl_pruefling_gebiet
WHERE pruefling_id=".$db->db_add_param($pruefling->pruefling_id)." AND gebiet_id=".$db->db_add_param($_POST['gebiet']).";";
if($result = $db->db_query($qry))
{
@@ -497,7 +500,8 @@ if(isset($_POST['delete_all']))
}
//Gebiet loeschen
$qry = "DELETE FROM testtool.tbl_pruefling_frage where pruefling_id=".$db->db_add_param($pruefling->pruefling_id).";
DELETE FROM testtool.tbl_antwort WHERE pruefling_id=".$db->db_add_param($pruefling->pruefling_id).";";
DELETE FROM testtool.tbl_antwort WHERE pruefling_id=".$db->db_add_param($pruefling->pruefling_id).";
DELETE FROM testtool.tbl_pruefling_gebiet WHERE pruefling_id=".$db->db_add_param($pruefling->pruefling_id).";";
if($result = $db->db_query($qry))
{