From: Winfried Kappeler Date: Sun, 24 May 2020 22:34:36 +0000 (+0200) Subject: WCL2020.05.24.00: Start X-Git-Url: https://gitweb.hamatoma.de/?a=commitdiff_plain;h=be09b836051d61d3de9455019d7cb1bb989c420e;p=webcal.git WCL2020.05.24.00: Start * lauffähige Version --- be09b836051d61d3de9455019d7cb1bb989c420e diff --git a/.buildpath b/.buildpath new file mode 100644 index 0000000..8bcb4b5 --- /dev/null +++ b/.buildpath @@ -0,0 +1,5 @@ + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c04993d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/Deploy.sh +/do.not.deploy.list diff --git a/.project b/.project new file mode 100644 index 0000000..4053352 --- /dev/null +++ b/.project @@ -0,0 +1,28 @@ + + + webcal + + + + + + org.eclipse.wst.common.project.facet.core.builder + + + + + org.eclipse.wst.validation.validationbuilder + + + + + org.eclipse.dltk.core.scriptbuilder + + + + + + org.eclipse.php.core.PHPNature + org.eclipse.wst.common.project.facet.core.nature + + diff --git a/app/common b/app/common new file mode 120000 index 0000000..32c2db8 --- /dev/null +++ b/app/common @@ -0,0 +1 @@ +../../skeleton/app/common \ No newline at end of file diff --git a/app/configurations b/app/configurations new file mode 120000 index 0000000..1fba546 --- /dev/null +++ b/app/configurations @@ -0,0 +1 @@ +../../skeleton/app/configurations \ No newline at end of file diff --git a/app/locals/build/common/locals.common.config.php b/app/locals/build/common/locals.common.config.php new file mode 100644 index 0000000..47e1c1a --- /dev/null +++ b/app/locals/build/common/locals.common.config.php @@ -0,0 +1,9 @@ + '', + 'local_css' => '', + 'local_pageType' => 'base', + 'local_title' => 'Lokales', +); diff --git a/app/locals/build/common/locals.common.snippets.html b/app/locals/build/common/locals.common.snippets.html new file mode 100644 index 0000000..4ec94e0 --- /dev/null +++ b/app/locals/build/common/locals.common.snippets.html @@ -0,0 +1,15 @@ +SNIPPET_NAV_START: + + +
  • + +SNIPPET_NAV_END: +
    + +
    + +END: diff --git a/app/locals/build/common/locals.frame.snippets.html b/app/locals/build/common/locals.frame.snippets.html new file mode 100644 index 0000000..293fe12 --- /dev/null +++ b/app/locals/build/common/locals.frame.snippets.html @@ -0,0 +1,259 @@ +SNIPPET_ROOT: + + + + +###seo### + + + ###global_title### - ###local_title### + + + + + + + +###local_css### + + +
    + +
    +
    + ###CONTENT### +
    + + + + + + + + + + + + + + + + + +SNIPPET_LINK: + + +SNIPPET_COMMON_LEGEND: +###txt_commonLegend### (###SNIPPET_COMMON_FILTER_ACTIVE###) ###count### ###txt_records### + +SNIPPET_COMMON_LEGEND_PAGED: +###txt_commonLegend### (###SNIPPET_COMMON_FILTER_ACTIVE###) ###filtered### ###txt_records### (###txt_of### ###unfiltered###) + +SNIPPET_COMMON_PAGING_DATA: +###txt_page### ###page### ###txt_of### ###pages### + +SNIPPET_COMMON_FILTER_INACTIVE: +###txt_commonFilterInActive### + +SNIPPET_COMMON_FILTER_ACTIVE: +###txt_commonFilterActive### + +SNIPPET_PDF_FRAME: + + + + + + + + + +###local_css### + + +
    + ###CONTENT### +
    +
    + + + + + + + + + + + + + + +
    ###pdf.company.large###
    ###pdf.company.normal######pdf.phone###
    ###pdf.fax###
    ###pdf.email###
    ###pdf.site###
    ###pdf.address######pdf.mobile######pdf.ushd###
    + + + + +SNIPPET_EXCEL_FRAME: + + + + + + + ###CONTENT### + + + +SNIPPET_COMMON_IMG_EDIT: +###iconEdit### + +SNIPPET_SORT_BUTTON: +###arg_1### + +SNIPPET_COMMON_IMG_EDIT_TABLE: +###iconEdit### +###iconChange### + + + +SNIPPET_COMMON_IN_TABLE_CONTROL: + + + + +SNIPPET_COMMON_MESSAGE_IN_TABLE: +###txt_helpEditInTable### + +SNIPPET_COMMON_PAGING: +
    + + + + + + + +
    ###txt_Filtered###: !filtered! ###txt_Unfiltered###: !unfiltered!   +  ###txt_linesPerPage### +
    +
    +
      +###ITEMS### +
    +
    +
    +
    +
    + +SNIPPET_COMMON_PAGING_ITEM: +
  • ###no###
  • + +SNIPPET_COMMON_PAGING_ITEM_ACTIVE: +
  • ###no###
  • + +SNIPPET_COMMON_PAGING_GAP: +
  • ...
  • + +SNIPPET_LEAVE_PAGE: + + + + + + + + +
    +
    +

    Es gibt noch ungesicherte Daten.

    +###ITEMS### +
    +
    +
     
    +
    +
     
    +
    +
    +
    + + + +SNIPPET_LEAVE_PAGE_ITEM: +
    ###label###
    +
    ###value###
    + +SNIPPET_GENERAL_TABLE: + + +~heads~ + +~rows~ +
    + +SNIPPET_CALENDAR: + + + + + + + + +###ROWS### + +
    ###mon######tue######wed######thu######fri######sat######sun###
    + +SNIPPET_CALENDAR_ROW: +###COLS### + + +SNIPPET_CALENDAR_COL: +###DATE######SEP######INFO### + +END: diff --git a/app/locals/locals.controller.php b/app/locals/locals.controller.php new file mode 100644 index 0000000..ba53ec3 --- /dev/null +++ b/app/locals/locals.controller.php @@ -0,0 +1,82 @@ +, ) pairs from $references. + * + * @param string $references + * a blank separated list of keys + * @param array $config + * a map containing the values (with the same key) + * @return array|NULL + */ + public function buildPlaceholderMap(string $references, array &$config): ?array + { + return null; + } + + /** + * Sets the "breadcrumb" info line in a HTML text. + * + * @precondition: a macro ###breadcrumb### exists in the HTML text + * + * @param array $data + * a list of pairs [name, url], e.g. [['Projekte' FULL_URL_APP_PREFIX . '/projects/overview'], ['Neuanlage', '#']] + * @param string $html + * the HTML text with the expanded macro + */ + public function setBreadcrumb(array $data, string $html, string $moduleType = 'std') + { + if (($ixMax = count($data) - 1) < 0) { + $html2 = ''; + } else { + $html2 = "
      \n"; + $container = LN\Config::get('main_sys')['app'][APP_NAME]; + $name1 = 'urlBreadcrumb' . LN\StringUtils::capitalize($moduleType); + $name2 = 'labelBreadcrumb' . LN\StringUtils::capitalize($moduleType); + if (! array_key_exists($name1, $container)) { + $url = FULL_URL_APP_PREFIX . '/overview/base'; + $text = 'Start'; + } else { + $url = LN\Config::get('main_sys')['app'][APP_NAME][$name1]; + $text = LN\Config::get('main_sys')['app'][APP_NAME][$name2]; + } + $html2 .= "
    1. $text
    2. \n"; + for ($ix = 0; $ix < $ixMax; $ix ++) { + $text = $data[$ix][0]; + if ($text[0] === '#'){ + $text = $this->translate($text); + } else { + $text = LN\StringUtils::textToHtml($text2); + } + $url = $data[$ix][1]; + if (! empty($text)) { + $html2 .= "
    3. $text
    4. \n"; + } + } + $text = LN\StringUtils::textToHtml($data[$ixMax][0]); + $html2 .= "
    5. $text
    6. \n"; + $html2 .= "
    \n"; + } + $html = str_replace('###breadcrumb###', $html2, $html); + return $html; + } +} diff --git a/app/locals/locals.model.php b/app/locals/locals.model.php new file mode 100644 index 0000000..571bb2b --- /dev/null +++ b/app/locals/locals.model.php @@ -0,0 +1,79 @@ +__initSQL(); + } + + /** + * Adds a member to a term (table userterm). + * + * @param int $member + * the member to add + * @param int $term + * the related term + */ + public function addMember(int $member, int $term) + { + $sql = $this->_sqlSnippetsModel->getSnippet('SQL_INSERT_USERTERM'); + $this->_db->prepareAndExecute($sql, [ + ':member' => $member, + ':term' => $term, + ':user' => $_SESSION['shortname'] + ]); + } + + /** + * Deletes a member of a term (table userterm). + * + * @param int $member + * the member to add + * @param int $term + * the related term + */ + public function subMember(int $member, int $term) + { + $sql = $this->_sqlSnippetsModel->getSnippet('SQL_DELETE_USERTERM'); + $this->_db->prepareAndExecute($sql, [ + ':member' => $member, + ':term' => $term + ]); + } + + /** + * Assembles all members as a text. + * + * @param int $term + * the members related to this term will be assembled + * @return string the display names of the members, delimited by ' ' + */ + public function membersOfTerm(int $term): string + { + $sql = $this->_sqlSnippetsModel->getSnippet('SQL_USERTERM_BY_TERM'); + $recs = $this->_db->readall($sql, [ + ':term' => $term + ], false); + $rc = ''; + foreach ($recs as &$rec) { + if (! empty($rc)) { + $rc .= ', '; + } + $rc .= $rec['user_displayname']; + } + return $rc; + } +} diff --git a/app/locals/locals.service.php b/app/locals/locals.service.php new file mode 100644 index 0000000..46bcfa0 --- /dev/null +++ b/app/locals/locals.service.php @@ -0,0 +1,168 @@ +daemon($argv); + } + /** + * Prints an error message. + * + * @param string $message + */ + private function error(string $message) + { + echo "+++ $message\n"; + } + + /** + * Offers services outside of the web interface (as command line tool). + * + * @param array $argv + * the program arguments, e.g. ['create-charge', '19'] + */ + public function service(array $argv) + { + if (\count($argv) < 1) { + $this->usage("zu wenig Argumente"); + } else { + $script = \array_shift($argv); + $cmd = \array_shift($argv); + switch ($cmd) { + case 'help': + $this->usage(null); + break; + case 'version': + require_once ROOT . 'config/changes/version.php'; + echo 'Version: ' . APP_VERSION . "\nSkeleton-Version: " . SKELETON_VERSION . "\n"; + break; + case 'test': + $this->test($argv); + break; + case 'daemon': + $this->daemon($argv); + break; + default: + echo "unknown subcommand: $cmd\nUse 'service help' for help\n"; + break; + } + } + } + + /** + * A test whether the service is working. + * + * Counts from + * + * @param array $argv + * expected: [, ] + */ + private function test(array $argv) + { + if (count($argv) < 2) { + $this->argError('too few arguments)'); + } elseif (($count = intval($argv[1])) == 0) { + LN\Service::progressWrite($argv[0], 0, 100, 100, 100); + $this->argError('invalid : ' . $argv[0]); + } else { + $id = $argv[0]; + for ($ix = 0; $ix < $count; $ix++){ + LN\Service::progressWrite($id, 0, 100, $ix, $count); + \sleep(3); + } + LN\Service::progressWrite($id, 0, 100, 1, 1); + } + } +/** + * Handles the tasks of this service. + * + * @param string $task + * the task to do, e.g. 'charge' + * @param array $args + * the task arguments, e.g. ['19'] + * @return bool true: the task could be handled false: unknown task + */ + public function taskHandler(string $task, array $args): bool + { + $locals = Controller::getInstance(); + $rc = true; + echo "task found: $task\n"; + switch ($task) { + case 'test': + $locals->test($args); + break; + default: + $rc = false; + break; + } + return $rc; + } + + + /** + * Displays the usage info. + * + * @param string $message + * null or the error message + */ + private function usage(?string $message) + { + echo "usage: service \n"; + echo ":\n"; + echo " charge \n"; + echo " creates the pools, tokens and qrcodes of a charge\n"; + echo " daemon\n"; + echo " starts an daemon waiting for tasks given by task files in a directory\n"; + echo " help\n"; + echo " displays this info\n"; + echo "..test \n"; + echo "....counts from 0 to (for testing purpose)\n"; + echo " version\n"; + echo " displays the program version\n"; + if ($message != null) { + echo "+++ $message\n"; + } + } +} diff --git a/app/locals/locals.view.php b/app/locals/locals.view.php new file mode 100644 index 0000000..ff757d9 --- /dev/null +++ b/app/locals/locals.view.php @@ -0,0 +1,32 @@ + [ + 'name' => 'common', + 'site' => 1, + 'config' => 1 + ] + ]; + + /** + * Adapts the full page in a application depending way. + * + * @param string $content + * IN/OUT: the full page, may be changed + */ + public function adaptFullPage(string &$content) + { + // do nothing + } + +} diff --git a/app/locals/sql/locals.snippets.sql b/app/locals/sql/locals.snippets.sql new file mode 100644 index 0000000..6ff8c6d --- /dev/null +++ b/app/locals/sql/locals.snippets.sql @@ -0,0 +1,26 @@ +SQL_INSERT_USERTERM: +insert into userterms + (userterm_user, userterm_term, created, createdby) +values + (:member, :term, now(), :user) +; + +SQL_DELETE_USERTERM: +delete + from userterms +where + userterm_term=:term and userterm_user=:member +; + +SQL_USERTERM_BY_TERM: +select + u.user_displayname +from + userterms tt + left join loginusers u on u.user_id=tt.userterm_user +where + userterm_term=:term +order by user_displayname +; + +END: \ No newline at end of file diff --git a/app/login b/app/login new file mode 120000 index 0000000..dd40685 --- /dev/null +++ b/app/login @@ -0,0 +1 @@ +../../skeleton/app/login \ No newline at end of file diff --git a/app/logs b/app/logs new file mode 120000 index 0000000..30e3dc2 --- /dev/null +++ b/app/logs @@ -0,0 +1 @@ +../../skeleton/app/logs \ No newline at end of file diff --git a/app/overview b/app/overview new file mode 120000 index 0000000..be7e20d --- /dev/null +++ b/app/overview @@ -0,0 +1 @@ +../../skeleton/app/overview \ No newline at end of file diff --git a/app/roles b/app/roles new file mode 120000 index 0000000..9ac17a1 --- /dev/null +++ b/app/roles @@ -0,0 +1 @@ +../../skeleton/app/roles \ No newline at end of file diff --git a/app/sessions b/app/sessions new file mode 120000 index 0000000..0b0850d --- /dev/null +++ b/app/sessions @@ -0,0 +1 @@ +../../skeleton/app/sessions \ No newline at end of file diff --git a/app/settings b/app/settings new file mode 120000 index 0000000..fd009af --- /dev/null +++ b/app/settings @@ -0,0 +1 @@ +../../skeleton/app/settings \ No newline at end of file diff --git a/app/starters b/app/starters new file mode 120000 index 0000000..83e3139 --- /dev/null +++ b/app/starters @@ -0,0 +1 @@ +../../skeleton/app/starters \ No newline at end of file diff --git a/app/sysflags b/app/sysflags new file mode 120000 index 0000000..f182201 --- /dev/null +++ b/app/sysflags @@ -0,0 +1 @@ +../../skeleton/app/sysflags \ No newline at end of file diff --git a/app/terms/build/base/terms.base.config.php b/app/terms/build/base/terms.base.config.php new file mode 100644 index 0000000..c0906e4 --- /dev/null +++ b/app/terms/build/base/terms.base.config.php @@ -0,0 +1,15 @@ + '', + 'local_css' => '', + 'local_pageType' => 'base', + 'local_title' => 'Termine', + 'hasFilter' => TC\Controller::getInstance()->configurationAsBool('terms', 'filter:switch'), + + // PHP_HOOK_CONFIG_BASE + '!EoA!' => '' +); diff --git a/app/terms/build/base/terms.base.snippets.html b/app/terms/build/base/terms.base.snippets.html new file mode 100644 index 0000000..3f3f14a --- /dev/null +++ b/app/terms/build/base/terms.base.snippets.html @@ -0,0 +1,58 @@ +SNIPPET_ROOT: +
    ###breadcrumb###
    + +
    + + +
    + +
    +
    + +###SNIPPET_IF_2### +
    + + +###SNIPPET_COMMON_PAGING### + + + + + + + + + + + + + +###ROWS### + +
    Id###COMMON_SNIPPET[SNIPPET_SORT_BUTTON,sort_term_id]###Datum###COMMON_SNIPPET[SNIPPET_SORT_BUTTON,sort_term_date]###Bezeichnung###COMMON_SNIPPET[SNIPPET_SORT_BUTTON,sort_term_name]###PlatzanweiserBemerkung
    +
    +
    + + +SNIPPET_IF_2: +
    + + +SNIPPET_ROW: + + ###alt_edit### + + ###term_id### + ###term_date### + ###term_name### + ###members### + ###term_info### + ###alt_delete### + + + + +SNIPPET_DUMMY_FOR_HOOK: + + +END: diff --git a/app/terms/build/common/terms.common.config.php b/app/terms/build/common/terms.common.config.php new file mode 100644 index 0000000..4d0e492 --- /dev/null +++ b/app/terms/build/common/terms.common.config.php @@ -0,0 +1,13 @@ + '', + 'local_css' => '', + 'data' => 'Daten', 'parent_title' => 'Termine', + + // PHP_HOOK_CONFIG_COMMON + '!EoA!' => '' +); diff --git a/app/terms/build/common/terms.common.snippets.html b/app/terms/build/common/terms.common.snippets.html new file mode 100644 index 0000000..6e42e03 --- /dev/null +++ b/app/terms/build/common/terms.common.snippets.html @@ -0,0 +1,8 @@ +SNIPPET_FIELDS: + + + +SNIPPET_DUMMY_FOR_HOOK: + + +END: diff --git a/app/terms/build/delete/terms.delete.config.php b/app/terms/build/delete/terms.delete.config.php new file mode 100644 index 0000000..7e8df43 --- /dev/null +++ b/app/terms/build/delete/terms.delete.config.php @@ -0,0 +1,14 @@ + '', + 'local_css' => '', + 'local_pageType' => 'delete', + 'local_title' => 'Termin löschen', + + // PHP_HOOK_CONFIG_DELETE + '!EoA!' => '' +); diff --git a/app/terms/build/delete/terms.delete.snippets.html b/app/terms/build/delete/terms.delete.snippets.html new file mode 100644 index 0000000..2187d9b --- /dev/null +++ b/app/terms/build/delete/terms.delete.snippets.html @@ -0,0 +1,27 @@ +SNIPPET_ROOT: +
    + ###breadcrumb### +
    +
    + + + + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    + +
    + +SNIPPET_DUMMY_FOR_HOOK: + + +END: diff --git a/app/terms/build/edit/terms.edit.config.php b/app/terms/build/edit/terms.edit.config.php new file mode 100644 index 0000000..afe55fe --- /dev/null +++ b/app/terms/build/edit/terms.edit.config.php @@ -0,0 +1,14 @@ + '', + 'local_css' => '', + 'local_pageType' => 'edit', + 'local_title' => 'Termin ändern', + + // PHP_HOOK_CONFIG_EDIT + '!EoA!' => '' +); diff --git a/app/terms/build/edit/terms.edit.snippets.html b/app/terms/build/edit/terms.edit.snippets.html new file mode 100644 index 0000000..01c3cbe --- /dev/null +++ b/app/terms/build/edit/terms.edit.snippets.html @@ -0,0 +1,41 @@ +SNIPPET_ROOT: +
    + ###breadcrumb### +
    +
    + + + + + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + +###SNIPPET_IF_2### +###SNIPPET_IF_3### +
    +
    + +
    + + +SNIPPET_IF_2: +###errors### +
    +
    + +SNIPPET_IF_3: +
    + +SNIPPET_DUMMY_FOR_HOOK: + + +END: diff --git a/app/terms/build/new/terms.new.config.php b/app/terms/build/new/terms.new.config.php new file mode 100644 index 0000000..da46f2f --- /dev/null +++ b/app/terms/build/new/terms.new.config.php @@ -0,0 +1,14 @@ + '', + 'local_css' => '', + 'local_pageType' => 'new', + 'local_title' => 'Neuer Termin', + + // PHP_HOOK_CONFIG_NEW + '!EoA!' => '' +); diff --git a/app/terms/build/new/terms.new.snippets.html b/app/terms/build/new/terms.new.snippets.html new file mode 100644 index 0000000..02c155d --- /dev/null +++ b/app/terms/build/new/terms.new.snippets.html @@ -0,0 +1,27 @@ +SNIPPET_ROOT: +
    + ###breadcrumb### +
    +
    + + + + +
    +
    + +
    +
    +
    +###errors### +
    +
    +
    +
    + +
    + +SNIPPET_DUMMY_FOR_HOOK: + + +END: diff --git a/app/terms/sql/terms.snippets.sql b/app/terms/sql/terms.snippets.sql new file mode 100644 index 0000000..bbac8f9 --- /dev/null +++ b/app/terms/sql/terms.snippets.sql @@ -0,0 +1,85 @@ +SQL_SUFFIX_GETALL: +ORDER BY + !order! +!paging! +; + +SQL_SUFFIX_GETALL_COUNT: +; + +SQL_TERM_BY_NAME: +SELECT + * +FROM + terms +WHERE + term_name=:name +; + +SQL_BY_ID_TERM: +SELECT + * +FROM + terms +WHERE + term_id = :termID +; + +SQL_DELETE_BY_ID_TERM: +DELETE FROM + terms +WHERE + term_id = :termID +; + +SQL_TERMS_DEFAULT_ORDER: +term_date + +SQL_TERMS_BASE_GETALL: +SELECT + tt.*,'dummy' as members +FROM + terms tt +WHERE + tt.deletedat is null + +SQL_TERMS_BASE_GETALL_COUNT: +SELECT + count(*) +FROM + terms tt +WHERE + tt.deletedat is null + +SQL_TERMS_NEW_INSERT: +INSERT INTO terms ( + created, + createdby, + term_name, + term_date, + term_info +) VALUES ( + now(), + :loggeduser, + :name, + :date, + :info +); + + + +SQL_TERMS_EDIT_UPDATE: +UPDATE terms set + changed=now(), + changedby=:loggeduser, + term_name = :name, + term_date = :date, + term_info = :info +WHERE + term_id = :termID +; + + + + +END: diff --git a/app/terms/sql/terms.sql b/app/terms/sql/terms.sql new file mode 100644 index 0000000..19e0612 --- /dev/null +++ b/app/terms/sql/terms.sql @@ -0,0 +1,64 @@ +drop table if exists terms; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE terms( + term_id int(10) unsigned NOT NULL AUTO_INCREMENT, + term_name varchar(32), + term_date timestamp, + term_info text, + created timestamp NULL, + createdby varchar(16), + changed timestamp NULL, + changedby varchar(16), + deletedat timestamp NULL, + deletedby varchar(16), + PRIMARY KEY (term_id) +); + +-- ! if no records in select * from configurations where configuration_scope='terms' and configuration_property='required:base' +INSERT INTO configurations +(configuration_scope, configuration_property, configuration_order, configuration_type, configuration_value, configuration_description, configuration_changed_by) +VALUES +('terms', 'required:base', 0, 'string', 'term_id,term_name', 'Pflichtfelder bei terms. Trennung mit Komma', 'wk'); +-- ! endif +insert into starters (starter_name, starter_label, starter_parent, starter_image, starter_link) +values ( +'terms', 'Terms', 'start', 'start/sysflag.png', '/webcal/configurations/base' +); +insert into rolestarter (starter_id, role_id, order_no) +values((select max(starter_id) from starters where starter_name='terms'), 1, 1000), + ((select max(starter_id) from starters where starter_name='terms'), 2, 1000), + ((select max(starter_id) from starters where starter_name='terms'), 3, 1000) +; + + +drop table if exists userterms; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE userterms( + term_id int(10) unsigned NOT NULL AUTO_INCREMENT, + userterm_term int(10) unsigned, + userterm_user int(10) unsigned, + created timestamp NULL, + createdby varchar(16), + changed timestamp NULL, + changedby varchar(16), + deletedat timestamp NULL, + deletedby varchar(16), + PRIMARY KEY (term_id) +); + +-- ! if no records in select * from configurations where configuration_scope='userterms' and configuration_property='required:base' +INSERT INTO configurations +(configuration_scope, configuration_property, configuration_order, configuration_type, configuration_value, configuration_description, configuration_changed_by) +VALUES +('terms', 'required:base', 0, 'string', 'term_id,userterm_term,userterm_user,created,createdby,changed,changedby,deletedat,deletedby', 'Pflichtfelder bei terms. Trennung mit Komma', 'wk'); +-- ! endif +insert into starters (starter_name, starter_label, starter_parent, starter_image, starter_link) +values ( +'terms', 'Terms', 'start', 'start/sysflag.png', '/webcal/configurations/base' +); +insert into rolestarter (starter_id, role_id, order_no) +values((select max(starter_id) from starters where starter_name='terms'), 1, 1000), + ((select max(starter_id) from starters where starter_name='terms'), 2, 1000), + ((select max(starter_id) from starters where starter_name='terms'), 3, 1000) +; + diff --git a/app/terms/terms.controller.php b/app/terms/terms.controller.php new file mode 100644 index 0000000..0d31369 --- /dev/null +++ b/app/terms/terms.controller.php @@ -0,0 +1,23 @@ +__initSQL(); + // PHP_HOOK_CONTROLLER_CONSTRUCTOR + } + // PHP_HOOK_CONTROLLER_CUSTOMIZED_METHODS +} + +?> \ No newline at end of file diff --git a/app/terms/terms.model.php b/app/terms/terms.model.php new file mode 100644 index 0000000..1e154c9 --- /dev/null +++ b/app/terms/terms.model.php @@ -0,0 +1,205 @@ +_sqlSnippetsModel->getSnippet('SQL_TERM_BY_NAME'); + $rc = $this->_db->readSingleRecord($sql, [ + ':name' => $term_name + ], true); + LN\DB::changeDateColumn($rc, 'term_date', $this->dateTimeFormat, true); + LN\DB::changeDateColumn($rc, 'created', $this->dateTimeFormat, true); + LN\DB::changeDateColumn($rc, 'changed', $this->dateTimeFormat, true); + LN\DB::changeDateColumn($rc, 'deletedat', $this->dateTimeFormat, true); + return $rc; + } + + /** + * Returns the db record given by id. + * + * @param int $termID + * primary key + * @return array the database record + */ + public function termByID(int $termID) + { + $sql = $this->_sqlSnippetsModel->getSnippet('SQL_BY_ID_TERM'); + $rc = $this->_db->readSingleRecord($sql, [ + ':termID' => $termID + ]); + LN\DB::changeDateColumn($rc, 'term_date', $this->dateTimeFormat, true); + LN\DB::changeDateColumn($rc, 'created', $this->dateTimeFormat, true); + LN\DB::changeDateColumn($rc, 'changed', $this->dateTimeFormat, true); + LN\DB::changeDateColumn($rc, 'deletedat', $this->dateTimeFormat, true); + return $rc; + } + /** + * Deletes a record given by its primary key. + * + * @param int $id + * primary key + * return bool true: success + */ + public function deleteTermById(int $id, array &$record) + { + $common = TC\Controller::getInstance(); + $common->putToLogDelete('terms', 'term_id', $record); + $sql = $this->_sqlSnippetsModel->getSnippet('SQL_DELETE_BY_ID_TERM'); + $rc = $this->_db->prepareAndExecute($sql, [ + ':termID' => $id + ]); + return $rc !== FALSE; + } + /** + * Returns all records filtered by scope and property. + * + * @param string $sqlReplacement + * null or a string with (search replacement) pairs preceeded by + * Example: ':!idList!:1,2,4:!order!:name' + * @param LN\PagingData $pagingData + * Info about sorting and paging + * @return array the records matching the filter expressions + */ + public function getAllTermsBase(?string $sqlReplacement, LN\PagingData &$pagingData, array &$record) + { + // PHP_HOOK_MODEL_TERMS_BASE_BODY_GETALL + PHP_HOOK_SQL_TERMS_BASE_ALL + PHP_HOOK_SQL_TERMS_BASE_ALL_COUNT + $sql = $this->_sqlSnippetsModel->getSnippet('SQL_TERMS_BASE_GETALL'); + $sql .= "\n" . $this->_sqlSnippetsModel->getSnippet('SQL_SUFFIX_GETALL'); + $search = null; + // PHP_HOOK_MODEL_TERMS_BASE_GETALL_FIX_ARGS + if ($sqlReplacement != null){ + $parts = explode($sqlReplacement[0], substr($sqlReplacement, 1)); + $search = []; + $replacement = []; + for ($ix = 0; $ix < count($parts) / 2; $ix++){ + array_push($search, $parts[2*$ix]); + array_push($replacement, $parts[2*$ix+1]); + } + } + + $sqlCount = $this->_sqlSnippetsModel->getSnippet('SQL_TERMS_BASE_GETALL_COUNT'); + + $sqlCount .= "\n" . $this->_sqlSnippetsModel->getSnippet('SQL_SUFFIX_GETALL_COUNT'); + if ($search != null){ + $sqlCount = str_replace($search, $replacement, $sqlCount); + } + // PHP_HOOK_MODEL_TERMS_BASE_GETALL_START + $params = [ + ]; + // PHP_HOOK_MODEL_TERMS_BASE_GETALL_PARAM_FIX + $countFiltered = intval($this->_db->readUniqueValue($sqlCount, $params)); + $pagingData->setFilteredRecords($countFiltered); + $order = $this->_sqlSnippetsModel->getSnippet('SQL_TERMS_DEFAULT_ORDER'); + // PHP_HOOK_MODEL_TERMS_BASE_PATCH_ORDER + $order = $pagingData->getOrder($order); + $sql = str_replace('!order!', $order, $sql); + + $size = $pagingData->pagingSize; + if ($size <= 0){ + $sql = str_replace('!paging!', '', $sql); + } else { + $offset = max(0, $pagingData->pagingIndex * $size); + $sql = str_replace('!paging!', "limit $offset, $size", $sql); + } + // PHP_HOOK_MODEL_TERMS_BASE_GETALL + if ($search != null){ + $sql = str_replace($search, $replacement, $sql); + } + $rc = $this->_db->readAll($sql, $params, FALSE); + $pagingData->unfilteredRecords = $this->_db->countOfAll('terms'); + if (count($rc) > 0 && \array_key_exists('term_date', $rc[0])) { + LN\DB::changeDateColumn($rc, 'term_date', $this->dateTimeFormat); + } + if (count($rc) > 0 && \array_key_exists('created', $rc[0])) { + LN\DB::changeDateColumn($rc, 'created', $this->dateTimeFormat); + } + if (count($rc) > 0 && \array_key_exists('changed', $rc[0])) { + LN\DB::changeDateColumn($rc, 'changed', $this->dateTimeFormat); + } + if (count($rc) > 0 && \array_key_exists('deletedat', $rc[0])) { + LN\DB::changeDateColumn($rc, 'deletedat', $this->dateTimeFormat); + } + + // PHP_HOOK_MODEL_TERMS_BASE_GETALL_ADAPTION + return $rc; + } + /** + * Creates a new db record. + * + * @param array $record + * the record data as map: column_name -> value + * @return int primary key + */ + public function insertByRecordNewTerm(array &$record) + { + $common = TC\Controller::getInstance(); + $id = 0; + $sql = $this->_sqlSnippetsModel->getSnippet('SQL_TERMS_NEW_INSERT'); + $params = [ + ':loggeduser' => $_SESSION['shortname'], + ':name' => $record['term_name'], + ':date' => $record['term_date'] != null ? LN\DB::dateTimeToDbDateTime($record['term_date']) : null, + ':info' => $record['term_info'], + ]; + // PHP_HOOK_MODEL_NEW_TERMS_INSERT_NEW + if ($this->_db->prepareAndExecute($sql, $params)) { + $id = $this->_db->lastInsertId(); + if ($record != null){ + $common->putToLogInsert('terms', 'term_id', $id); + } + } + return $id; + } + /** + * Updates a database record. + * + * @param int $termID + * primary key + * @param array $record + * the record data as map: column_name -> value + */ + public function updateByRecordEditTerm(int $termID, array &$record) + { + $common = TC\Controller::getInstance(); + $sql = $this->_sqlSnippetsModel->getSnippet('SQL_TERMS_EDIT_UPDATE'); + $params = [ + ':loggeduser' => $_SESSION['shortname'], + ':termID' => $record['termID'], + ':name' => $record['term_name'], + ':date' => $record['term_date'] != null ? LN\DB::dateTimeToDbDateTime($record['term_date']) : null, + ':info' => $record['term_info'], + ]; + // PHP_HOOK_MODEL_EDIT_TERMS_UPDATE + if ($record != null){ + $common->putToLogUpdate('terms', 'term_id', $termID, $record, null, ['termID']); + } + return $this->_db->prepareAndExecute($sql, $params); + } + + + + // PHP_HOOK_MODEL_METHODS +} + +?> \ No newline at end of file diff --git a/app/terms/terms.view.php b/app/terms/terms.view.php new file mode 100644 index 0000000..7062541 --- /dev/null +++ b/app/terms/terms.view.php @@ -0,0 +1,622 @@ + [ + 'name' => 'base', + 'site' => 1, + 'config' => 1 + ], + 'new' => [ + 'name' => 'new', + 'site' => 1, + 'config' => 1 + ], + 'edit' => [ + 'name' => 'edit', + 'site' => 1, + 'config' => 1 + ], + 'delete' => [ + 'name' => 'delete', + 'site' => 1, + 'config' => 1 + ] + ]; + public $_moduleType = 'backend'; + /** + * Returns the configuration of the common page. + * + * @return array the configuration + */ + public function getCommonConfig() + { + // common config is included in edit config: + $config = $this->_getConfiguration($this->_build['edit'], 'edit'); + return $config; + } + /** + * Handles AJAX requests. + */ + public function ajax() + { + [ + $snippets, + $config + ] = $this->_getSnippetsAndConfig($this->_build['edit'], 'edit'); + $common = TC\Controller::getInstance(); + $fieldSet = $this->buildCommonFieldSet('edit', URL_DOMAIN_APP_PREFIX . '/terms/ajax'); + $answer = ''; + if (! \array_key_exists('ajaxMode', $_POST)) { + error_log('Terms::ajax(): Missing argument "ajaxMode"'); + } else { + switch ($_POST['ajaxMode']) { + case 'newChanged': + $answer = $common->testNewChanged($fieldSet); + break; + case 'editChanged': + $record = $this->termById($_POST['termID']); + $answer = $common->testNewChanged($fieldSet); + break; + default: + error_log('Terms::ajax(): unknown mode: ' . $_POST['ajaxMode']); + break; + } + } + $this->answerAjax($answer ?? ''); + } + // PHP_HOOK_VIEW_METHODS + + /** + * Returns the HTML text of the base page (without frame). + * + * @return string the HTML text of the base page (overview table) + */ + public function buildBase(): string + { + $common = TC\Controller::getInstance(); + $locals = LOC\Controller::getInstance(); + // PHP_HOOK_BASE_INIT + [ + $snippets, + $config + ] = $this->_getSnippetsAndConfig($this->_build['base'], 'base'); + $content = $snippets->getSnippet('SNIPPET_ROOT', $config); + $breadcrumbs = [ + [ + $config['local_title'], + '#' + ] + ]; + // PHP_HOOK_BASE_BREADCRUMB + $content = $this->_setBreadcrumb($breadcrumbs, $content, $this->_moduleType); + $validator = new TermsValidator($this); + $urlAction = FULL_URL_APP_PREFIX . '/terms/base'; + $fieldSet = new LN\FieldSet('base', $config, $urlAction, $validator); + $common->addPagingAndSortingFields($fieldSet); + + // PHP_HOOK_BASE_FIELDS + $fieldSet->setFromPost(); + // PHP_HOOK_BASE_START + $storage = TSE\Controller::getInstance()->getStorage($_SESSION['hash'], 'terms', 'base'); + $fieldSet->setFromArray($storage->getData()); + // use all fields: + $storageFields = null; + // PHP_HOOK_BASE_SET_STORAGE_FIELDS + $storage->fetchFromFieldset($fieldSet, $storageFields); + if (count($_POST) == 0){ + // First call + // PHP_HOOK_BASE_FIRST_CALL + } + // PHP_HOOK_BASE_FIELDS_READY + $pagingData = $common->getPagingAndSortingData($fieldSet, $storage); + $empty = false; + if (\array_key_exists('buttonNew', $_POST)) { + $linkPage = '/terms/new'; + // PHP_HOOK_BASE_REDIRECT_BUTTONNEW + if ($linkPage != null) { + LN\Bootstrap::redirectUrl(URL_DOMAIN_APP_PREFIX . $linkPage); + $content = null; + } + // PHP_HOOK_BASE_BUTTONS + } else { + $templateRow = $snippets->getSnippet('SNIPPET_ROW', []); + // PHP_HOOK_BASE_TEMPLATE_ROW + $rows = ''; + $record = []; + $sqlReplacement = ''; + // PHP_HOOK_BASE_BEFORE_GET_ALL + $records = $this->getAllTermsBase($sqlReplacement, $pagingData, $record); + $content = $common->setLegendWithPages($pagingData, $content, ! $empty); + $editAllowed = true; + $deleteAllowed = true; + // === start PHP_HOOK_BASE_TABLE_RECORDS_ADAPTION + $deleteAllowed = $_SESSION['roleID'] <= 2; + // === end PHP_HOOK_BASE_TABLE_RECORDS_ADAPTION + $rowNo = 0; + foreach ($records as &$rec) { + $rowNo++; + // === start PHP_HOOK_BASE_TABLE_ROW_ADAPTION + $rec['members'] = $locals->membersOfTerm($rec['term_id']); + // === end PHP_HOOK_BASE_TABLE_ROW_ADAPTION + $html = $common->rowFromDatabase($templateRow, $rec); + if (! $editAllowed){ + $html = $common->deleteEditIcon($html); + } + if (! $deleteAllowed){ + $html = $common->deleteDeleteIcon($html); + } + $rows .= $html; + } + $content = str_replace('###ROWS###', $rows, $content); + // PHP_HOOK_BASE_HTML + $common->switchConditional($content, 'SNIPPET_IF_2', null, 'role', '1,2', false, $snippets, $config); + $dummyStr = null; + $map = $locals->buildPlaceholderMap('', $config); + // PHP_HOOK_BASE_HTML_REPLACEMENT + $content = $common->replaceInHtmlWithErrors($content, 'terms', $fieldSet, $map, $dummyStr, $pagingData, $storage); + } + // PHP_HOOK_BASE_EXIT + return $content; + } + + /** + * Builds the html text for creation in page new (without frame). + * + * @return string the HTML text of the "new" page + */ + public function buildNew(): string + { + $sysflags = TS\Controller::getInstance(); + $common = TC\Controller::getInstance(); + $locals = LOC\Controller::getInstance(); + // PHP_HOOK_NEW_INIT + [ + $snippets, + $config + ] = $this->_getSnippetsAndConfig($this->_build['new'], 'new'); + // PHP_HOOK_NEW_FIELDSET_START + $content = null; + $validator = new TermsValidator($this); + $urlAction = FULL_URL_APP_PREFIX . '/terms/new'; + $fieldSet = new LN\FieldSet('new', $config, $urlAction, $validator); + $fieldTerm_id = new LN\InputField($fieldSet, "term_id", ':db:hidden:', 'int', null, 'Id'); + $fieldName = new LN\InputField($fieldSet, "term_name", ':db:', 'text', null, 'Bezeichnung'); + $fieldDate = new LN\InputField($fieldSet, "term_date", ':db:', 'datetime', null, 'Datum'); + $fieldInfo = new LN\InputField($fieldSet, "term_info", ':db:', 'text', null, 'Bemerkungen'); + // PHP_HOOK_NEW_FIELDSET_COMPLETE_FIELDS + $fieldSet->setFromPost(); + // PHP_HOOK_NEW_START + $errorMessages = ''; + if (\array_key_exists('buttonClose', $_POST)) { + $linkPage = '/terms/base'; + // PHP_HOOK_NEW_REDIRECT_BUTTONCLOSE + if ($linkPage != null) { + LN\Bootstrap::redirectUrl(URL_DOMAIN_APP_PREFIX . $linkPage); + $content = null; + } + } elseif (\array_key_exists('buttonSave', $_POST)) { + if (! $common->validate('terms.new.store', $fieldSet)) { + $errorMessages = $fieldSet->errorsAsHtml(); + } else { + $record = []; + $record['term_name'] = $fieldSet->getField('term_name')->value; + $record['term_date'] = LN\StringUtils::stringToDateTime($fieldSet->getField('term_date')->value, $error); + $record['term_info'] = $fieldSet->getField('term_info')->value; + // PHP_HOOK_NEW_BEFORE_INSERT + $term_id = $this->insertByRecordNewTerm($record); + $_POST['term_id'] = $term_id; + // PHP_HOOK_NEW_AFTER_INSERT + $linkPage = '/terms/edit?termID='; + $linkPage = str_replace('', \array_key_exists('term_id', $_GET) ? $_GET['term_id'] : $_POST['term_id'], $linkPage); + // PHP_HOOK_NEW_REDIRECT_BUTTONSAVE + if ($linkPage != null) { + LN\Bootstrap::redirectUrl(URL_DOMAIN_APP_PREFIX . $linkPage); + $content = null; + } + // PHP_HOOK_NEW_STORE_END + } + } else { + if (! isset($_POST['term_name'])) { + // first entry: + // PHP_HOOK_NEW_FIRST_CALL + } else { + // PHP_HOOK_NEW_NOT_FIRST_CALL + if (! $common->validate('terms.new', $fieldSet)) { + $errorMessages = $fieldSet->errorsAsHtml(); + } else { + // PHP_HOOK_NEW_VALIDATED + } + } + } + if ($content === null) { + $breadcrumbs = [ + [ + $config['parent_title'], + 'base' + ], + [ + $config['local_title'], + '#' + ] + ]; + $content = $snippets->getSnippet('SNIPPET_ROOT', $config); + // PHP_HOOK_NEW_BREADCRUMBS + $content = $this->_setBreadcrumb($breadcrumbs, $content, $this->_moduleType); + $include = $snippets->getSnippet('SNIPPET_FIELDS', $config, ''); + $content = str_replace('###SNIPPET_FIELDS###', $include, $content); + // PHP_HOOK_NEW_HTML + $map = $locals->buildPlaceholderMap('', $config); + // PHP_HOOK_NEW_HTML_REPLACEMENT + $content = $common->replaceInHtmlWithErrors($content, 'terms', $fieldSet, $map, $errorMessages); + } + // PHP_HOOK_NEW_EXIT + return $content; + } + + /** + * Builds the html text for editing in page edit (without frame). + * + * @return string the HTML text of the "edit" page + */ + public function buildEdit(): string + { + $sysflags = TS\Controller::getInstance(); + $common = TC\Controller::getInstance(); + $locals = LOC\Controller::getInstance(); + // PHP_HOOK_EDIT_INIT + $termID = isset($_GET['termID']) ? $_GET['termID'] : (isset($_POST['term_id']) ? $_POST['term_id'] : -1); + [ + $snippets, + $config + ] = $this->_getSnippetsAndConfig($this->_build['edit'], 'edit'); + // PHP_HOOK_EDIT_FIELDSET_START + $content = null; + $validator = new TermsValidator($this); + $urlAction = FULL_URL_APP_PREFIX . '/terms/edit'; + $fieldSet = new LN\FieldSet('edit', $config, $urlAction, $validator); + $fieldTerm_id = new LN\InputField($fieldSet, "term_id", ':db:hidden:', 'int'); + $fieldName = new LN\InputField($fieldSet, "term_name", ':db:', 'text', null, 'Bezeichnung'); + $fieldDate = new LN\InputField($fieldSet, "term_date", ':db:', 'datetime', null, 'Datum'); + $fieldInfo = new LN\InputField($fieldSet, "term_info", ':db:', 'text', null, 'Bemerkungen'); + $fieldMembers = new LN\InputField($fieldSet, "members", ':readonly:', 'text', null, 'Platzanweiser'); + $fieldMembersShdw = new LN\InputField($fieldSet, "membersShdw", ':isshadow:hidden:', 'text'); + $fieldMember = new LN\InputField($fieldSet, "member", ':undef:', 'combobox', null, 'Ändern'); + // === start PHP_HOOK_EDIT_FIELDSET_COMPLETE_FIELDS + $common->fillUserCombobox($fieldMember, [1, 27, 30], false); + // === end PHP_HOOK_EDIT_FIELDSET_COMPLETE_FIELDS + $fieldSet->setFromPost(); + // PHP_HOOK_EDIT_START + if (isset($_POST['term_name'])) { + $termID = \array_key_exists('term_id', $_POST) ? $_POST['term_id'] : -1; + } else { + if (! \array_key_exists('termID', $_GET) || $_GET['termID'] <= 0) { + LN\Bootstrap::redirectUrl(URL_DOMAIN_APP_PREFIX . '/terms/base'); + } + if (! isset($record)){ + $record = $this->termById($termID); + } + $fieldSet->setFromRecord($record); + // === start PHP_HOOK_EDIT_FIRST_CALL_EARLY + $fieldMember->value = $_SESSION['userID']; + // === end PHP_HOOK_EDIT_FIRST_CALL_EARLY + } + // PHP_HOOK_EDIT_START2 + $errorMessages = ''; + if (\array_key_exists('buttonClose', $_POST)) { + $linkPage = '/terms/base'; + // PHP_HOOK_EDIT_REDIRECT_BUTTONCLOSE + if ($linkPage != null) { + LN\Bootstrap::redirectUrl(URL_DOMAIN_APP_PREFIX . $linkPage); + $content = null; + } + } elseif (\array_key_exists('buttonSave', $_POST)) { + if (! $common->validate('terms.edit.store', $fieldSet)) { + $errorMessages = $fieldSet->errorsAsHtml(); + } else { + $record = []; + $id = LN\StringUtils::intOrNull($fieldSet->getField('term_id')->value); + $record['termID'] = $id; + $record['term_name'] = $fieldSet->getField('term_name')->value; + $record['term_date'] = LN\StringUtils::stringToDateTime($fieldSet->getField('term_date')->value, $error); + $record['term_info'] = $fieldSet->getField('term_info')->value; + // PHP_HOOK_EDIT_BEFORE_UPDATE + $this->updateByRecordEditTerm($id, $record); + // PHP_HOOK_EDIT_REDIRECT_AFTER_UPDATE + $linkPage = '/terms/base'; + // PHP_HOOK_EDIT_REDIRECT_BUTTONSAVE + if ($linkPage != null) { + LN\Bootstrap::redirectUrl(URL_DOMAIN_APP_PREFIX . $linkPage); + $content = null; + } + // PHP_HOOK_EDIT_STORE_END + } + } else { + if (! isset($_POST['term_name'])) { + // first entry: + // PHP_HOOK_EDIT_FIRST_CALL + } else { + // PHP_HOOK_EDIT_NOT_FIRST_CALL + if (! $common->validate('terms.edit', $fieldSet)) { + $errorMessages = $fieldSet->errorsAsHtml(); + } else { + // === start PHP_HOOK_EDIT_VALIDATED + if (\array_key_exists('buttonAdd', $_POST)){ + $locals->addMember($fieldMember->value, $termID); + } elseif (\array_key_exists('buttonSub', $_POST)){ + $locals->subMember($fieldMember->value, $termID); + } + // === end PHP_HOOK_EDIT_VALIDATED + } + } + } + if ($content === null) { + $breadcrumbs = [ + [ + $config['parent_title'], + 'base' + ], + [ + $config['local_title'], + '#' + ] + ]; + $content = $snippets->getSnippet('SNIPPET_ROOT', $config); + // PHP_HOOK_EDIT_BREADCRUMBS + $content = $this->_setBreadcrumb($breadcrumbs, $content, $this->_moduleType); + $include = $snippets->getSnippet('SNIPPET_FIELDS', $config, ''); + $content = str_replace('###SNIPPET_FIELDS###', $include, $content); + $storageAllowed = true; + $buttonName = 'buttonSave'; + // === start PHP_HOOK_EDIT_HTML + $fieldMembers->value = $locals->membersOfTerm($termID); + // === end PHP_HOOK_EDIT_HTML + $common->switchConditional($content, 'SNIPPET_IF_2', null, 'role', '1,2', false, $snippets, $config); + $common->switchConditional($content, 'SNIPPET_IF_3', null, 'role', '3,4', false, $snippets, $config); + if (! $storageAllowed) { + $content = $common->deleteButton($content, $buttonName); + } + $map = $locals->buildPlaceholderMap('', $config); + // PHP_HOOK_EDIT_HTML_REPLACEMENT + $content = $common->replaceInHtmlWithErrors($content, 'terms', $fieldSet, $map, $errorMessages); + } + // PHP_HOOK_EDIT_EXIT + return $content; + } + + /** + * Handles the deletion request of a record. + * + * Deletion can also be done with changing a state property. + * + * @return string the HTML text of the deletion dialog + */ + private function buildDelete(): ?string + { + // PHP_HOOK_DELETE_INIT + [ + $snippets, + $config + ] = $this->_getSnippetsAndConfig($this->_build['delete'], 'delete'); + $sysflags = TS\Controller::getInstance(); + $record = null; + if (\array_key_exists('termID', $_GET)){ + $termID = $_GET['termID']; + $record = $this->termById($termID); + + } else { + $termID = \array_key_exists('term_id', $_POST) ? $_POST['term_id'] : -1; + + } + if ($record == null){ + $record = $this->termById($termID); + } + $common = TC\Controller::getInstance(); + $locals = LOC\Controller::getInstance(); + $content = $snippets->getSnippet('SNIPPET_ROOT', $config); + $breadcrumbs = [ + [ + $config['parent_title'], + 'base' + ], + [ + $config['local_title'], + '#' + ] + ]; + // PHP_HOOK_DELETE_BREADCRUMBS + $content = $this->_setBreadcrumb($breadcrumbs, $content, $this->_moduleType); + // PHP_HOOK_DELETE_START + $validator = null; + $urlAction = FULL_URL_APP_PREFIX . '/terms/delete'; + $fieldSet = new LN\FieldSet('delete', $config, $urlAction, $validator); + $fieldId = new LN\InputField($fieldSet, "term_id", ':hidden:'); + $fieldTerm_id = new LN\InputField($fieldSet, "term_id", ':db:hidden:', 'int', null, 'Id'); + $fieldName = new LN\InputField($fieldSet, "term_name", ':db:', 'text', null, 'Bezeichnung'); + $fieldDate = new LN\InputField($fieldSet, "term_date", ':db:', 'datetime', null, 'Datum'); + $fieldInfo = new LN\InputField($fieldSet, "term_info", ':db:', 'text', null, 'Bemerkungen'); + // PHP_HOOK_DELETE_FIELDSET_COMPLETE_FIELDS + $fieldSet->setAttributeForAll('readonly'); + $fieldSet->setFromPost(); + // PHP_HOOK_DELETE_START + if ($record != null){ + $fieldSet->setFromRecord($record); + } + $fieldId->value = $termID; + if (\array_key_exists('buttonClose', $_POST)) { + $linkPage = '/terms/base'; + // PHP_HOOK_DELETE_REDIRECT_BUTTONCLOSE + if ($linkPage != null) { + LN\Bootstrap::redirectUrl(URL_DOMAIN_APP_PREFIX . $linkPage); + $content = null; + } + } elseif (isset($_POST['buttonDelete'])) { + $this->deleteTermById(intval($termID), $record); + $linkPage = '/terms/base'; + // PHP_HOOK_DELETE_REDIRECT_BUTTONDELETE + if ($linkPage != null) { + LN\Bootstrap::redirectUrl(URL_DOMAIN_APP_PREFIX . $linkPage); + $content = null; + } + } + $deleteAllowed = true; + $buttonName = 'buttonDelete'; + // PHP_HOOK_DELETE_HTML + if (! $deleteAllowed) { + $content = $common->deleteButton($content, $buttonName); + } + $map = $locals->buildPlaceholderMap('', $config); + // PHP_HOOK_DELETE_HTML_REPLACEMENT + $content = $common->replaceInHtmlWithErrors($content, 'terms', $fieldSet, $map); + // PHP_HOOK_DELETE_EXIT + return $content; + } + + /** + * Builds and displays the "base" page. + * Method will be called by URL. + * + * @return string the html text of the full page + * only used for unit testing + */ + public function base() + { + // PHP_HOOK_VIEW_BUILD_METHOD_BASE + $html = $this->buildBase(); + + $macros = $this->_getConfiguration($this->_build['base'], 'base'); + $common = TC\Controller::getInstance(); + $locals = LOC\Controller::getInstance(); + $html = $common->buildFullPage($macros, $html); + $locals->adaptFullPage($html); + $this->_showHTML($html); + return $html; + } + + /** + * Builds and displays the "new" page. + * Method will be called by URL. + * + * @return string the html text of the full page + * only used for unit testing + */ + public function new() + { + // PHP_HOOK_VIEW_BUILD_METHOD_NEW + $html = $this->buildNew(); + + $macros = $this->_getConfiguration($this->_build['new'], 'new'); + $common = TC\Controller::getInstance(); + $locals = LOC\Controller::getInstance(); + $html = $common->buildFullPage($macros, $html); + $locals->adaptFullPage($html); + $this->_showHTML($html); + return $html; + } + + /** + * Builds and displays the "edit" page. + * Method will be called by URL. + * + * @return string the html text of the full page + * only used for unit testing + */ + public function edit() + { + // PHP_HOOK_VIEW_BUILD_METHOD_EDIT + $html = $this->buildEdit(); + + $macros = $this->_getConfiguration($this->_build['edit'], 'edit'); + $common = TC\Controller::getInstance(); + $locals = LOC\Controller::getInstance(); + $html = $common->buildFullPage($macros, $html); + $locals->adaptFullPage($html); + $this->_showHTML($html); + return $html; + } + + /** + * Builds and displays the "delete" page. + * Method will be called by URL. + * + * @return string the html text of the full page + * only used for unit testing + */ + public function delete() + { + // PHP_HOOK_VIEW_BUILD_METHOD_DELETE + $html = $this->buildDelete(); + + $macros = $this->_getConfiguration($this->_build['delete'], 'delete'); + $common = TC\Controller::getInstance(); + $locals = LOC\Controller::getInstance(); + $html = $common->buildFullPage($macros, $html); + $locals->adaptFullPage($html); + $this->_showHTML($html); + return $html; + } + +} +class TermsValidator implements LN\IFieldValidator +{ + private $_controller; + private $_parent; + /** + * Constructor. + * + * @param Controller $controller + * the controller of the abbreviations module + */ + public function __construct(Controller $controller) + { + $this->_controller = $controller; + $parent = new TC\Validator(); + $this->setValidatorParent($parent); + } + /** + * + * {@inheritdoc} + * @see \lib\native\IFieldValidator::getValidatorParent() + */ + public function getValidatorParent(): ?IFieldValidator + { + return $this->_parent; + } + + /** + * + * {@inheritdoc} + * @see \lib\native\IFieldValidator::setValidatorParent() + */ + public function setValidatorParent(IFieldValidator $parent): void + { + $this->_parent = $parent; + } + /** + * + * {@inheritdoc} + * @see \lib\native\IFieldValidator::validateInputField() + */ + public function validateInputField(InputField $field): bool + { + $rc = true; + $common = TC\Controller::getInstance(); + $locals = LOC\Controller::getInstance(); + // PHP_HOOK_VALIDATION_START + // PHP_HOOK_VALIDATE_INPUT_FIELD + return $rc; + } +} +?> + diff --git a/app/users b/app/users new file mode 120000 index 0000000..e59009f --- /dev/null +++ b/app/users @@ -0,0 +1 @@ +../../skeleton/app/users \ No newline at end of file diff --git a/config/changes/skeletonversion.php b/config/changes/skeletonversion.php new file mode 120000 index 0000000..859d9fe --- /dev/null +++ b/config/changes/skeletonversion.php @@ -0,0 +1 @@ +../../../skeleton/config/changes/skeletonversion.php \ No newline at end of file diff --git a/config/changes/version.php b/config/changes/version.php new file mode 100644 index 0000000..e6db836 --- /dev/null +++ b/config/changes/version.php @@ -0,0 +1,22 @@ + diff --git a/config/sys/.gitignore b/config/sys/.gitignore new file mode 100644 index 0000000..8a71748 --- /dev/null +++ b/config/sys/.gitignore @@ -0,0 +1 @@ +/main.php diff --git a/config/sys/error.php b/config/sys/error.php new file mode 120000 index 0000000..4c84964 --- /dev/null +++ b/config/sys/error.php @@ -0,0 +1 @@ +../../../skeleton/config/sys/error.php \ No newline at end of file diff --git a/config/sys/loader.php b/config/sys/loader.php new file mode 120000 index 0000000..04111f1 --- /dev/null +++ b/config/sys/loader.php @@ -0,0 +1 @@ +../../../skeleton/config/sys/loader.php \ No newline at end of file diff --git a/db/.gitignore b/db/.gitignore new file mode 100644 index 0000000..31cbb99 --- /dev/null +++ b/db/.gitignore @@ -0,0 +1 @@ +/webcal.sql.gz diff --git a/lib b/lib new file mode 120000 index 0000000..183f052 --- /dev/null +++ b/lib @@ -0,0 +1 @@ +../skeleton/lib \ No newline at end of file diff --git a/public/css/page/common/global.css b/public/css/page/common/global.css new file mode 120000 index 0000000..046e7c4 --- /dev/null +++ b/public/css/page/common/global.css @@ -0,0 +1 @@ +../../../../../skeleton/public/css/page/common/global.css \ No newline at end of file diff --git a/public/css/page/common/jquery.datetimepicker.css b/public/css/page/common/jquery.datetimepicker.css new file mode 120000 index 0000000..4851126 --- /dev/null +++ b/public/css/page/common/jquery.datetimepicker.css @@ -0,0 +1 @@ +../../../../../skeleton/public/css/page/common/jquery.datetimepicker.css \ No newline at end of file diff --git a/public/css/page/common/local.css b/public/css/page/common/local.css new file mode 100644 index 0000000..de23d49 --- /dev/null +++ b/public/css/page/common/local.css @@ -0,0 +1,133 @@ +@charset "UTF-8"; + +a { + color: #859319; +} + +fieldset { + background-color: #e4eda3; + border-radius: 4px; +} + +legend { + font-size: 0.9em; +} + +input { + border-radius: 3px; + margin-bottom: 0.5em; +} + +select { + border-radius: 3px; + margin-bottom: 0.5em; + height: 1.8em; +} + +button { + border-radius: 3px; +} + +label, .pseudo-label { + text-align: left; +} + +.page-item.active .page-link { + background-color: #B1C903 +} +/* Hintergrundfarbe der Navigation-Menue-Leiste */ +nav.navbar { + background-color: #B1C903; +} +/* Schrift in Menüleiste */ +.navbar a.nav-link { + color: #EEE; + /* background-color: #e4eda3; */ +} + +.tabs-modules { + display: flex; + flex-wrap: wrap; + padding-left: 0; + margin-bottom: 0; + list-style: none; +} + +.tab-module-link { + display: block; + text-decoration: none; +} + +.tab-pane.active { + background-color: #e4eda3; +} + +.tabs-modules a.tab-module-link { + background-color: #f0f0f0; + border-color: #111; + border-top-left-radius: 10px; + border-top-right-radius: 10px; + border: solid 1px black; + border-bottom: none; + padding: 0.5em; +} + +.tabs-modules a.tab-module-link, .tabs-modules a:link.tab-module-link, + .tabs-modules a:visited.tab-module-link, .tabs-modules a:active.tab-module-link, + .tabs-modules a:hover.tab-module-link { + text-decoration: none; + color: #859319; +} + +.tabs-modules a.tab-module-link.active { + background-color: #e4eda3; +} + +.tabs-modules a:hover.tab-module-link { + font-weight: bold; + color: #7c841e; +} + +.tabs-modules a:hover.tab-module-link.active { + text-decoration: none; + font-weight: normal; +} + +.tabs-modules a:hover.nav-link { + color: #444; + font-weight: bold; +} + +.page-item.active .page-link { + border-color: #879901; +} + +.page-link { + color: #B1C903; +} + +.page-link:hover { + color: #0; +} + +.paging-text { + font-size: 0.8em; +} + +.editdatetime, .edittextmiddle, .editselectshort, .edittextshort, + .editnumber { + width: 100%; +} + +.readonly { + background-color: #e4eda3; + border: 1px solid #B1C903; +} + +input, .pseudo-field { + width: 100%; +} + +.filtered-text { + background-color: #e4eda3; +} \ No newline at end of file diff --git a/public/css/page/common/pdf.global.css b/public/css/page/common/pdf.global.css new file mode 120000 index 0000000..88c8b9b --- /dev/null +++ b/public/css/page/common/pdf.global.css @@ -0,0 +1 @@ +../../../../../skeleton/public/css/page/common/pdf.global.css \ No newline at end of file diff --git a/public/css/page/overview/overview.global.css b/public/css/page/overview/overview.global.css new file mode 120000 index 0000000..5e78b45 --- /dev/null +++ b/public/css/page/overview/overview.global.css @@ -0,0 +1 @@ +../../../../../skeleton/public/css/page/overview/overview.global.css \ No newline at end of file diff --git a/public/css/page/overview/overview.local.css b/public/css/page/overview/overview.local.css new file mode 100644 index 0000000..ffd5d70 --- /dev/null +++ b/public/css/page/overview/overview.local.css @@ -0,0 +1,13 @@ +@charset "UTF-8"; + +a.overview-cell, a:visited.overview-cell, a:link.overview-cell, a:active.overview-cell, + .dashboard-link a, .dashboard-link a:visited, .dashboard-link a:link, + .dashboard-link a:active { + border: 1px solid #B1C903; + color: #859319; + box-shadow: 0 0 5px 0 #88961e; +} + +a:hover.overview-cell { + background-color: rgba(177, 201, 3, 0.5); +} \ No newline at end of file diff --git a/public/css/vendor b/public/css/vendor new file mode 120000 index 0000000..f174506 --- /dev/null +++ b/public/css/vendor @@ -0,0 +1 @@ +../../../skeleton/public/css/vendor \ No newline at end of file diff --git a/public/fonts b/public/fonts new file mode 120000 index 0000000..152b0c7 --- /dev/null +++ b/public/fonts @@ -0,0 +1 @@ +../../skeleton/public/fonts \ No newline at end of file diff --git a/public/icon/famfamfam b/public/icon/famfamfam new file mode 120000 index 0000000..21973d5 --- /dev/null +++ b/public/icon/famfamfam @@ -0,0 +1 @@ +../../../skeleton/public/icon/famfamfam \ No newline at end of file diff --git a/public/icon/favicon.ico b/public/icon/favicon.ico new file mode 120000 index 0000000..b2d1e8a --- /dev/null +++ b/public/icon/favicon.ico @@ -0,0 +1 @@ +kirchturm.ico \ No newline at end of file diff --git a/public/icon/kirchturm.ico b/public/icon/kirchturm.ico new file mode 100644 index 0000000..c0bacdc Binary files /dev/null and b/public/icon/kirchturm.ico differ diff --git a/public/icon/msg b/public/icon/msg new file mode 120000 index 0000000..a474549 --- /dev/null +++ b/public/icon/msg @@ -0,0 +1 @@ +../../../skeleton/public/icon/msg \ No newline at end of file diff --git a/public/icon/start b/public/icon/start new file mode 120000 index 0000000..fca59e8 --- /dev/null +++ b/public/icon/start @@ -0,0 +1 @@ +../../../skeleton/public/icon/start \ No newline at end of file diff --git a/public/img/vendor b/public/img/vendor new file mode 120000 index 0000000..2408d6c --- /dev/null +++ b/public/img/vendor @@ -0,0 +1 @@ +../../../skeleton/public/img/vendor \ No newline at end of file diff --git a/public/index.php b/public/index.php new file mode 100644 index 0000000..94ed55f --- /dev/null +++ b/public/index.php @@ -0,0 +1,72 @@ +errorHandler($error['type'], $error['message'], $error['file'], $error['line'], debug_backtrace()); + } + + session_write_close(); + + if (LN\Config::get('main_sys')['profiler']['logAtShutdown']) { + LN\Profiler::getInstance()->end(); + } +}); +define('AJAX', LN\Tool::isAJAX()); +define('DOMAIN', LN\Config::getHostConfig('main_sys:domain', 'covid.infeos.eu')); + +// If changed: change root.definitions.php also! +LN\SemaphorePool::init(array( + "singleton" => 47 +)); + +LN\Error::getInstance(); + +LN\Session::getInstance(); +if (LN\Config::get('main_sys')['autoLogin']) { + $_SESSION['roleID'] = ROLE_MANAGER; + $_SESSION['userID'] = 4; + $_SESSION['shortname'] = 'wk'; +} + +LN\Bootstrap::getInstance(); + +?> diff --git a/public/js/common b/public/js/common new file mode 120000 index 0000000..58acc78 --- /dev/null +++ b/public/js/common @@ -0,0 +1 @@ +../../../skeleton/public/js/common \ No newline at end of file diff --git a/public/js/vendor b/public/js/vendor new file mode 120000 index 0000000..4f48350 --- /dev/null +++ b/public/js/vendor @@ -0,0 +1 @@ +../../../skeleton/public/js/vendor \ No newline at end of file diff --git a/tools/generator/.gitignore b/tools/generator/.gitignore new file mode 100644 index 0000000..62ad278 --- /dev/null +++ b/tools/generator/.gitignore @@ -0,0 +1,2 @@ +/generator.root.def.php +/rungenerator.php diff --git a/tools/generator/templates b/tools/generator/templates new file mode 120000 index 0000000..3dfdcb1 --- /dev/null +++ b/tools/generator/templates @@ -0,0 +1 @@ +../../../skeleton/tools/generator/templates \ No newline at end of file diff --git a/tools/generator/terms.module b/tools/generator/terms.module new file mode 100644 index 0000000..ddeba02 --- /dev/null +++ b/tools/generator/terms.module @@ -0,0 +1,102 @@ +module Terms Term + option.module use:terms.snippets.php + option.module project:webcal + option.module type:backend + + dbtable terms term + dbfield term_id "Id" int primary + dbfield term_name "Bezeichnung" text size:32 secondary + dbfield term_date "Datum" datetime + dbfield term_info "Bemerkungen" text size:4000 rows:3 + option.dbtable default.order:term_date + end.dbtable + + dbtable userterms userterm + option.dbtable make.sql + dbfield term_id "Id" int primary + dbfield userterm_term " " int ref:terms.term_id + dbfield userterm_user " " int ref:loginusers.user_id + end.dbtable + + dbtable loginusers user + dbfield user_id "Id" int primary + dbfield user_name "Kennung" text size:200 secondary notnull unique width:2:4 + dbfield user_displayname "Anzeigename" text size:32 notnull unique width:2:4 + dbfield user_shortname "Kürzel" text size:20 notnull unique width:2:4 + dbfield user_login "E-Mail" text size:100 notnull unique width:2:4 + dbfield user_gender "Geschlecht" text size:1 notnull field.option:undef combobox width:2:4 + dbfield user_status "Status" int ref:sysflags.sysflag_id field.option:undef width:2:4 + dbfield user_info "Info" text size:128 width:2:10 + dbfield role_id "Rolle" int ref:roles.role_id field.option:undef width:2:4 + dbfield user_hourssat "Stunden Sa" float width:2:1 + dbfield user_hourssun "So" float width:1:1 + dbfield user_hoursmon "Stunden Mo" float width:2:1 + dbfield user_hourstue "Di" float width:1:1 + dbfield user_hourswed "Mi" float width:1:1 + dbfield user_hoursthu "Do" float width:1:1 + dbfield user_hoursfri "Fr" float width:1:1 + dbfield user_pass "Passwort" text size:200 hidden + dbfield user_token "Token" text size:100 hidden + dbfield user_tokenstamp "Tokenzeit" datetime hidden + dbfield changed "Geändert" datetime update now hidden + dbfield changedby "Geändert von" text size:20 auto.user hidden + dbfield created "Erzeugt" datetime update now hidden + dbfield createdby "Erzeugt von" text size:20 auto.user hidden + end.dbtable + + page base "Termine" + option.page sql.getall:PHP_HOOK_SQL_GET_ALL + option.page sql.getall.count:PHP_HOOK_SQL_GET_ALL_COUNT + section panel.fields + empty.line + if role in 1,2 + button buttonNew "Neuer Termin" 0 12 redirect:*.new + empty.line + end.if + end.section + section table + option.table viewed:term_id:term_date:term_name:members:term_info + option.table sorted:term_id:term_date:term_name + option.table heads:"Id":"Datum":"Bezeichnung":"Platzanweiser":"Bemerkung" + option.table edit.by.dialog + option.table delete.by.dialog + end.section + end.page + + page new "Neuer Termin" + section panel.fieldgroup + fill.from.db.table terms + button buttonSave "Speichern" 2 4 save.and.edit + button buttonClose "Schließen" 2 4 close + end.section + end.page + + page edit "Termin ändern" + section panel.fieldgroup + dbreference term_name 2 4 + dbreference term_date 2 4 + dbreference term_info 2 10 + field members "Platzanweiser" text 2 10 rows:3 readonly no.storage + field member "Ändern" int 2 4 combobox undef no.storage + button buttonAdd "Hinzufügen" 0 3 none + button buttonSub "Entfernen" 0 3 none + empty.line + if role in 1,2 + button buttonSave "Speichern" 2 4 save.and.exit + end.if + if role in 3,4 + filler 6 + end.if + button buttonClose "Schließen" 0 3 close + end.section + end.page + + page delete "Termin löschen" + section panel.fieldgroup + fill.from.db.table terms + button buttonDelete "Löschen" 2 4 delete.direct + button buttonClose "Schließen" 2 4 close + end.section + end.page + +end.module diff --git a/tools/generator/terms.snippets.php b/tools/generator/terms.snippets.php new file mode 100644 index 0000000..8aecdc0 --- /dev/null +++ b/tools/generator/terms.snippets.php @@ -0,0 +1,39 @@ +PHP_HOOK_SQL_GET_ALL: +SELECT + tt.*,'dummy' as members +FROM + terms tt +WHERE + tt.deletedat is null + +PHP_HOOK_SQL_GET_ALL_COUNT: +SELECT + count(*) +FROM + terms tt +WHERE + tt.deletedat is null + +PHP_HOOK_EDIT_FIELDSET_COMPLETE_FIELDS: + $common->fillUserCombobox($fieldMember, [1, 27, 30], false); + +PHP_HOOK_EDIT_VALIDATED: + if (\array_key_exists('buttonAdd', $_POST)){ + $locals->addMember($fieldMember->value, $termID); + } elseif (\array_key_exists('buttonSub', $_POST)){ + $locals->subMember($fieldMember->value, $termID); + } + +PHP_HOOK_EDIT_HTML: + $fieldMembers->value = $locals->membersOfTerm($termID); + +PHP_HOOK_BASE_TABLE_ROW_ADAPTION: + $rec['members'] = $locals->membersOfTerm($rec['term_id']); + +PHP_HOOK_EDIT_FIRST_CALL_EARLY: + $fieldMember->value = $_SESSION['userID']; + +PHP_HOOK_BASE_TABLE_RECORDS_ADAPTION: + $deleteAllowed = $_SESSION['roleID'] <= 2; + +END: \ No newline at end of file diff --git a/tpl/email/newPwd.html b/tpl/email/newPwd.html new file mode 100644 index 0000000..f9c805b --- /dev/null +++ b/tpl/email/newPwd.html @@ -0,0 +1,16 @@ + + + + + +

    [&&SALUTATION] [&&PERSON],

    +

    Ihr neues Passwort lautet: [&&PASSWORD]

    +

    Mit freundlichen Grüßen

    +

    [&&EMAIL_SIGNATURE]

    + + \ No newline at end of file diff --git a/tpl/email/newPwd.txt b/tpl/email/newPwd.txt new file mode 100644 index 0000000..11deb5f --- /dev/null +++ b/tpl/email/newPwd.txt @@ -0,0 +1,7 @@ +[&&SALUTATION] [&&PERSON], + +Ihr neues Passwort lautet: [&&PASSWORD] + +Mit freundlichen Grüßen + +[&&EMAIL_SIGNATURE] \ No newline at end of file diff --git a/tpl/email/newUser.html b/tpl/email/newUser.html new file mode 100644 index 0000000..e66bbb0 --- /dev/null +++ b/tpl/email/newUser.html @@ -0,0 +1,26 @@ + + + + + +

    [&&SALUTATION] [&&PERSON],

    +

    Sie wurden auf dem Webportal - TimeTracking angelegt: [&&LINK]

    + + + + + + + + + +
    Benutzername:[&&EMAIL]
    Passwort:[&&PASSWORD]
    +

    Mit freundlichen Grüßen

    +

    [&&EMAIL_SIGNATURE]

    + + \ No newline at end of file diff --git a/tpl/email/newUser.txt b/tpl/email/newUser.txt new file mode 100644 index 0000000..d95eb4f --- /dev/null +++ b/tpl/email/newUser.txt @@ -0,0 +1,10 @@ +[&&SALUTATION] [&&PERSON], + +Sie wurden auf dem Webportal - TimeTracking angelegt: [&&LINK] + +Benutzername: [&&EMAIL] +Passwort: [&&PASSWORD] + +Mit freundlichen Grüßen + +[&&EMAIL_SIGNATURE] \ No newline at end of file diff --git a/tpl/email/pwdForgotten.html b/tpl/email/pwdForgotten.html new file mode 100644 index 0000000..6e29ef1 --- /dev/null +++ b/tpl/email/pwdForgotten.html @@ -0,0 +1,17 @@ + + + + + +

    [&&SALUTATION] [&&PERSON],

    +

    klicken Sie bitte auf den Link um Ihr Passwort zu ändern. Die Sitzung ist in [&&TIMEOUT] Minuten abgelaufen:

    +

    Passwort ändern

    +

    Mit freundlichen Grüßen

    +

    [&&EMAIL_SIGNATURE]

    + + \ No newline at end of file diff --git a/tpl/email/pwdForgotten.txt b/tpl/email/pwdForgotten.txt new file mode 100644 index 0000000..61a9a87 --- /dev/null +++ b/tpl/email/pwdForgotten.txt @@ -0,0 +1,9 @@ +[&&SALUTATION] [&&PERSON], + +klicken Sie bitte auf den Link um Ihr Passwort zu ändern. Die Sitzung ist [&&TIMEOUT] Minuten abgelaufen: + +[&&LINK] + +Mit freundlichen Grüßen + +[&&EMAIL_SIGNATURE] \ No newline at end of file