Commit 031296d7 authored by Carsten  Rose's avatar Carsten Rose
Browse files

OnArray.php: some doc enhanced

parent 4d6214ec
...@@ -58,4 +58,5 @@ QFQ Extension ...@@ -58,4 +58,5 @@ QFQ Extension
:maxdepth: 4 :maxdepth: 4
Manual Manual
Release
Links Links
This diff is collapsed.
.. _release:
Release
=======
Version 0.future
----------------
Changes
^^^^^^^
* Play formEditor.sql.
* Dropdownlist of container assignment:
* Display 'type' ('pill', 'fieldset', 'templategroup') instead of 'class' (always 'container').
* Display 'name' (internal name) instead of 'label' (shown on the website and might not so usefull as 'name' which is nowhere else used than in that dropdown.
* FormElement.placeholder colum width extended to 512:
ALTER TABLE `FormElement` CHANGE `placeholder` `placeholder` VARCHAR(512) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '';
Features
^^^^^^^^
Bug Fixes
^^^^^^^^^
Version 0.13
------------
Changes
^^^^^^^
* Play formEditor.sql.
* formEditor.sql:
* Checktype of `Form.name` restricted to `alnumx` (prior `all`).
* Changed `access` for Form `form` & '`ormElement` from `always` to `sip`.
* Table `FormElement`
* Modified column: `checkType` - new value `numerical`
ALTER TABLE FormElement MODIFY COLUMN checkType ENUM('alnumx','digit','numerical','email','min|max','min|max date',
'pattern','allbut','all') NOT NULL DEFAULT 'alnumx'
* Example Report for `forms` extended by a delete button per row.
Features
^^^^^^^^
* print.php: offers 'print page' for any local page - create a PDF on the fly (printout is then browser independent).
* Install `wkhtmltopdf` on the webserver (http://wkhtmltopdf.org/).
* In config.qfq.ini setup:
BASE_URL_PRINT=http://www.../
WKHTMLTOPDF=/opt/wkhtmltox/bin/wkhtmltopdf
* Check and error report if 'php_intl' is missing.
* New Checktype 'allow numerical'.
* Documentation: example for 'radio' with no pre selection.
* #3063, Radios and checkboxes optional rendered in Bootstrap layout.
* Added 'help-box with-errors'-DIV after radios and checkboxes.
* Respect attribute `data-class-on-change` on save buttons.
Bug Fixes
^^^^^^^^^
* #2138 / digit sanitize: new class 'numerical' implemented.
* Fixed recursive thrown exception.
* #2064 / search of a default value for a non existing tablecolumn returns 'false'.
* Fixed setting of STORE_SYSTEM / showDebugInfo during API call.
* #2081, #3180 Form: Label & note - update via `DynamicUpdate`
* #3253, if there is no STORE_TYPO3 (calls through .../api/ like save, delete, load): use SIP / CLIENT_TYPO3VARS.
* qfq-bs.css:
* Alignment of checkboxes and radios optimized.
* CSS class 'qfq-note' for 'notes' (third column in a form).
Version 0.12
------------
Changes
^^^^^^^
* Table 'FormElement'
* New column: rowLabelInputNote
ALTER TABLE `FormElement` ADD `rowLabelInputNote` set('row','label','/label','input','/input','note','/note','/row')
NOT NULL DEFAULT 'row,label,/label,input,/input,note,/note,/row' AFTER `bsNoteColumns` ;
* Modified column: 'type' - new value 'templateGroup'
ALTER TABLE `FormElement` CHANGE `type` `type` ENUM( 'checkbox', 'date', 'datetime', 'dateJQW', 'datetimeJQW', 'extra',
'gridJQW', 'text', 'editor', 'time', 'note', 'password', 'radio', 'select', 'subrecord', 'upload', 'fieldset', 'pill',
'templateGroup', 'beforeLoad', 'beforeSave', 'beforeInsert', 'beforeUpdate', 'beforeDelete', 'afterLoad', 'afterSave',
'afterInsert', 'afterUpdate', 'afterDelete', 'sendMail' ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'text';
* formEditor.sql: Added HTML 'placeholder' in FormEditor for bs*Columns.
* PLAY 'formEditor.sql'.
* User Input will be UTF8 normalized.
* INSTALL 'php5-intl' or 'php7.0-intl' on Webserver.
* Add globalize.js to be included. Needed by jqx-all.js
* UPDATE EXISTING TypoScript TEMPLATES of QFQ Installation.
* Name of variable '_filename' (used in field 'parameter') has changed. Old: '_filename', New: 'filename'
* UPDATE `FormElement` SET parameter = REPLACE(parameter, '_filename', 'filename')
Features
^^^^^^^^
* User input will be UTF8 normalized
* config.qfq-ini:
* New configuration values: FORM_BS_LABEL_COLUMNS / FORM_BS_INPUT_COLUMNS / FORM_BS_NOTE_COLUMNS
* Comment empty variables - the new default setting is, that empty parameter in config.qfq.ini means EMPTY (=parameter is set and will not be overwritten by internal default), not UNDEFINED (overwritten by internal default).
* FileUpload:
* Implemented new Formelement.parameter: fileReplace=always - will replace existing files.
* Multiple / Advanced Upload: new logic implements slaveId, sqlInsert, sqlUpdate, sqlDelete.
* FormElement.parameter: sqlBefore / sqlAfter fired during 'Form' save for action elements.
* STORE FORM: variable 'filename' moved to STORE VAR - sanatize class needs no longer specified.
* STORE VAR: two new variables 'filename' and 'fileDestination' valid during processing of current upload FormElement.
* Default store priority list changed. Old: 'FSRD', New: 'FSRVD'.
* CODING.md: update doc for FormElement 'upload' and general 'Form' rendering & save (recursive rendering).
* User manual:
* Described form layout options: description for bsLabelColumn, bsInputColumn, bsNoteColumn
* Update 'file-upload' doc.
* Described 3 examples for upload forms.
* Administrator manual:
* Add description page.meta...
* New FormElement (type= 'container') added: 'templateGroup'
* FormElement.parameter.tgAddClass | tgAddText | tgRemoveClass | tgRemoveText | tgClass
* FormElement.maxSize: max number of duplicates
* #3230 templateGroup: margin between copies. 'tgClass' implemented.
* Native FormElements:
* FormElement.parameter.htlmlBefore|htmlAfter - add the specified HTML code before or after the element (outside of any wrapping)
* #3224, #3231 Html Tag <hr> als FormElement. >> htmlBefore | htmlAfter.
* FormElement.parameter.wrapLabel | wrapInput | wrapAfter | wrapRow - if specified, any default wrapping is omitted.
* FormElement.bsNoteColumns | bsInputColumns | bsNoteColumns - a '0' will suppress the whole rendering of the item.
* FormElement.rowLabelInputNote - switch on/off rendering of the corresponding system wrapping items.
* #3232 Define custom 'on-change' color - used for the save button: Form.parameter.buttonOnChangeClass=...
* Form.parameter & FormElement.parameter: Lines starting with '#' are treated as comments and will not be parsed.
Bug fixes
^^^^^^^^^
* User manual:
* Fixed double include of validator.js in T3 Typoscript template example.
* Fixed wrong store name SYSTEM: S > Y
* Fixed wrong STORE_FORM variable names.
* Reformat FormElement.parameter description.
* Styling errors fixed.
* Use of 'decryptCurlyBraces()' to get better error messages.
* Skip unwanted parameter expansion during save.
* Fixed bug with uninitialized FE_SLAVE_ID
* formEditor.sql:
* The defintion as 'editor' (not text) for FormElement 'note' has been lost - reinserted.
* Fixed problem while playing SQL query - deleting old FormElements of Formeditor deleted also FormElements of other forms.
* #3066 / help-text with-error - CSS class 'hidden' will be rendered by default (as long there is no error).
* Labels are skipped, if FormElement.bsLabelColumns=0.
* Respect attribute `data-class-on-change` on save buttons.
Version 0.11
------------
Features
^^^^^^^^
* Added STORE_BEFORE, #3146 - Mainly used to compare old and new values during a form 'save' action.
* Added 'best practice' for defining and using of 'Central configure values' in UserManual.
* Added accent characters to sanatize class 'alnumx', #3183.
* Set default all QFQ send mails to 'auto-submit'.
* Added possibility to customize error messages ('data-pattern-error', 'data-rquired-error', 'data-match-error',
'data-error') if validation fails. Customization can be done on global level (config.qfq.ini), per Form or per FormElement.
* *FormElement*: Double an input element and validate that the input match: FormElement.parameter.retype=1
* Autofocus in Forms is now supported. By default the first Input Element receives the focus. Can be customized.
* Added a timestamp in shown exceptions. Usefull for screenshots, send by customer, to find the problem in SQL logfiles.
Bug fixes
^^^^^^^^^
* Fixed missing docutmentation for FormElement 'note'.
* Failed SQL queries will now always be logged, even if they do not modify some data.
Version 0.10
------------
Features
^^^^^^^^
* Implemented Parameter 'extraDeleteForm' for 'forms' and 'subrecords'. Update doc.
Bug fixes
^^^^^^^^^
* Suppress rendering of form title during a 'delete' call. No one will see it and required parameters are not supplied.
* In case of broken SQL queries, print them in ajax error message.
* Remove parameter 'table' from Delete SIP URLs. ToolTip updated.
Version 0.9
-----------
Features
^^^^^^^^
* FormEditor:
* design update - new default background color: grey.
* per form configureable background colors.
* Optional right align of all form element labels.
* Added config.qfq.ini values CSS_CLASS_QFQ_FORM_PILL, CSS_CLASS_QFQ_FORM_BODY, CSS_CLASS_QFQ_CONTAINER.
Bug fixes
^^^^^^^^^
* BuildFormBootstrap.php: added new class name 'qfq-label' to form labels - needed to assign 'qfq-form-right' class. Changed wrapping of formelements from 'col-md-8' (wrong) to 'col-md-12'.
* QuickFormQuery.php: Set default for new F_CLASS_PILL & F_CLASS_BODY.
* formEditor.sql: New default background color for formElements is blue.
* qfq-bs.css.less: add classes qfq-form-pill, qfq-form-body, form-group (center), qfq-color-..., qfq-form-right.
* Index.rst: Add note to hierachy chars. Fixed uncomplete doc to a) bs*Columns, showButton. Add classPill, classBody. Rewrote form.paramter.class.
* QuickFormQuery.php: Button save/ close/ delete/ new - align to right border of form.
* UsersManual/index.rst: renamed chapter for formelements. Cleanup formelement types. Wrote chapter 'Detailed concept'.
* QuickFormQuery.php, FormAction.php: '#2931 / afterSave Hauptrecord xId nicht direkt verfügbar' - load master record again, after 'action'-elements has been processed.
* UsersManual/index.rst: Startet FAQ section.
* config.qfq.example.ini: Added comment where to save config.qfq.ini.
* UsersManual/index.rst: Rewrite of 'action'-FormElement definition.
* #2739: beforeDelete / afterDelete.
* PROTOCOL.md: update 'delete' description.
* delete.php: fixed unwanted loose of MSG_CONTENT.
* Report.php: Fixed double '&&' in building UrlParam.
* FormAction.php: In case of 'AFTER_DELETE', do not try to load primary record - that one is already deleted.
* Sip.php: Do not skip SIP_TARGET_URL as parameter for the SIP.
* #3001 Report: delete implementieren.
* Index.rst, Constants.php: reverted parameter '_table' in delete links back to 'table' - Reason: 'form' needs to be 'form' (instead of '_form') due to many used places already.
* Sip.php: move SIP_TARGET_URL back to stored inside SIP - it's necessary for 'delete'-links.
* Report.php, Constants.php: Remove code to handle unecessary 'p:' tag for delete links.
* Link.php: Check paged / Paged that the parameter r, table and form are given in the right combination.
* Link.php, Report.php: New '_link' token 'x'. '_paged' and '_Paged' are rendered via Link() class, Link() class now supports delete links.
* QuickFormQuery.php: for modeForm='Form Delete' the 'required param' are not respected - this makes sense, cause these parameters typically filled in newly created records.
* Fixed: #3076 Delete Button bei Subrecords erzeugt sporadisch Javascript Exceptions (Webkit: Chrome / Vivaldi) - kein loeschen moeglich.
...@@ -22,6 +22,7 @@ require_once(__DIR__ . '/../qfq/Database.php'); ...@@ -22,6 +22,7 @@ require_once(__DIR__ . '/../qfq/Database.php');
require_once(__DIR__ . '/../qfq/helper/HelperFormElement.php'); require_once(__DIR__ . '/../qfq/helper/HelperFormElement.php');
require_once(__DIR__ . '/../qfq/helper/Support.php'); require_once(__DIR__ . '/../qfq/helper/Support.php');
require_once(__DIR__ . '/../qfq/helper/OnArray.php'); require_once(__DIR__ . '/../qfq/helper/OnArray.php');
require_once(__DIR__ . '/../qfq/helper/Ldap.php');
require_once(__DIR__ . '/../qfq/report/Link.php'); require_once(__DIR__ . '/../qfq/report/Link.php');
/** /**
...@@ -375,16 +376,15 @@ abstract class AbstractBuildForm { ...@@ -375,16 +376,15 @@ abstract class AbstractBuildForm {
continue; // skip this FE continue; // skip this FE
} }
$flagOutput = ($fe[FE_TYPE] !== FE_TYPE_EXTRA); // type='extra' will not displayed not trasnmitted to the form $flagOutput = ($fe[FE_TYPE] !== FE_TYPE_EXTRA); // type='extra' will not displayed and not transmitted to the form
$debugStack = array(); $debugStack = array();
// Preparation for Log, Debug // Preparation for Log, Debug
$this->store->setVar(SYSTEM_FORM_ELEMENT, Logger::formatFormElementName($fe), STORE_SYSTEM); $this->store->setVar(SYSTEM_FORM_ELEMENT, Logger::formatFormElementName($fe), STORE_SYSTEM);
if (isset($fe[FE_FILL_STORE_LDAP]) || isset($fe[FE_TYPEAHEAD_LDAP])) { // Fill STORE_LDAP
$fe = $this->prepareLdapConfig($fe); $fe = $this->prepareFillStoreFireLdap($fe);
}
// for Upload FormElements, it's necessary to precalculate an optional given 'slaveId'. // for Upload FormElements, it's necessary to precalculate an optional given 'slaveId'.
if ($fe[FE_TYPE] === FE_TYPE_UPLOAD) { if ($fe[FE_TYPE] === FE_TYPE_UPLOAD) {
...@@ -393,7 +393,7 @@ abstract class AbstractBuildForm { ...@@ -393,7 +393,7 @@ abstract class AbstractBuildForm {
$this->store->setVar(VAR_SLAVE_ID, $slaveId, STORE_VAR); $this->store->setVar(VAR_SLAVE_ID, $slaveId, STORE_VAR);
} }
// evaluate current FormElement // ** evaluate current FormElement **
$formElement = $this->evaluate->parseArray($fe, $skip, $debugStack); $formElement = $this->evaluate->parseArray($fe, $skip, $debugStack);
// Some Defaults // Some Defaults
...@@ -466,14 +466,52 @@ abstract class AbstractBuildForm { ...@@ -466,14 +466,52 @@ abstract class AbstractBuildForm {
} }
/** /**
* Copy LDAP Server from $this->formSpec to $formElement if they are not specified on $formElement * Checks if LDAP search is requested.
* Yes: prepare configuration and fire the query.
* No: do nothing.
*
* @param array $formElement
* @return array
* @throws CodeException
* @throws UserFormException
*/
private function prepareFillStoreFireLdap(array $formElement) {
$config = array();
if (isset($formElement[FE_FILL_STORE_LDAP]) || isset($formElement[FE_TYPEAHEAD_LDAP])) {
$formElement = $this->copyLdapConfigForm2FormElement($formElement);
} else {
return $formElement; // nothing to do.
}
if (isset($formElement[FE_FILL_STORE_LDAP])) {
// Extract necessary elements
foreach ([FE_LDAP_SERVER, FE_LDAP_BASE_DN, FE_LDAP_SEARCH, FE_LDAP_ATTRIBUTES] as $key) {
$config[$key] = $formElement[$key];
}
$config = $this->evaluate->parseArray($config);
$ldap = new Ldap();
$arr = $ldap->process($config, '', MODE_LDAP_SINGLE);
$this->store->setStore($arr, STORE_LDAP, true);
}
return $formElement;
}
/**
* Copy LDAP Config from $this->formSpec to $formElement. Do not copy if a parameter already exist on $formElement.
*
* @param array $formElement * @param array $formElement
* @return array * @return array
*/ */
private function prepareLdapConfig(array $formElement) { private function copyLdapConfigForm2FormElement(array $formElement) {
foreach ([F_LDAP_SERVER, F_LDAP_BASE_DN, F_LDAP_ATTRIBUTES, F_LDAP_SEARCH, F_TYPEAHEAD_LDAP_SEARCH, F_TYPEAHEAD_LIMIT,
F_TYPEAHEAD_MINLENGTH, F_TYPEAHEAD_LDAP_VALUE_PRINTF, F_TYPEAHEAD_LDAP_KEY_PRINTF, F_LDAP_TIME_LIMIT] as $key) {
foreach ([F_LDAP_SERVER, F_LDAP_BASE_DN, F_LDAP_SEARCH, F_TYPEAHEAD_LIMIT,
F_TYPEAHEAD_MINLENGTH, F_TYPEAHEAD_LDAP_VALUE_PRINTF, F_TYPEAHEAD_LDAP_KEY_PRINTF] as $key) {
if (!isset($formElement[$key])) { if (!isset($formElement[$key])) {
if (isset($this->formSpec[$key])) { if (isset($this->formSpec[$key])) {
$formElement[$key] = $this->formSpec[$key]; $formElement[$key] = $this->formSpec[$key];
...@@ -483,6 +521,7 @@ abstract class AbstractBuildForm { ...@@ -483,6 +521,7 @@ abstract class AbstractBuildForm {
return $formElement; return $formElement;
} }
/** /**
* Check if there is an explicit 'autofocus' definition in at least one FE. * Check if there is an explicit 'autofocus' definition in at least one FE.
* Found: do nothing, it will be rendered at the correct position. * Found: do nothing, it will be rendered at the correct position.
...@@ -830,11 +869,12 @@ abstract class AbstractBuildForm { ...@@ -830,11 +869,12 @@ abstract class AbstractBuildForm {
/** /**
* Check $formElement for FE_TYPE_AHEAD_SQL or FE_TYPE_AHEAD_LDAP_SERVER. * Check $formElement for FE_TYPE_AHEAD_SQL or FE_TYPE_AHEAD_LDAP_SERVER.
* If one of them is given: fill $urlParam. * If one of them is given: build $urlParam with typeAhead Params.
* Set some parameter for later outside use, especially FE_TYPEAHEAD_LIMIT, FE_TYPEAHEAD_MINLENGTH * Additionally set some parameter for later outside use, especially FE_TYPEAHEAD_LIMIT, FE_TYPEAHEAD_MINLENGTH
* *
* @param array $formElement * @param array $formElement
* @return string * @return string
* @throws UserFormException
*/ */
private function typeAheadBuildParam(array &$formElement) { private function typeAheadBuildParam(array &$formElement) {
...@@ -846,23 +886,33 @@ abstract class AbstractBuildForm { ...@@ -846,23 +886,33 @@ abstract class AbstractBuildForm {
if (isset($formElement[FE_TYPEAHEAD_SQL])) { if (isset($formElement[FE_TYPEAHEAD_SQL])) {
$sql = $this->checkSqlAppendLimit($formElement[FE_TYPEAHEAD_SQL], $formElement[FE_TYPEAHEAD_LIMIT]); $sql = $this->checkSqlAppendLimit($formElement[FE_TYPEAHEAD_SQL], $formElement[FE_TYPEAHEAD_LIMIT]);
$urlParam = FE_TYPEAHEAD_SQL . '=' . $sql; $urlParam = FE_TYPEAHEAD_SQL . '=' . $sql;
} elseif (isset($formElement[FE_LDAP_SERVER])) { } elseif (isset($formElement[FE_TYPEAHEAD_LDAP])) {
$formElement[FE_LDAP_SERVER] = Support::setIfNotSet($formElement, FE_LDAP_SERVER); $formElement[FE_LDAP_SERVER] = Support::setIfNotSet($formElement, FE_LDAP_SERVER);
$formElement[FE_LDAP_BASE_DN] = Support::setIfNotSet($formElement, FE_LDAP_BASE_DN); $formElement[FE_LDAP_BASE_DN] = Support::setIfNotSet($formElement, FE_LDAP_BASE_DN);
$formElement[FE_LDAP_SEARCH] = Support::setIfNotSet($formElement, FE_LDAP_SEARCH); $formElement[FE_TYPEAHEAD_LDAP_SEARCH] = Support::setIfNotSet($formElement, FE_TYPEAHEAD_LDAP_SEARCH);
$formElement[FE_TYPEAHEAD_LDAP_VALUE_PRINTF] = Support::setIfNotSet($formElement, FE_TYPEAHEAD_LDAP_VALUE_PRINTF); $formElement[FE_TYPEAHEAD_LDAP_VALUE_PRINTF] = Support::setIfNotSet($formElement, FE_TYPEAHEAD_LDAP_VALUE_PRINTF);
$formElement[FE_TYPEAHEAD_LDAP_KEY_PRINTF] = Support::setIfNotSet($formElement, FE_TYPEAHEAD_LDAP_KEY_PRINTF); $formElement[FE_TYPEAHEAD_LDAP_KEY_PRINTF] = Support::setIfNotSet($formElement, FE_TYPEAHEAD_LDAP_KEY_PRINTF);
foreach ([FE_LDAP_SERVER, FE_LDAP_BASE_DN, FE_TYPEAHEAD_LDAP_SEARCH] as $key) {
if ($formElement[$key] == '') {
throw new UserFormException('Missing definition: ' . $key, ERROR_MISSING_DEFINITON);
}
}
if ($formElement[FE_TYPEAHEAD_LDAP_VALUE_PRINTF] . $formElement[FE_TYPEAHEAD_LDAP_KEY_PRINTF] == '') {
throw new UserFormException('Missing definition: ' . FE_TYPEAHEAD_LDAP_VALUE_PRINTF . ' or ' . FE_TYPEAHEAD_LDAP_KEY_PRINTF, ERROR_MISSING_DEFINITON);
}
$arr = [ $arr = [
FE_LDAP_SERVER => $formElement[FE_LDAP_SERVER], FE_LDAP_SERVER => $formElement[FE_LDAP_SERVER],
FE_LDAP_BASE_DN => $formElement[FE_LDAP_BASE_DN], FE_LDAP_BASE_DN => $formElement[FE_LDAP_BASE_DN],
FE_LDAP_SEARCH => $formElement[FE_LDAP_SEARCH], FE_TYPEAHEAD_LDAP_SEARCH => $formElement[FE_TYPEAHEAD_LDAP_SEARCH],
FE_TYPEAHEAD_LDAP_VALUE_PRINTF => $formElement[FE_TYPEAHEAD_LDAP_VALUE_PRINTF], FE_TYPEAHEAD_LDAP_VALUE_PRINTF => $formElement[FE_TYPEAHEAD_LDAP_VALUE_PRINTF],
FE_TYPEAHEAD_LDAP_KEY_PRINTF => $formElement[FE_TYPEAHEAD_LDAP_KEY_PRINTF], FE_TYPEAHEAD_LDAP_KEY_PRINTF => $formElement[FE_TYPEAHEAD_LDAP_KEY_PRINTF],
FE_TYPEAHEAD_LIMIT => $formElement[FE_TYPEAHEAD_LIMIT], FE_TYPEAHEAD_LIMIT => $formElement[FE_TYPEAHEAD_LIMIT],
]; ];
$urlParam = OnArray::toString($arr); $urlParam = OnArray::toString($arr);
} }
return $urlParam; return $urlParam;
......
...@@ -16,23 +16,57 @@ require_once(__DIR__ . '/OnArray.php'); ...@@ -16,23 +16,57 @@ require_once(__DIR__ . '/OnArray.php');
class Ldap { class Ldap {
/** /**
* @param $query * @param $ldapServer
* @return array * @return resource
* @throws UserFormException * @throws UserFormException
*/ */
public function process($config, $query) { private function ldapConnect($ldapServer) {
$arr = array();
$ldapServer = $config[FE_LDAP_SERVER];
$ldapBaseDn = $config[FE_LDAP_BASE_DN];
$ldapSearch = $config[FE_LDAP_SEARCH];
$ldapSearch = str_replace('?', $query, $ldapSearch);
$ldapLimit = $config[FE_TYPEAHEAD_LIMIT];
$ds = ldap_connect($ldapServer); // must be a valid LDAP server! $ds = ldap_connect($ldapServer); // must be a valid LDAP server!
if (!$ds) { if (!$ds) {
throw new UserFormException("Unable to connect to LDAP server: $ldapServer", ERROR_LDAP_CONNECT); throw new UserFormException("Unable to connect to LDAP server: $ldapServer", ERROR_LDAP_CONNECT);
} }
return $ds;
}
/**
* @param $ds
* @param array $config
* @param array $attr
* @return resource
*/
private function ldapSearch($ds, array $config, array $attr) {
// 'Size Limit errors' are reported, even if it is not a real problem.
// Fake all errors at the moment.
// TODO: just drop the 'Size Limit errors' and report all others
set_error_handler(function () { /* ignore errors */
});
$sr = ldap_search($ds, $config[FE_LDAP_BASE_DN], $config[FE_LDAP_SEARCH], $attr, 0, $config[FE_TYPEAHEAD_LIMIT] + 1, $config[FE_LDAP_TIME_LIMIT]);
restore_error_handler();
return $sr;
}
/**
* @param array $config
* @param string $searchValue
* @param string $mode
* @return array
* @throws UserFormException
*/
private function prepareConfig(array $config, $searchValue, $mode) {
$config[FE_LDAP_ATTRIBUTES] = Support::setIfNotSet($config, FE_LDAP_ATTRIBUTES, '');
$config[FE_LDAP_TIME_LIMIT] = Support::setIfNotSet($config, FE_LDAP_TIME_LIMIT, DEFAULT_LDAP_TIME_LIMIT);
$config[FE_LDAP_SEARCH] = str_replace('?', $searchValue, $config[FE_LDAP_SEARCH]);
$config[FE_TYPEAHEAD_LIMIT] = ($mode == MODE_LDAP_MULTI) ? $config[FE_TYPEAHEAD_LIMIT] : 1;
if ($mode == MODE_LDAP_MULTI) {
$config[FE_TYPEAHEAD_LDAP_KEY_PRINTF] = Support::setIfNotSet($config, FE_TYPEAHEAD_LDAP_KEY_PRINTF, '');
$config[FE_TYPEAHEAD_LDAP_VALUE_PRINTF] = Support::setIfNotSet($config, FE_TYPEAHEAD_LDAP_VALUE_PRINTF, '');
if ($config[FE_TYPEAHEAD_LDAP_KEY_PRINTF] == '') { if ($config[FE_TYPEAHEAD_LDAP_KEY_PRINTF] == '') {
$config[FE_TYPEAHEAD_LDAP_KEY_PRINTF] = $config[FE_TYPEAHEAD_LDAP_VALUE_PRINTF]; $config[FE_TYPEAHEAD_LDAP_KEY_PRINTF] = $config[FE_TYPEAHEAD_LDAP_VALUE_PRINTF];
...@@ -42,25 +76,50 @@ class Ldap { ...@@ -42,25 +76,50 @@ class Ldap {
$config[FE_TYPEAHEAD_LDAP_VALUE_PRINTF] = $config[FE_TYPEAHEAD_LDAP_KEY_PRINTF]; $config[FE_TYPEAHEAD_LDAP_VALUE_PRINTF] = $config[FE_TYPEAHEAD_LDAP_KEY_PRINTF];
} }
if ($config[FE_TYPEAHEAD_LDAP_KEY_PRINTF] == '') { if ($mode == MODE_LDAP_MULTI && $config[FE_TYPEAHEAD_LDAP_KEY_PRINTF] == '') {
throw new UserFormException("Missing parameter '" . FE_TYPEAHEAD_LDAP_KEY_PRINTF . "' and/or '" . FE_TYPEAHEAD_LDAP_VALUE_PRINTF); throw new UserFormException("Missing parameter '" . FE_TYPEAHEAD_LDAP_KEY_PRINTF . "' and/or '" . FE_TYPEAHEAD_LDAP_VALUE_PRINTF);
} }
}
$keyArr = $this->printfPrepare($config[FE_TYPEAHEAD_LDAP_KEY_PRINTF], $keyFormat); return $config;
$valueArr = $this->printfPrepare($config[FE_TYPEAHEAD_LDAP_VALUE_PRINTF], $valueFormat); }
$attr = array_values(array_unique(array_merge($keyArr, $valueArr))); /**
* @param array $config [FE_LDAP_SERVER , FE_LDAP_BASE_DN, FE_LDAP_SEARCH, FE_TYPEAHEAD_LIMIT, FE_TYPEAHEAD_LDAP_KEY_PRINTF, FE_TYPEAHEAD_LDAP_VALUE_PRINTF]
* @param string $searchValue value to search via $config[FE_LDAP_SEARCH]
* @param string $mode MODE_LDAP_SINGLE | MODE_LDAP_MULTI
* @return array Array: [ [ 'key' => '...', 'value' => '...' ], ]
* @throws UserFormException
*/
public function process(array $config, $searchValue, $mode = MODE_LDAP_MULTI) {
$arr = array();
// 'Size Limit errors' are reported, even if it is not a real problem. // For TypeAhead, use an optional given F_TYPEAHEAD_LDAP_SEARCH
// Fake all errors at the moment. if ($mode == MODE_LDAP_MULTI && $config[F_TYPEAHEAD_LDAP_SEARCH] != '') {
// TODO: just drop the 'Size Limit errors' and report all others $config[F_LDAP_SEARCH] = $config[F_TYPEAHEAD_LDAP_SEARCH];
set_error_handler(function () { /* ignore errors */ }
});
$sr = ldap_search($ds, $ldapBaseDn, $ldapSearch, $attr, 0, $ldapLimit);
restore_error_handler();
$searchValue = Support::ldap_escape($searchValue, null, LDAP_ESCAPE_FILTER);
$config = $this->prepareConfig($config, $searchValue, $mode);
$ds = $this->ldapConnect($config[FE_LDAP_SERVER]); // must be a valid LDAP server!
$keyArr = $this->preparePrintf($config, FE_TYPEAHEAD_LDAP_KEY_PRINTF, $keyFormat);
$valueArr = $this->preparePrintf($config, FE_TYPEAHEAD_LDAP_VALUE_PRINTF, $valueFormat);
$specificArr = OnArray::arrayValueToLower(OnArray::trimArray(explode(',', $config[FE_LDAP_ATTRIBUTES])));
// merge, trim, toLower, unique, values
$attr = array_values(
array_unique(
OnArray::removeEmptyElementsFromArray(
array_merge($keyArr, $valueArr, $specificArr))));
$sr = $this->ldapSearch($ds, $config, $attr);
$info = ldap_get_entries($ds, $sr); $info = ldap_get_entries($ds, $sr);
if ($mode == MODE_LDAP_MULTI) {
// Iterate over all Elements, per element collect all needed attributes
for ($i = 0; $i < $info["count"]; $i++) { for ($i = 0; $i < $info["count"]; $i++) {
$key = $this->printfResult($keyFormat, $keyArr, $info[$i]); $key = $this->printfResult($keyFormat, $keyArr, $info[$i]);
...@@ -72,7 +131,13 @@ class Ldap { ...@@ -72,7 +131,13 @@ class Ldap {
$arr[] = [API_TYPEAHEAD_KEY => $key, API_TYPEAHEAD_VALUE => $value];