From 1cccab7b660044c3960719dba50b99fb12379a50 Mon Sep 17 00:00:00 2001 From: Roland Gruber Date: Tue, 15 Oct 2013 16:12:39 +0000 Subject: [PATCH] added PyKota support --- lam/HISTORY | 1 + .../pdf/default.pykotaBillingCodeType.xml | 7 + .../pdf/default.pykotaPrinterType.xml | 11 + .../profiles/default.pykotaBillingCodeType | 3 + .../profiles/default.pykotaPrinterType | 3 + lam/graphics/pykotaBillingCodeType.png | Bin 0 -> 662 bytes lam/graphics/pykotaPrinterType.png | Bin 0 -> 662 bytes lam/lib/modules/pykotaBillingCode.inc | 300 ++++ lam/lib/modules/pykotaGroup.inc | 592 ++++++++ lam/lib/modules/pykotaGroupStructural.inc | 69 + lam/lib/modules/pykotaPrinter.inc | 689 ++++++++++ lam/lib/modules/pykotaUser.inc | 1213 +++++++++++++++++ lam/lib/modules/pykotaUserStructural.inc | 70 + lam/lib/types/pykotaBillingCodeType.inc | 172 +++ lam/lib/types/pykotaPrinterType.inc | 173 +++ 15 files changed, 3303 insertions(+) create mode 100644 lam/config/templates/pdf/default.pykotaBillingCodeType.xml create mode 100644 lam/config/templates/pdf/default.pykotaPrinterType.xml create mode 100644 lam/config/templates/profiles/default.pykotaBillingCodeType create mode 100644 lam/config/templates/profiles/default.pykotaPrinterType create mode 100644 lam/graphics/pykotaBillingCodeType.png create mode 100644 lam/graphics/pykotaPrinterType.png create mode 100644 lam/lib/modules/pykotaBillingCode.inc create mode 100644 lam/lib/modules/pykotaGroup.inc create mode 100644 lam/lib/modules/pykotaGroupStructural.inc create mode 100644 lam/lib/modules/pykotaPrinter.inc create mode 100644 lam/lib/modules/pykotaUser.inc create mode 100644 lam/lib/modules/pykotaUserStructural.inc create mode 100644 lam/lib/types/pykotaBillingCodeType.inc create mode 100644 lam/lib/types/pykotaPrinterType.inc diff --git a/lam/HISTORY b/lam/HISTORY index 482d5a4c..be11a74e 100644 --- a/lam/HISTORY +++ b/lam/HISTORY @@ -1,4 +1,5 @@ December 2013 4.4 + - PyKota support: users, groups, printers, billing codes - allow to set a custom label for each account type - LAM Pro: -> Samba/Shadow: display password change date in self service diff --git a/lam/config/templates/pdf/default.pykotaBillingCodeType.xml b/lam/config/templates/pdf/default.pykotaBillingCodeType.xml new file mode 100644 index 00000000..3ef5c350 --- /dev/null +++ b/lam/config/templates/pdf/default.pykotaBillingCodeType.xml @@ -0,0 +1,7 @@ + +
+ + + +
+
\ No newline at end of file diff --git a/lam/config/templates/pdf/default.pykotaPrinterType.xml b/lam/config/templates/pdf/default.pykotaPrinterType.xml new file mode 100644 index 00000000..cc8d89a3 --- /dev/null +++ b/lam/config/templates/pdf/default.pykotaPrinterType.xml @@ -0,0 +1,11 @@ + +
+ + + + + + + +
+
\ No newline at end of file diff --git a/lam/config/templates/profiles/default.pykotaBillingCodeType b/lam/config/templates/profiles/default.pykotaBillingCodeType new file mode 100644 index 00000000..39b62e62 --- /dev/null +++ b/lam/config/templates/profiles/default.pykotaBillingCodeType @@ -0,0 +1,3 @@ +profname: default +ldap_suffix: - +ldap_rdn: cn diff --git a/lam/config/templates/profiles/default.pykotaPrinterType b/lam/config/templates/profiles/default.pykotaPrinterType new file mode 100644 index 00000000..39b62e62 --- /dev/null +++ b/lam/config/templates/profiles/default.pykotaPrinterType @@ -0,0 +1,3 @@ +profname: default +ldap_suffix: - +ldap_rdn: cn diff --git a/lam/graphics/pykotaBillingCodeType.png b/lam/graphics/pykotaBillingCodeType.png new file mode 100644 index 0000000000000000000000000000000000000000..cc17b26b6184489e6088d952fb2d2c85aa4a47ff GIT binary patch literal 662 zcmV;H0%`q;P)55P0Rm|dyRo_;fiAk% z;6fG{C6S0i0v4J#GjHa)z&5Qm@gygiJ2U4b_uNa&j4qNSdAPW^IAd)F03hnVTpn_{ z0Q>u&KQx;=PkJI|?y*{(f5*(6WsaTmJ$ldVJu@@&+S>ZJeocTrIG1GrptZ&@M9~Pe zPF??<=r5#{g#TjjivWPyMxEX?8l#m;rJ}X@S3(3Lf-x3_Lg8wo(I^fWIOnd{YPGvU z$UG522mv7kh%Py31o3ipb-A`tF9QH6rJ|?Lp1-eFZaDVyUjX+8{Jv%#Fbk0Lci71K?st{?aVU32%98{=~&*!02jdr^Ytqtbp z<_bhK6U1>Wl~Vam=K{w+f8gxw6joc<%py%wq-hFkEIOSI+UIQ;V=y%}g;Hr^B#vVl z5Yd=1Mj37Rp%;k`eVtD6qcz6iMEVC?|2#hh~we>YGq=fShrO6Wd*Lz^*L^!{= zK;vx#xgf~A_mi};vNAeaEIzQ-&KKgst#bLA?56%Vq?Bm2S{O@`mQw1~+3D#EFtf7O zCa>#t+S=N3&N55P0Rm|dyRo_;fiAk% z;6fG{C6S0i0v4J#GjHa)z&5Qm@gygiJ2U4b_uNa&j4qNSdAPW^IAd)F03hnVTpn_{ z0Q>u&KQx;=PkJI|?y*{(f5*(6WsaTmJ$ldVJu@@&+S>ZJeocTrIG1GrptZ&@M9~Pe zPF??<=r5#{g#TjjivWPyMxEX?8l#m;rJ}X@S3(3Lf-x3_Lg8wo(I^fWIOnd{YPGvU z$UG522mv7kh%Py31o3ipb-A`tF9QH6rJ|?Lp1-eFZaDVyUjX+8{Jv%#Fbk0Lci71K?st{?aVU32%98{=~&*!02jdr^Ytqtbp z<_bhK6U1>Wl~Vam=K{w+f8gxw6joc<%py%wq-hFkEIOSI+UIQ;V=y%}g;Hr^B#vVl z5Yd=1Mj37Rp%;k`eVtD6qcz6iMEVC?|2#hh~we>YGq=fShrO6Wd*Lz^*L^!{= zK;vx#xgf~A_mi};vNAeaEIzQ-&KKgst#bLA?56%Vq?Bm2S{O@`mQw1~+3D#EFtf7O zCa>#t+S=N3&N pykotaBillingCode1, dn2 => pykotaBillingCode2)) */ + private $codeCache = null; + + /** + * Returns meta data that is interpreted by parent class + * + * @return array array with meta data + * + * @see baseModule::get_metaData() + */ + function get_metaData() { + $return = array(); + // icon + $return['icon'] = 'printerBig.png'; + // manages host accounts + $return["account_types"] = array('pykotaBillingCodeType'); + // alias name + $return["alias"] = _("PyKota"); + // this is a base module + $return["is_base"] = true; + // RDN attribute + $return["RDN"] = array("cn" => "high"); + // LDAP filter + $return["ldap_filter"] = array('or' => "(objectClass=pykotaBilling)"); + // module dependencies + $return['dependencies'] = array('depends' => array(), 'conflicts' => array()); + // managed object classes + $return['objectClasses'] = array('pykotaObject', 'pykotaBilling'); + // managed attributes + $return['attributes'] = array('cn', 'pykotaBillingCode', 'description', 'pykotaBalance', 'pykotaPageCounter'); + // help Entries + $return['help'] = array( + 'pykotaBillingCode' => array( + "Headline" => _("Billing code"), 'attr' => 'pykotaBillingCode', + "Text" => _("Billing code name which should be created. Valid characters are: a-z, A-Z, 0-9 and .-_ .") + ), + 'description' => array ( + "Headline" => _("Description"), 'attr' => 'description', + "Text" => _("Billing code description.") + ), + 'pykotaBalance' => array ( + "Headline" => _('Balance'), 'attr' => 'pykotaBalance', + "Text" => _('Used balance for the billing code.') + ), + 'pykotaPageCounter' => array ( + "Headline" => _('Page count'), 'attr' => 'pykotaPageCounter', + "Text" => _('Number of pages printed with this billing code.') + ), + 'reset' => array ( + "Headline" => _('Reset'), 'attr' => 'pykotaBalance, pykotaPageCounter', + "Text" => _('Resets the billing code\'s balance and page counter to 0.') + ), + ); + // upload fields + $return['upload_columns'] = array( + array( + 'name' => 'pykotaBillingCode_pykotaBillingCode', + 'description' => _('Printer name'), + 'help' => 'cn', + 'example' => _('billingCode01'), + 'required' => true, + 'unique' => true, + ), + array( + 'name' => 'pykotaBillingCode_description', + 'description' => _('Description'), + 'help' => 'description', + ), + ); + // available PDF fields + $return['PDF_fields'] = array( + 'pykotaBillingCode' => _('Billing code'), + 'description' => _('Description'), + 'pykotaBalance' => _('Balance'), + 'pykotaPageCounter' => _('Page count'), + ); + return $return; + } + + /** + * This function fills the $messages variable with output messages from this module. + */ + function load_Messages() { + $this->messages['pykotaBillingCode'][0] = array('ERROR', _('Billing code'), _('Billing code contains invalid characters. Valid characters are: a-z, A-Z, 0-9 and .-_ !')); + $this->messages['pykotaBillingCode'][1] = array('ERROR', _('Account %s:') . ' pykotaBillingCode_cn', _('Billing code contains invalid characters. Valid characters are: a-z, A-Z, 0-9 and .-_ !')); + $this->messages['pykotaBillingCode'][2] = array('ERROR', _('Billing code'), _('Billing code already exists!')); + $this->messages['pykotaBillingCode'][3] = array('ERROR', _('Account %s:') . ' pykotaBillingCode_cn', _('Billing code already exists!')); + } + + /** + * Returns the HTML meta data for the main account page. + * + * @return htmlElement HTML meta data + */ + function display_html_attributes() { + $container = new htmlTable(); + // pykotaBillingCode + $this->addSimpleInputTextField($container, 'pykotaBillingCode', _('BillingCode'), true); + // balance + $container->addElement(new htmlOutputText(_('Balance'))); + $pykotaBalance = ''; + if (isset($this->attributes['pykotaBalance'][0])) { + $pykotaBalance = $this->attributes['pykotaBalance'][0]; + } + $container->addElement(new htmlOutputText($pykotaBalance)); + $container->addElement(new htmlHelpLink('pykotaBalance'), true); + // page count + $container->addElement(new htmlOutputText(_('Page count'))); + $pykotaPageCounter = ''; + if (isset($this->attributes['pykotaPageCounter'][0])) { + $pykotaPageCounter = $this->attributes['pykotaPageCounter'][0]; + } + $container->addElement(new htmlOutputText($pykotaPageCounter)); + $container->addElement(new htmlHelpLink('pykotaPageCounter'), true); + // description + $this->addSimpleInputTextField($container, 'description', _('Description'), false, null, true); + // reset + $container->addElement(new htmlSpacer(null, '20px'), true); + $container->addElement(new htmlOutputText('')); + $container->addElement(new htmlButton('resetCounters', _('Reset'))); + $container->addElement(new htmlHelpLink('reset'), true); + // same width + $container->addElement(new htmlEqualWidth(array('pykotaBillingCode', 'description'))); + return $container; + } + + /** + * Processes user input of the primary module page. + * It checks if all input values are correct and updates the associated LDAP attributes. + * + * @return array list of info/error messages + */ + function process_attributes() { + $errors = array(); + // pykotaBillingCode + if (isset($_POST['pykotaBillingCode']) && ($_POST['pykotaBillingCode'] != '')) { + if (!get_preg($_POST['pykotaBillingCode'], 'username')) { + $errors[] = $this->messages['pykotaBillingCode'][0]; + } + else { + $this->attributes['pykotaBillingCode'][0] = $_POST['pykotaBillingCode']; + $this->attributes['cn'][0] = $_POST['pykotaBillingCode']; + if ((!isset($this->orig['pykotaBillingCode'][0]) || ($this->attributes['pykotaBillingCode'][0] != $this->orig['pykotaBillingCode'][0])) + && $this->codeExists($_POST['pykotaBillingCode'])) { + $errors[] = $this->messages['pykotaBillingCode'][2]; + } + } + } + else { + if (isset($this->attributes['cn'][0])) { + unset($this->attributes['cn'][0]); + } + if (isset($this->attributes['pykotaBillingCode'][0])) { + unset($this->attributes['pykotaBillingCode'][0]); + } + } + // description + $this->attributes['description'][0] = $_POST['description']; + // reset + if (isset($_POST['resetCounters'])) { + $this->attributes['pykotaBalance'][0] = '0.0'; + $this->attributes['pykotaPageCounter'][0] = '0'; + } + return $errors; + } + + /** + * In this function the LDAP account is built up. + * + * @param array $rawAccounts list of hash arrays (name => value) from user input + * @param array $ids list of IDs for column position (e.g. "posixAccount_uid" => 5) + * @param array $partialAccounts list of hash arrays (name => value) which are later added to LDAP + * @param array $selectedModules list of selected account modules + * @return array list of error messages if any + */ + function build_uploadAccounts($rawAccounts, $ids, &$partialAccounts, $selectedModules) { + $messages = array(); + $this->loadCodeCache(); + for ($i = 0; $i < sizeof($rawAccounts); $i++) { + // add object classes + if (!in_array('pykotaBilling', $partialAccounts[$i]['objectClass'])) { + $partialAccounts[$i]['objectClass'][] = 'pykotaBilling'; + } + if (!in_array('pykotaObject', $partialAccounts[$i]['objectClass'])) { + $partialAccounts[$i]['objectClass'][] = 'pykotaObject'; + } + // pykotaBillingCode + if (!get_preg($rawAccounts[$i][$ids['pykotaBillingCode_pykotaBillingCode']], 'username')) { + $errMsg = $this->messages['pykotaBillingCode'][1]; + array_push($errMsg, array($i)); + $messages[] = $errMsg; + } + elseif ($this->codeExists($rawAccounts[$i][$ids['pykotaBillingCode_pykotaBillingCode']])) { + $errMsg = $this->messages['pykotaBillingCode'][3]; + array_push($errMsg, array($i)); + $messages[] = $errMsg; + } + else { + $partialAccounts[$i]['cn'] = $rawAccounts[$i][$ids['pykotaBillingCode_pykotaBillingCode']]; + $partialAccounts[$i]['pykotaBillingCode'] = $rawAccounts[$i][$ids['pykotaBillingCode_pykotaBillingCode']]; + } + // description + if (!empty($rawAccounts[$i][$ids['pykotaBillingCode_description']])) { + $partialAccounts[$i]['description'] = $rawAccounts[$i][$ids['pykotaBillingCode_description']]; + } + // balance + $partialAccounts[$i]['pykotaBalance'] = '0.0'; + // page count + $partialAccounts[$i]['pykotaPageCounter'] = '0'; + } + return $messages; + } + + /** + * Returns a list of PDF entries + */ + function get_pdfEntries() { + $return = array(); + $this->loadCodeCache(); + $this->addSimplePDFField($return, 'pykotaBillingCode', _('Billing code')); + $this->addSimplePDFField($return, 'description', _('Description')); + $this->addSimplePDFField($return, 'pykotaBalance', _('Balance')); + $this->addSimplePDFField($return, 'pykotaPageCounter', _('Page count')); + return $return; + } + + /** + * Returns if the given billing code already exists. + * + * @param String $code pykotaBillingCode attribute value + * @return boolean pykotaBillingCode exists + */ + private function codeExists($code) { + if ($this->codeCache == null) { + $this->loadCodeCache(); + } + foreach ($this->codeCache as $dn => $bCode) { + if (!empty($bCode) && ($bCode == $code)) { + return true; + } + } + return false; + } + + /** + * Loads the list of billing code names into the cache. + */ + private function loadCodeCache() { + if ($this->codeCache != null) { + return; + } + $results = searchLDAPByFilter('(objectClass=pykotaBilling)', array('pykotaBillingCode', 'dn'), array($this->get_scope())); + $this->codeCache = array(); + foreach ($results as $result) { + if (isset($result['pykotabillingcode'][0])) { + $this->codeCache[$result['dn']] = $result['pykotabillingcode'][0]; + } + } + } + +} + + +?> diff --git a/lam/lib/modules/pykotaGroup.inc b/lam/lib/modules/pykotaGroup.inc new file mode 100644 index 00000000..13786a9e --- /dev/null +++ b/lam/lib/modules/pykotaGroup.inc @@ -0,0 +1,592 @@ + value */ + private $limitOptions; + + /** + * Returns if this module also manages the structural object class pykotaObject. + * This is overridden by a submodule that must provide the structural object class. + * + * @return boolean structural usage + */ + public function isStructural() { + return false; + } + + /** + * Creates a new pykotaGroup object. + * + * @param string $scope account type (user, group, host) + */ + function __construct($scope) { + $this->limitOptions = array( + _('Quota') => 'quota', + _('Balance') => 'balance', + _('No quota') => 'noquota', + _('Free printing') => 'nochange', + _('Deny printing') => 'noprint', + ); + // call parent constructor + parent::__construct($scope); + $this->autoAddObjectClasses = $this->isStructural(); + } + + /** + * Returns meta data that is interpreted by parent class + * + * @return array array with meta data + * + * @see baseModule::get_metaData() + */ + function get_metaData() { + $return = array(); + // icon + $return['icon'] = 'printerBig.png'; + // manages host accounts + $return["account_types"] = array('group'); + // alias name + $return["alias"] = _("PyKota"); + // this is a base module + $return["is_base"] = $this->isStructural(); + // LDAP filter + $return["ldap_filter"] = array('or' => "(objectClass=pykotaGroup)"); + // module dependencies + $return['dependencies'] = array('depends' => array(), 'conflicts' => array()); + // managed object classes + $return['objectClasses'] = array('pykotaGroup'); + // managed attributes + $return['attributes'] = array('pykotaLimitBy', 'pykotaGroupName'); + if ($this->manageCn()) { + $return['attributes'][] = 'cn'; + } + if ($this->manageDescription()) { + $return['attributes'][] = 'description'; + } + // help Entries + $return['help'] = array( + 'cn' => array( + "Headline" => _("Common name"), 'attr' => 'cn', + "Text" => _("Group name of the group which should be created. Valid characters are: a-z, A-Z, 0-9 and .-_ .") + ), + 'description' => array ( + "Headline" => _("Description"), 'attr' => 'description', + "Text" => _("Group description.") + ), + 'pykotaGroupName' => array( + "Headline" => _("PyKota group name"), 'attr' => 'pykotaGroupName', + "Text" => _("Group name that is used for PyKota.") + ), + 'pykotaLimitBy' => array( + "Headline" => _("Limit type"), 'attr' => 'pykotaLimitBy', + "Text" => _("Specifies the type of limit for printing if any. Please note that in contrast to \"Free printing\" the option \"No quota\" includes accounting.") + ), + 'autoAdd' => array( + "Headline" => _("Automatically add this extension"), + "Text" => _("This will enable the extension automatically if this profile is loaded.") + ), + ); + // profile options + $profileContainer = new htmlTable(); + $pykotaLimitByProfileOption = new htmlTableExtendedSelect('pykotaGroup_pykotaLimitBy', $this->limitOptions, array(), _('Limit type'), 'pykotaLimitBy'); + $pykotaLimitByProfileOption->setHasDescriptiveElements(true); + $pykotaLimitByProfileOption->setSortElements(false); + $profileContainer->addElement($pykotaLimitByProfileOption, true); + if (!$this->isStructural()) { + $profileContainer->addElement(new htmlTableExtendedInputCheckbox('pykotaGroup_addExt', false, _('Automatically add this extension'), 'autoAdd'), true); + } + $return['profile_options'] = $profileContainer; + $return['profile_mappings']['pykotaGroup_pykotaLimitBy'] = 'pykotaLimitBy'; + // upload fields + $return['upload_columns'] = array( + array( + 'name' => 'pykotaGroup_pykotaGroupName', + 'description' => _('PyKota group name'), + 'help' => 'pykotaGroupName', + 'example' => _('adminstrators'), + 'unique' => true, + ) + ); + if ($this->manageCn()) { + $return['upload_columns'][] = array( + 'name' => 'pykotaGroup_cn', + 'description' => _('Common name'), + 'help' => 'cn', + 'example' => _('adminstrators'), + 'required' => true, + ); + } + if ($this->manageDescription()) { + $return['upload_columns'][] = array( + 'name' => 'pykotaGroup_description', + 'description' => _('Description'), + 'help' => 'description', + 'example' => _('Administrators group'), + ); + } + $return['upload_columns'][] = array( + 'name' => 'pykotaGroup_pykotaLimitBy', + 'description' => _('Limit type'), + 'help' => 'pykotaLimitBy', + 'example' => _('Quota'), + 'default' => _('Quota'), + 'values' => implode(', ', array_keys($this->limitOptions)) + ); + // available PDF fields + $return['PDF_fields'] = array( + 'pykotaGroupName' => _('PyKota group name'), + 'pykotaLimitBy' => _('Limit type'), + ); + if ($this->manageCn()) { + $return['PDF_fields']['cn'] = _('Common name'); + } + if ($this->manageDescription()) { + $return['PDF_fields']['description'] = _('Description'); + } + return $return; + } + + /** + * This function fills the $messages variable with output messages from this module. + */ + function load_Messages() { + $this->messages['cn'][0] = array('ERROR', _('Group name'), _('Group name contains invalid characters. Valid characters are: a-z, A-Z, 0-9 and .-_ !')); + $this->messages['cn'][1] = array('ERROR', _('Account %s:') . ' pykotaGroup_cn', _('Group name contains invalid characters. Valid characters are: a-z, A-Z, 0-9 and .-_ !')); + $this->messages['cn'][2] = array('ERROR', _('Group name'), _('Group name already exists!')); + $this->messages['cn'][3] = array('ERROR', _('Account %s:') . ' pykotaGroup_cn', _('Group name already exists!')); + $this->messages['pykotaGroupName'][0] = array('ERROR', _('PyKota group name'), _('Group name contains invalid characters. Valid characters are: a-z, A-Z, 0-9 and .-_ !')); + $this->messages['pykotaGroupName'][1] = array('ERROR', _('Account %s:') . ' pykotaGroup_pykotaGroupName', _('Group name contains invalid characters. Valid characters are: a-z, A-Z, 0-9 and .-_ !')); + $this->messages['pykotaGroupName'][2] = array('ERROR', _('PyKota group name'), _('Group name already exists!')); + $this->messages['pykotaGroupName'][3] = array('ERROR', _('Account %s:') . ' pykotaGroup_pykotaGroupName', _('Group name already exists!')); + $this->messages['pykotaLimitBy'][0] = array('ERROR', _('Account %s:') . ' pykotaGroup_pykotaLimitBy', _('Please enter a valid limit type.')); + } + + /** + * Returns the HTML meta data for the main account page. + * + * @return htmlElement HTML meta data + */ + function display_html_attributes() { + $container = new htmlTable(); + if ($this->isStructural() || (isset($this->attributes['objectClass']) && in_array('pykotaGroup', $this->attributes['objectClass']))) { + // cn + if ($this->manageCn()) { + $this->addSimpleInputTextField($container, 'cn', _('Group name'), true); + } + // pykotaGroupName + $this->addSimpleInputTextField($container, 'pykotaGroupName', _('Pykota group name')); + // limit by + $limitOption = 'quota'; + if (!empty($this->attributes['pykotaLimitBy'][0])) { + $limitOption = $this->attributes['pykotaLimitBy'][0]; + } + $limitSelect = new htmlTableExtendedSelect('pykotaLimitBy', $this->limitOptions, array($limitOption), _('Limit type'), 'pykotaLimitBy'); + $limitSelect->setHasDescriptiveElements(true); + $limitSelect->setSortElements(false); + $container->addElement($limitSelect, true); + // description + if ($this->manageDescription()) { + $this->addMultiValueInputTextField($container, 'description', _('Description'), false, null, true); + } + // remove button + if (!$this->isStructural()) { + $container->addElement(new htmlSpacer(null, '20px'), true); + $remButton = new htmlButton('remObjectClass', _('Remove PyKota extension')); + $remButton->colspan = 5; + $container->addElement($remButton); + } + } + else { + // add button + $container->addElement(new htmlButton('addObjectClass', _('Add PyKota extension'))); + } + return $container; + } + + /** + * Processes user input of the primary module page. + * It checks if all input values are correct and updates the associated LDAP attributes. + * + * @return array list of info/error messages + */ + function process_attributes() { + $errors = array(); + if (isset($_POST['addObjectClass'])) { + if (!isset($this->attributes['objectClass'])) { + $this->attributes['objectClass'] = array(); + } + if (!in_array('pykotaGroup', $this->attributes['objectClass'])) { + $this->attributes['objectClass'][] = 'pykotaGroup'; + } + if (!isset($this->attributes['pykotaGroupName'][0])) { + $this->attributes['pykotaGroupName'][0] = $this->getCurrentGroupName(); + } + return $errors; + } + if (isset($_POST['remObjectClass'])) { + $this->attributes['objectClass'] = array_delete(array('pykotaGroup'), $this->attributes['objectClass']); + $attrs = array('pykotaLimitBy', 'pykotaGroupName'); + if ($this->manageDescription()) { + $attrs[] = 'description'; + } + if ($this->manageCn()) { + $attrs[] = 'cn'; + } + foreach ($attrs as $name) { + if (isset($this->attributes[$name])) { + unset($this->attributes[$name]); + } + } + return $errors; + } + // skip processing if object class is not set + if (!isset($this->attributes['objectClass']) || !in_array('pykotaGroup', $this->attributes['objectClass'])) { + return $errors; + } + // cn + if ($this->manageCn()) { + if (isset($_POST['cn']) && ($_POST['cn'] != '')) { + if (!get_preg($_POST['cn'], 'groupname')) { + $errors[] = $this->messages['cn'][0]; + } + else { + $this->attributes['cn'][0] = $_POST['cn']; + if ((!isset($this->orig['cn'][0]) || ($this->attributes['cn'][0] != $this->orig['cn'][0])) + && $this->cnExists($_POST['cn'])) { + $errors[] = $this->messages['cn'][2]; + } + } + } + elseif (isset($this->attributes['cn'][0])) { + unset($this->attributes['cn'][0]); + } + } + // PyKota group name + if (!empty($_POST['pykotaGroupName'])) { + if (!get_preg($_POST['pykotaGroupName'], 'groupname')) { + $errors[] = $this->messages['pykotaGroupName'][0]; + } + else { + $this->attributes['pykotaGroupName'][0] = $_POST['pykotaGroupName']; + if ((!isset($this->orig['pykotaGroupName'][0]) || ($this->attributes['pykotaGroupName'][0] != $this->orig['pykotaGroupName'][0])) + && $this->pykotaGroupNameExists($_POST['pykotaGroupName'])) { + $errors[] = $this->messages['pykotaGroupName'][2]; + } + } + } + else { + $this->attributes['pykotaGroupName'][0] = $this->getCurrentGroupName(); + } + // limit by + $this->attributes['pykotaLimitBy'][0] = $_POST['pykotaLimitBy']; + // description + if ($this->manageDescription()) { + $this->processMultiValueInputTextField('description', $errors); + } + return $errors; + } + + /** + * Returns a list of modifications which have to be made to the LDAP account. + * + * @return array list of modifications + *
This function returns an array with 3 entries: + *
array( DN1 ('add' => array($attr), 'remove' => array($attr), 'modify' => array($attr)), DN2 .... ) + *
DN is the DN to change. It may be possible to change several DNs (e.g. create a new user and add him to some groups via attribute memberUid) + *
"add" are attributes which have to be added to LDAP entry + *
"remove" are attributes which have to be removed from LDAP entry + *
"modify" are attributes which have to been modified in LDAP entry + *
"info" are values with informational value (e.g. to be used later by pre/postModify actions) + */ + function save_attributes() { + if (!in_array('pykotaGroup', $this->attributes['objectClass']) && !in_array('pykotaGroup', $this->orig['objectClass'])) { + // skip saving if the extension was not added/modified + return array(); + } + return parent::save_attributes(); + } + + /** + * This function is used to check if all settings for this module have been made. + * + * Calling this method requires the existence of an enclosing {@link accountContainer}.
+ *
+ * This function tells LAM if it can create/modify the LDAP account. If your module needs any + * additional input then set this to false. The user will be notified that your module needs + * more input.
+ * This method's return value defaults to true. + * + * @return boolean true, if settings are complete + */ + public function module_complete() { + if (in_array('pykotaGroup', $this->attributes['objectClass'])) { + // require cn + $cn = $this->getCurrentGroupName(); + return !empty($cn); + } + return true; + } + + /** + * Loads the values of an account profile into internal variables. + * + * @param array $profile hash array with profile values (identifier => value) + */ + function load_profile($profile) { + // profile mappings in meta data + parent::load_profile($profile); + // add extension + if (isset($profile['pykotaGroup_addExt'][0]) && ($profile['pykotaGroup_addExt'][0] == "true")) { + if (!in_array('pykotaGroup', $this->attributes['objectClass'])) { + $this->attributes['objectClass'][] = 'pykotaGroup'; + } + } + } + + /** + * In this function the LDAP account is built up. + * + * @param array $rawAccounts list of hash arrays (name => value) from user input + * @param array $ids list of IDs for column position (e.g. "posixAccount_uid" => 5) + * @param array $partialAccounts list of hash arrays (name => value) which are later added to LDAP + * @param array $selectedModules list of selected account modules + * @return array list of error messages if any + */ + function build_uploadAccounts($rawAccounts, $ids, &$partialAccounts, $selectedModules) { + $messages = array(); + for ($i = 0; $i < sizeof($rawAccounts); $i++) { + // add object classes + if (!in_array('pykotaGroup', $partialAccounts[$i]['objectClass'])) { + $partialAccounts[$i]['objectClass'][] = 'pykotaGroup'; + } + if ($this->isStructural() && !in_array('pykotaObject', $partialAccounts[$i]['objectClass'])) { + $partialAccounts[$i]['objectClass'][] = 'pykotaObject'; + } + // cn + if ($this->manageCn() && !empty($rawAccounts[$i][$ids['pykotaGroup_cn']])) { + if (!get_preg($rawAccounts[$i][$ids['pykotaGroup_cn']], 'groupname')) { + $errMsg = $this->messages['cn'][1]; + array_push($errMsg, array($i)); + $messages[] = $errMsg; + } + elseif ($this->cnExists($rawAccounts[$i][$ids['pykotaGroup_cn']])) { + $errMsg = $this->messages['cn'][3]; + array_push($errMsg, array($i)); + $messages[] = $errMsg; + } + else { + $partialAccounts[$i]['cn'] = $rawAccounts[$i][$ids['pykotaGroup_cn']]; + } + } + // description + if ($this->manageDescription() && !empty($rawAccounts[$i][$ids['pykotaGroup_description']])) { + $partialAccounts[$i]['description'] = $rawAccounts[$i][$ids['pykotaGroup_description']]; + } + // PyKota group name + if (!empty($rawAccounts[$i][$ids['pykotaGroup_pykotaGroupName']])) { + if (!get_preg($rawAccounts[$i][$ids['pykotaGroup_pykotaGroupName']], 'groupname')) { + $errMsg = $this->messages['pykotaGroupName'][1]; + array_push($errMsg, array($i)); + $messages[] = $errMsg; + } + elseif ($this->pykotaGroupNameExists($rawAccounts[$i][$ids['pykotaGroup_pykotaGroupName']])) { + $errMsg = $this->messages['pykotaGroupName'][3]; + array_push($errMsg, array($i)); + $messages[] = $errMsg; + } + else { + $partialAccounts[$i]['pykotaGroupName'] = $rawAccounts[$i][$ids['pykotaGroup_pykotaGroupName']]; + } + } + // limit by + if (!empty($rawAccounts[$i][$ids['pykotaGroup_pykotaLimitBy']])) { + if (isset($this->limitOptions[$rawAccounts[$i][$ids['pykotaGroup_pykotaLimitBy']]])) { + $partialAccounts[$i]['pykotaLimitBy'] = $this->limitOptions[$rawAccounts[$i][$ids['pykotaGroup_pykotaLimitBy']]]; + } + else { + $errMsg = $this->messages['pykotaLimitBy'][0]; + array_push($errMsg, array($i)); + $messages[] = $errMsg; + } + } + else { + $partialAccounts[$i]['pykotaLimitBy'] = 'quota'; + } + } + return $messages; + } + + /** + * Returns a list of PDF entries + */ + function get_pdfEntries() { + $return = array(); + $this->addSimplePDFField($return, 'cn', _('Common name')); + $this->addSimplePDFField($return, 'pykotaGroupName', _('PyKota group name')); + $this->addSimplePDFField($return, 'description', _('Description')); + $limitByOptions = array_flip($this->limitOptions); + $limitByValue = ''; + if (!empty($this->attributes['pykotaLimitBy'][0]) && isset($limitByOptions[$this->attributes['pykotaLimitBy'][0]])) { + $limitByValue = $limitByOptions[$this->attributes['pykotaLimitBy'][0]]; + } + $return[get_class($this) . '_pykotaLimitBy'] = array('' . _('Limit type') . '' . $limitByValue . ''); + return $return; + } + + /** + * Returns if the cn attribute should be managed. + * + * @return boolean manage cn attribute + */ + private function manageCn() { + if (isset($_SESSION['config'])) { + $conf = $_SESSION['config']; + if (in_array('posixGroup', $conf->get_AccountModules($this->get_scope())) + || in_array('groupOfNames', $conf->get_AccountModules($this->get_scope())) + || in_array('groupOfUniqueNames', $conf->get_AccountModules($this->get_scope()))) { + return false; + } + else { + return true; + } + } + return false; + } + + /** + * Returns if the description attribute should be managed. + * + * @return boolean manage description attribute + */ + private function manageDescription() { + if (isset($_SESSION['config'])) { + $conf = $_SESSION['config']; + if (in_array('posixGroup', $conf->get_AccountModules($this->get_scope())) + || in_array('groupOfNames', $conf->get_AccountModules($this->get_scope())) + || in_array('groupOfUniqueNames', $conf->get_AccountModules($this->get_scope()))) { + return false; + } + else { + return true; + } + } + return false; + } + + /** + * Returns if the given cn already exists. + * + * @param String $cn cn attribute value + * @return boolean cn exists + */ + private function cnExists($cn) { + if ($this->cnCache == null) { + $this->loadGroupNameCache(); + } + return in_array($cn, $this->cnCache); + } + + /** + * Returns if the given pykotaGroupName already exists. + * + * @param String $pykotaGroupName pykotaGroupName attribute value + * @return boolean pykotaGroupName exists + */ + private function pykotaGroupNameExists($pykotaGroupName) { + if ($this->pykotaGroupNameCache == null) { + $this->loadGroupNameCache(); + } + return in_array($pykotaGroupName, $this->pykotaGroupNameCache); + } + + /** + * Loads the list of group names into the cache. + */ + private function loadGroupNameCache() { + $results = searchLDAPByFilter('(objectClass=pykotaGroup)', array('cn', 'pykotaGroupName'), array($this->get_scope())); + $this->cnCache = array(); + $this->pykotaGroupNameCache = array(); + foreach ($results as $result) { + if (isset($result['cn'][0])) { + $this->cnCache[] = $result['cn'][0]; + } + if (isset($result['pykotagroupname'][0])) { + $this->pykotaGroupNameCache[] = $result['pykotagroupname'][0]; + } + } + } + + /** + * Returns the current group name (cn) of this account. + * + * @return String group name + */ + private function getCurrentGroupName() { + if (!empty($this->attributes['cn'][0])) { + return $this->attributes['cn'][0]; + } + if ($this->getAccountContainer()->getAccountModule('posixGroup') != null) { + $posix = $this->getAccountContainer()->getAccountModule('posixGroup'); + $attrs = $posix->getAttributes(); + if (!empty($attrs['cn'][0])) { + return $attrs['cn'][0]; + } + } + if ($this->getAccountContainer()->getAccountModule('groupOfNames') != null) { + $posix = $this->getAccountContainer()->getAccountModule('groupOfNames'); + $attrs = $posix->getAttributes(); + if (!empty($attrs['cn'][0])) { + return $attrs['cn'][0]; + } + } + if ($this->getAccountContainer()->getAccountModule('groupOfUniqueNames') != null) { + $posix = $this->getAccountContainer()->getAccountModule('groupOfUniqueNames'); + $attrs = $posix->getAttributes(); + if (!empty($attrs['cn'][0])) { + return $attrs['cn'][0]; + } + } + return ''; + } + +} + + +?> diff --git a/lam/lib/modules/pykotaGroupStructural.inc b/lam/lib/modules/pykotaGroupStructural.inc new file mode 100644 index 00000000..02f8d1b9 --- /dev/null +++ b/lam/lib/modules/pykotaGroupStructural.inc @@ -0,0 +1,69 @@ + array(), 'conflicts' => array('pykotaGroup')); + // additional object class and attribute + $return['objectClasses'][] = 'pykotaObject'; + // RDN attribute + $return["RDN"] = array("cn" => "normal"); + return $return; + } + +} diff --git a/lam/lib/modules/pykotaPrinter.inc b/lam/lib/modules/pykotaPrinter.inc new file mode 100644 index 00000000..6bfc713e --- /dev/null +++ b/lam/lib/modules/pykotaPrinter.inc @@ -0,0 +1,689 @@ + array(cn => ..., description => ...))) */ + private $printerCache = null; + /** printer group cache */ + private $groupCache = null; + /** list of pass through options: label => value */ + private $passThroughOptions; + + /** + * Creates a new pykotaPrinter object. + * + * @param string $scope account type (user, group, host) + */ + function __construct($scope) { + $this->passThroughOptions = array( + _('Yes') => 't', + _('No') => 'f', + ); + // call parent constructor + parent::__construct($scope); + } + + /** + * Returns meta data that is interpreted by parent class + * + * @return array array with meta data + * + * @see baseModule::get_metaData() + */ + function get_metaData() { + $return = array(); + // icon + $return['icon'] = 'printerBig.png'; + // manages host accounts + $return["account_types"] = array('pykotaPrinterType'); + // alias name + $return["alias"] = _("PyKota"); + // this is a base module + $return["is_base"] = true; + // RDN attribute + $return["RDN"] = array("cn" => "high"); + // LDAP filter + $return["ldap_filter"] = array('or' => "(objectClass=pykotaPrinter)"); + // module dependencies + $return['dependencies'] = array('depends' => array(), 'conflicts' => array()); + // managed object classes + $return['objectClasses'] = array('pykotaObject', 'pykotaPrinter'); + // managed attributes + $return['attributes'] = array('cn', 'description', 'pykotaMaxJobSize', 'pykotaPassThrough', 'pykotaPricePerJob', 'pykotaPricePerPage', 'pykotaPrinterName', 'uniqueMember'); + // help Entries + $return['help'] = array( + 'cn' => array( + "Headline" => _("Printer name"), 'attr' => 'cn', + "Text" => _("Printer name of the printer which should be created. Valid characters are: a-z, A-Z, 0-9 and .-_ .") + ), + 'description' => array ( + "Headline" => _("Description"), 'attr' => 'description', + "Text" => _("Printer description.") + ), + 'pykotaMaxJobSize' => array( + "Headline" => _('Maximum job size'), 'attr' => 'pykotaMaxJobSize', + "Text" => _('The maximum number of pages per job allowed on the printer. 0 means unlimited.') + ), + 'pykotaPassThrough' => array( + "Headline" => _('Passthrough'), 'attr' => 'pykotaPassThrough', + "Text" => _('In passthrough mode, users are allowed to print without any impact on their quota or account balance.') + ), + 'pykotaPricePerJob' => array( + "Headline" => _('Price per job'), 'attr' => 'pykotaPricePerJob', + "Text" => _('The price for each print job.') + ), + 'pykotaPricePerPage' => array( + "Headline" => _('Price per page'), 'attr' => 'pykotaPricePerPage', + "Text" => _('The price for each page of a print job.') + ), + 'uniqueMember' => array( + "Headline" => _('Group members'), 'attr' => 'uniqueMember', + "Text" => _('If this entry should be a printer group then you can set the member names here.') + ), + 'uniqueMemberUpload' => array( + "Headline" => _('Group members'), 'attr' => 'uniqueMember', + "Text" => _('If this entry should be a printer group then you can set the member names here.') + . ' ' . _('Multiple values are separated by comma.') + ), + 'filter' => array( + "Headline" => _("Filter"), + "Text" => _("Here you can enter a filter value. Only entries which contain the filter text will be shown.") + . ' ' . _('Possible wildcards are: "*" = any character, "^" = line start, "$" = line end') + ), + ); + // profile options + $profileContainer = new htmlTable(); + $profileContainer->addElement(new htmlTableExtendedInputField(_('Maximum job size'), 'pykotaPrinter_pykotaMaxJobSize', '', 'pykotaMaxJobSize'), true); + $return['profile_options'] = $profileContainer; + $return['profile_mappings']['pykotaPrinter_pykotaMaxJobSize'] = 'pykotaMaxJobSize'; + $return['profile_checks']['pykotaPrinter_pykotaMaxJobSize'] = array( + 'type' => 'ext_preg', + 'regex' => 'digit', + 'error_message' => $this->messages['pykotaMaxJobSize'][0]); + // upload fields + $return['upload_columns'] = array( + array( + 'name' => 'pykotaPrinter_cn', + 'description' => _('Printer name'), + 'help' => 'cn', + 'example' => _('printer01'), + 'required' => true, + ), + array( + 'name' => 'pykotaPrinter_description', + 'description' => _('Description'), + 'help' => 'description', + 'example' => _('Color laser printer'), + ), + array( + 'name' => 'pykotaPrinter_pykotaMaxJobSize', + 'description' => _('Maximum job size'), + 'help' => 'pykotaMaxJobSize', + 'example' => '100', + 'default' => '0', + ), + array( + 'name' => 'pykotaPrinter_pykotaPassThrough', + 'description' => _('Passthrough'), + 'help' => 'pykotaPassThrough', + 'example' => _('No'), + 'default' => _('No'), + 'values' => _('Yes') . ', ' . _('No'), + ), + array( + 'name' => 'pykotaPrinter_pykotaPricePerJob', + 'description' => _('Price per job'), + 'help' => 'pykotaPricePerJob', + 'example' => '0.01', + ), + array( + 'name' => 'pykotaPrinter_pykotaPricePerPage', + 'description' => _('Price per page'), + 'help' => 'pykotaPricePerPage', + 'example' => '0.01', + ), + array( + 'name' => 'pykotaPrinter_uniqueMember', + 'description' => _('Group members'), + 'help' => 'uniqueMemberUpload', + 'example' => _('printergroup1'), + ), + ); + // available PDF fields + $return['PDF_fields'] = array( + 'cn' => _('Printer name'), + 'description' => _('Description'), + 'pykotaMaxJobSize' => _('Maximum job size'), + 'pykotaPassThrough' => _('Passthrough'), + 'pykotaPricePerJob' => _('Price per job'), + 'pykotaPricePerPage' => _('Price per page'), + 'uniqueMember' => _('Group members'), + 'parentUniqueMember' => _('Printer groups'), + ); + return $return; + } + + /** + * This function fills the $messages variable with output messages from this module. + */ + function load_Messages() { + $this->messages['cn'][0] = array('ERROR', _('Printer name'), _('Printer name contains invalid characters. Valid characters are: a-z, A-Z, 0-9 and .-_ !')); + $this->messages['cn'][1] = array('ERROR', _('Account %s:') . ' pykotaPrinter_cn', _('Printer name contains invalid characters. Valid characters are: a-z, A-Z, 0-9 and .-_ !')); + $this->messages['cn'][2] = array('ERROR', _('Printer name'), _('Printer name already exists!')); + $this->messages['cn'][3] = array('ERROR', _('Account %s:') . ' pykotaPrinter_cn', _('Printer name already exists!')); + $this->messages['pykotaMaxJobSize'][0] = array('ERROR', _('Maximum job size'), _('Please enter a valid number.')); + $this->messages['pykotaMaxJobSize'][1] = array('ERROR', _('Account %s:') . ' pykotaPrinter_pykotaMaxJobSize', _('Please enter a valid number.')); + $this->messages['pykotaPricePerJob'][0] = array('ERROR', _('Price per job'), _('Please enter a valid number (e.g. "1.5").')); + $this->messages['pykotaPricePerJob'][1] = array('ERROR', _('Account %s:') . ' pykotaPrinter_pykotaPricePerJob', _('Please enter a valid number (e.g. "1.5").')); + $this->messages['pykotaPricePerPage'][0] = array('ERROR', _('Price per page'), _('Please enter a valid number (e.g. "1.5").')); + $this->messages['pykotaPricePerPage'][1] = array('ERROR', _('Account %s:') . ' pykotaPrinter_pykotaPricePerPage', _('Please enter a valid number (e.g. "1.5").')); + $this->messages['pykotaPassThrough'][0] = array('ERROR', _('Account %s:') . ' pykotaPrinter_pykotaPassThrough', _('Please enter "Yes" or "No".')); + $this->messages['uniqueMember'][0] = array('ERROR', _('Account %s:') . ' pykotaPrinter_uniqueMember', _('Unable to find a printer with name "%s".')); + } + + /** + * Returns the HTML meta data for the main account page. + * + * @return htmlElement HTML meta data + */ + function display_html_attributes() { + $container = new htmlTable(); + // cn + $this->addSimpleInputTextField($container, 'cn', _('Printer name'), true); + // job size + $this->addSimpleInputTextField($container, 'pykotaMaxJobSize', _('Maximum job size')); + // price per job + $this->addSimpleInputTextField($container, 'pykotaPricePerJob', _('Price per job')); + // price per page + $this->addSimpleInputTextField($container, 'pykotaPricePerPage', _('Price per page')); + // passthrough + $pykotaPassThroughOption = 'f'; + if (!empty($this->attributes['pykotaPassThrough'][0])) { + $pykotaPassThroughOption = $this->attributes['pykotaPassThrough'][0]; + } + $pykotaPassThroughSelect = new htmlTableExtendedSelect('pykotaPassThrough', $this->passThroughOptions, array($pykotaPassThroughOption), _('Passthrough'), 'pykotaPassThrough'); + $pykotaPassThroughSelect->setHasDescriptiveElements(true); + $container->addElement($pykotaPassThroughSelect); + $container->addElement(new htmlSpacer('150px', null), true); // layout fix if many parent groups exist + // description + $this->addMultiValueInputTextField($container, 'description', _('Description'), false, null, true); + // printer groups + if (!$this->getAccountContainer()->isNewAccount) { + $groups = $this->getPrinterGroups(); + $this->loadPrinterNameCache(); + $parentPrinters = array(); + foreach ($groups as $groupDN) { + $parentPrinters[] = $this->printerCache[$groupDN]['cn']; + } + if (sizeof($parentPrinters) > 0) { + $container->addElement(new htmlOutputText(_('Printer groups'))); + $parentPrinterText = new htmlOutputText(implode(', ', $parentPrinters)); + $parentPrinterText->colspan = 5; + $container->addElement($parentPrinterText, true); + } + } + // printer members + $memberLabel = new htmlOutputText(_('Group members')); + $memberLabel->alignment = htmlElement::ALIGN_TOP; + $container->addElement($memberLabel); + $addMemberButton = new htmlAccountPageButton(get_class($this), 'members', 'open', 'add.png', true); + $addMemberButton->setTitle(_('Add')); + $addMemberButton->alignment = htmlElement::ALIGN_TOP; + if (!empty($this->attributes['uniqueMember'][0])) { + $memberTable = new htmlTable(); + $memberTable->alignment = htmlElement::ALIGN_TOP; + for ($i = 0; $i < sizeof($this->attributes['uniqueMember']); $i++) { + $member = $this->attributes['uniqueMember'][$i]; + if (isset($this->printerCache[$member]['cn'])) { + $member = $this->printerCache[$member]['cn']; + } + $memberTable->addElement(new htmlOutputText($member)); + $delButton = new htmlButton('uniqueMemberDel_' . $i, 'del.png', true); + $delButton->setTitle(_('Delete')); + $memberTable->addElement($delButton); + if ($i == (sizeof($this->attributes['uniqueMember']) - 1)) { + $memberTable->addElement($addMemberButton); + } + $memberTable->addNewLine(); + } + $container->addElement($memberTable); + } + else { + $container->addElement($addMemberButton); + } + $memberHelp = new htmlHelpLink('uniqueMember'); + $memberHelp->alignment = htmlElement::ALIGN_TOP; + $container->addElement($memberHelp, true); + return $container; + } + + /** + * Processes user input of the primary module page. + * It checks if all input values are correct and updates the associated LDAP attributes. + * + * @return array list of info/error messages + */ + function process_attributes() { + $errors = array(); + // cn + if (isset($_POST['cn']) && ($_POST['cn'] != '')) { + if (!get_preg($_POST['cn'], 'username')) { + $errors[] = $this->messages['cn'][0]; + } + else { + $this->attributes['cn'][0] = $_POST['cn']; + $this->attributes['pykotaPrinterName'][0] = $_POST['cn']; + if ((!isset($this->orig['cn'][0]) || ($this->attributes['cn'][0] != $this->orig['cn'][0])) + && $this->cnExists($_POST['cn'])) { + $errors[] = $this->messages['cn'][2]; + } + } + } + else { + if (isset($this->attributes['cn'][0])) { + unset($this->attributes['cn'][0]); + } + if (isset($this->attributes['pykotaPrinterName'][0])) { + unset($this->attributes['pykotaPrinterName'][0]); + } + } + // description + $this->processMultiValueInputTextField('description', $errors); + // job size + $pykotaMaxJobSize = '0'; + if (isset($_POST['pykotaMaxJobSize']) && ($_POST['pykotaMaxJobSize'] != '')) { + $pykotaMaxJobSize = $_POST['pykotaMaxJobSize']; + if (!get_preg($pykotaMaxJobSize, 'digit')) { + $errors[] = $this->messages['pykotaMaxJobSize'][0]; + } + } + $this->attributes['pykotaMaxJobSize'][0] = $pykotaMaxJobSize; + // price per job + $pykotaPricePerJob = '0.0'; + if (isset($_POST['pykotaPricePerJob']) && ($_POST['pykotaPricePerJob'] != '')) { + $pykotaPricePerJob = $_POST['pykotaPricePerJob']; + $pykotaPricePerJob = str_replace(',', '.', $pykotaPricePerJob); + if (strpos($pykotaPricePerJob, '.') === false) { + $pykotaPricePerJob .= '.0'; + } + if (!get_preg($pykotaPricePerJob, 'float')) { + $errors[] = $this->messages['pykotaPricePerJob'][0]; + } + } + $this->attributes['pykotaPricePerJob'][0] = $pykotaPricePerJob; + // price per page + $pykotaPricePerPage = '0.0'; + if (isset($_POST['pykotaPricePerPage']) && ($_POST['pykotaPricePerPage'] != '')) { + $pykotaPricePerPage = $_POST['pykotaPricePerPage']; + $pykotaPricePerPage = str_replace(',', '.', $pykotaPricePerPage); + if (strpos($pykotaPricePerPage, '.') === false) { + $pykotaPricePerPage .= '.0'; + } + if (!get_preg($pykotaPricePerPage, 'float')) { + $errors[] = $this->messages['pykotaPricePerPage'][0]; + } + } + $this->attributes['pykotaPricePerPage'][0] = $pykotaPricePerPage; + // passthrough + $this->attributes['pykotaPassThrough'][0] = $_POST['pykotaPassThrough']; + // delete members + foreach ($_POST as $key => $value) { + if (strpos($key, 'uniqueMemberDel_') === 0) { + $index = substr($key, strlen('uniqueMemberDel_')); + unset($this->attributes['uniqueMember'][$index]); + $this->attributes['uniqueMember'] = array_values($this->attributes['uniqueMember']); + break; + } + } + return $errors; + } + + /** + * This function will create the meta HTML code to show a page to add members. + * + * @return htmlElement HTML meta data + */ + function display_html_members() { + $return = new htmlTable(); + $userFilter = ''; + $userFilterRegex = ''; + if (isset($_POST['newFilter'])) { + $userFilter = $_POST['newFilter']; + $userFilterRegex = '/' . str_replace(array('*', '(', ')'), array('.*', '\(', '\)'), $_POST['newFilter']) . '/ui'; + } + $options = array(); + $this->loadPrinterNameCache(); + foreach ($this->printerCache as $dn => $attrs) { + if (!empty($attrs['description'])) { + $label = $attrs['cn'] . ' (' . $attrs['description'] . ')'; + } + else { + $label = $attrs['cn']; + } + // skip filtered printers + if (!empty($userFilter) && !preg_match($userFilterRegex, $label)) { + continue; + } + // skip own entry + if (!$this->getAccountContainer()->isNewAccount && ($this->getAccountContainer()->dn_orig == $dn)) { + continue; + } + // skip already set members + if (!empty($this->attributes['uniqueMember'][0]) && in_array($dn, $this->attributes['uniqueMember'])) { + continue; + } + $options[$label] = $dn; + } + $size = 20; + if (sizeof($options) < 20) $size = sizeof($options); + $membersSelect = new htmlSelect('members', $options, array(), $size); + $membersSelect->setHasDescriptiveElements(true); + $membersSelect->setMultiSelect(true); + $membersSelect->setTransformSingleSelect(false); + $return->addElement($membersSelect, true); + $filterGroup = new htmlGroup(); + $filterGroup->addElement(new htmlInputField('newFilter', $userFilter)); + $filterGroup->addElement(new htmlButton('setFilter', _('Filter'))); + $filterGroup->addElement(new htmlHelpLink('filter')); + $return->addElement($filterGroup, true); + $return->addElement(new htmlSpacer(null, '10px'), true); + $buttonTable = new htmlTable(); + $buttonTable->addElement(new htmlAccountPageButton(get_class($this), 'attributes', 'addMembers', _('Add'))); + $buttonTable->addElement(new htmlAccountPageButton(get_class($this), 'attributes', 'cancel', _('Cancel'))); + $return->addElement($buttonTable); + return $return; + } + + /** + * Processes user input of the members page. + * It checks if all input values are correct and updates the associated LDAP attributes. + * + * @return array list of info/error messages + */ + function process_members() { + $return = array(); + if (isset($_POST['form_subpage_' . get_class($this) . '_attributes_addMembers']) && isset($_POST['members'])) { + for ($i = 0; $i < sizeof($_POST['members']); $i++) { + $this->attributes['uniqueMember'][] = $_POST['members'][$i]; + } + } + return $return; + } + + /** + * In this function the LDAP account is built up. + * + * @param array $rawAccounts list of hash arrays (name => value) from user input + * @param array $ids list of IDs for column position (e.g. "posixAccount_uid" => 5) + * @param array $partialAccounts list of hash arrays (name => value) which are later added to LDAP + * @param array $selectedModules list of selected account modules + * @return array list of error messages if any + */ + function build_uploadAccounts($rawAccounts, $ids, &$partialAccounts, $selectedModules) { + $messages = array(); + $this->loadPrinterNameCache(); + for ($i = 0; $i < sizeof($rawAccounts); $i++) { + // add object classes + if (!in_array('pykotaPrinter', $partialAccounts[$i]['objectClass'])) { + $partialAccounts[$i]['objectClass'][] = 'pykotaPrinter'; + } + if (!in_array('pykotaObject', $partialAccounts[$i]['objectClass'])) { + $partialAccounts[$i]['objectClass'][] = 'pykotaObject'; + } + // cn + if (!empty($rawAccounts[$i][$ids['pykotaPrinter_cn']])) { + if (!get_preg($rawAccounts[$i][$ids['pykotaPrinter_cn']], 'username')) { + $errMsg = $this->messages['cn'][1]; + array_push($errMsg, array($i)); + $messages[] = $errMsg; + } + elseif ($this->cnExists($rawAccounts[$i][$ids['pykotaPrinter_cn']])) { + $errMsg = $this->messages['cn'][3]; + array_push($errMsg, array($i)); + $messages[] = $errMsg; + } + else { + $partialAccounts[$i]['cn'] = $rawAccounts[$i][$ids['pykotaPrinter_cn']]; + $partialAccounts[$i]['pykotaPrinterName'] = $rawAccounts[$i][$ids['pykotaPrinter_cn']]; + } + } + // description + if (!empty($rawAccounts[$i][$ids['pykotaPrinter_description']])) { + $partialAccounts[$i]['description'] = $rawAccounts[$i][$ids['pykotaPrinter_description']]; + } + // job size + if (!empty($rawAccounts[$i][$ids['pykotaPrinter_pykotaMaxJobSize']])) { + $pykotaMaxJobSize = $rawAccounts[$i][$ids['pykotaPrinter_pykotaMaxJobSize']]; + if (!get_preg($pykotaMaxJobSize, 'digit')) { + $errMsg = $this->messages['pykotaMaxJobSize'][1]; + array_push($errMsg, array($i)); + $messages[] = $errMsg; + } + else { + $partialAccounts[$i]['pykotaMaxJobSize'] = $pykotaMaxJobSize; + } + } + else { + $partialAccounts[$i]['pykotaMaxJobSize'] = '0.0'; + } + // price per job + if (!empty($rawAccounts[$i][$ids['pykotaPrinter_pykotaPricePerJob']])) { + $pykotaPricePerJob = $rawAccounts[$i][$ids['pykotaPrinter_pykotaPricePerJob']]; + $pykotaPricePerJob = str_replace(',', '.', $pykotaPricePerJob); + if (strpos($pykotaPricePerJob, '.') === false) { + $pykotaPricePerJob .= '.0'; + } + if (!get_preg($pykotaPricePerJob, 'float')) { + $errMsg = $this->messages['pykotaPricePerJob'][1]; + array_push($errMsg, array($i)); + $messages[] = $errMsg; + } + else { + $partialAccounts[$i]['pykotaPricePerJob'] = $pykotaPricePerJob; + } + } + else { + $partialAccounts[$i]['pykotaPricePerJob'] = '0.0'; + } + // price per page + if (!empty($rawAccounts[$i][$ids['pykotaPrinter_pykotaPricePerPage']])) { + $pykotaPricePerPage = $rawAccounts[$i][$ids['pykotaPrinter_pykotaPricePerPage']]; + $pykotaPricePerPage = str_replace(',', '.', $pykotaPricePerPage); + if (strpos($pykotaPricePerPage, '.') === false) { + $pykotaPricePerPage .= '.0'; + } + if (!get_preg($pykotaPricePerPage, 'float')) { + $errMsg = $this->messages['pykotaPricePerPage'][1]; + array_push($errMsg, array($i)); + $messages[] = $errMsg; + } + else { + $partialAccounts[$i]['pykotaPricePerPage'] = $pykotaPricePerPage; + } + } + else { + $partialAccounts[$i]['pykotaPricePerPage'] = '0.0'; + } + // passthrough + if (!empty($rawAccounts[$i][$ids['pykotaPrinter_pykotaPassThrough']])) { + if (isset($this->passThroughOptions[$rawAccounts[$i][$ids['pykotaPrinter_pykotaPassThrough']]])) { + $partialAccounts[$i]['pykotaPassThrough'] = $this->passThroughOptions[$rawAccounts[$i][$ids['pykotaPrinter_pykotaPassThrough']]]; + } + else { + $errMsg = $this->messages['pykotaPassThrough'][0]; + array_push($errMsg, array($i)); + $messages[] = $errMsg; + } + } + else { + $partialAccounts[$i]['pykotaPassThrough'] = 'f'; + } + // group members + if (!empty($rawAccounts[$i][$ids['pykotaPrinter_uniqueMember']])) { + $members = preg_split('/,[ ]*/', $rawAccounts[$i][$ids['pykotaPrinter_uniqueMember']]); + $memberDNs = array(); + foreach ($members as $cn) { + if (empty($cn)) { + continue; + } + // search printer cache for cn to get DN + $found = false; + foreach ($this->printerCache as $dn => $attrs) { + if ($this->printerCache[$dn]['cn'] == $cn) { + $found = true; + $memberDNs[] = $dn; + break; + } + } + if (!$found) { + $errMsg = $this->messages['uniqueMember'][0]; + array_push($errMsg, array($i, htmlspecialchars($cn))); + $messages[] = $errMsg; + } + } + if (sizeof($memberDNs) > 0) { + $partialAccounts[$i]['uniqueMember'] = $memberDNs; + } + } + } + return $messages; + } + + /** + * Returns a list of PDF entries + */ + function get_pdfEntries() { + $return = array(); + $this->loadPrinterNameCache(); + $this->addSimplePDFField($return, 'cn', _('Printer name')); + $this->addSimplePDFField($return, 'description', _('Description')); + $this->addSimplePDFField($return, 'pykotaMaxJobSize', _('Maximum job size')); + $this->addSimplePDFField($return, 'pykotaPricePerJob', _('Price per job')); + $this->addSimplePDFField($return, 'pykotaPricePerPage', _('Price per page')); + // passthrough + $passthroughOptions = array_flip($this->passThroughOptions); + $passthroughValue = ''; + if (!empty($this->attributes['pykotaPassThrough'][0]) && isset($passthroughOptions[$this->attributes['pykotaPassThrough'][0]])) { + $passthroughValue = $passthroughOptions[$this->attributes['pykotaPassThrough'][0]]; + } + $return[get_class($this) . '_pykotaPassThrough'] = array('' . _('Passthrough') . '' . $passthroughValue . ''); + // members + if (!empty($this->attributes['uniqueMember'][0])) { + $members = array(); + foreach ($this->attributes['uniqueMember'] as $member) { + if (!empty($this->printerCache[$member]['cn'])) { + $members[] = $this->printerCache[$member]['cn']; + } + else { + $members[] = getAbstractDN($member); + } + } + $return[get_class($this) . '_uniqueMember'] = array('' . _('Group members') . '' . implode(', ', $members) . ''); + } + // printer groups + $parentGroups = array(); + $groups = $this->getPrinterGroups(); + foreach ($groups as $group) { + if (!empty($this->printerCache[$group]['cn'])) { + $parentGroups[] = $this->printerCache[$group]['cn']; + } + else { + $parentGroups[] = getAbstractDN($group); + } + } + if (sizeof($parentGroups) > 0) { + $return[get_class($this) . '_parentUniqueMember'] = array('' . _('Printer groups') . '' . implode(', ', $parentGroups) . ''); + } + return $return; + } + + /** + * Returns if the given cn already exists. + * + * @param String $cn cn attribute value + * @return boolean cn exists + */ + private function cnExists($cn) { + if ($this->printerCache == null) { + $this->loadPrinterNameCache(); + } + foreach ($this->printerCache as $dn => $attrs) { + if (!empty($attrs['cn']) && ($attrs['cn'] == $cn)) { + return true; + } + } + return false; + } + + /** + * Loads the list of printer names into the cache. + */ + private function loadPrinterNameCache() { + if ($this->printerCache != null) { + return; + } + $results = searchLDAPByFilter('(objectClass=pykotaPrinter)', array('cn', 'dn', 'description'), array($this->get_scope())); + $this->printerCache = array(); + foreach ($results as $result) { + if (isset($result['cn'][0])) { + $this->printerCache[$result['dn']]['cn'] = $result['cn'][0]; + } + if (isset($result['description'][0])) { + $this->printerCache[$result['dn']]['description'] = $result['description'][0]; + } + } + } + + /** + * Returns the printer group memberships. + * + * @return array DNs of parent groups + */ + private function getPrinterGroups() { + if ($this->groupCache != null) { + return $this->groupCache; + } + $results = searchLDAPByFilter('(&(objectClass=pykotaPrinter)(uniqueMember=' . $this->getAccountContainer()->dn_orig . '))', array('dn'), array($this->get_scope())); + $this->groupCache = array(); + foreach ($results as $result) { + $this->groupCache[] = $result['dn']; + } + return $this->groupCache; + } + +} + + +?> diff --git a/lam/lib/modules/pykotaUser.inc b/lam/lib/modules/pykotaUser.inc new file mode 100644 index 00000000..ae8309ff --- /dev/null +++ b/lam/lib/modules/pykotaUser.inc @@ -0,0 +1,1213 @@ + value */ + private $limitOptions; + + /** + * Returns if this module also manages the structural object class pykotaObject. + * This is overridden by a submodule that must provide the structural object class. + * + * @return boolean structural usage + */ + public function isStructural() { + return false; + } + + /** + * Creates a new pykotaUser object. + * + * @param string $scope account type (user, group, host) + */ + function __construct($scope) { + $this->limitOptions = array( + _('Quota') => 'quota', + _('Balance') => 'balance', + _('No quota') => 'noquota', + _('Free printing') => 'nochange', + _('Deny printing') => 'noprint', + ); + // call parent constructor + parent::__construct($scope); + $this->autoAddObjectClasses = $this->isStructural(); + } + + /** + * Returns meta data that is interpreted by parent class + * + * @return array array with meta data + * + * @see baseModule::get_metaData() + */ + function get_metaData() { + $return = array(); + // icon + $return['icon'] = 'printerBig.png'; + // manages host accounts + $return["account_types"] = array('user'); + // alias name + $return["alias"] = _("PyKota"); + // this is a base module + $return["is_base"] = $this->isStructural(); + // LDAP filter + $return["ldap_filter"] = array('or' => "(objectClass=pykotaAccount)"); + // module dependencies + $return['dependencies'] = array('depends' => array(), 'conflicts' => array()); + // managed object classes + $return['objectClasses'] = array('pykotaAccount', 'pykotaAccountBalance'); + // managed attributes + $return['attributes'] = array('uid', 'mail', 'description', 'pykotaLimitBy', 'pykotaUserName', 'pykotaBalance', 'pykotaLifeTimePaid', + 'pykotaOverCharge', 'pykotaPayments'); + // help Entries + $return['help'] = array( + 'cn' => array( + "Headline" => _("Common name"), 'attr' => 'cn', + "Text" => _("This is the natural name of the user.") + ), + 'uid' => array( + "Headline" => _("User name"), 'attr' => 'uid', + "Text" => _("User name of the user who should be created. Valid characters are: a-z,A-Z,0-9, @.-_.") + ), + 'description' => array ( + "Headline" => _("Description"), 'attr' => 'description', + "Text" => _("User description.") + ), + 'pykotaUserName' => array( + "Headline" => _("PyKota user name"), 'attr' => 'pykotaUserName', + "Text" => _("User name that is used for PyKota.") + ), + 'pykotaLimitBy' => array( + "Headline" => _("Limit type"), 'attr' => 'pykotaLimitBy', + "Text" => _("Specifies the type of limit for printing if any. Please note that in contrast to \"Free printing\" the option \"No quota\" includes accounting.") + ), + 'pykotaBalance' => array( + "Headline" => _('Balance'), 'attr' => 'pykotaBalance', + "Text" => _('Current account balance for the user.') + ), + 'pykotaBalanceComment' => array( + "Headline" => _('Balance comment'), 'attr' => 'pykotaPayments', + "Text" => _('Comment for initial balance.') + ), + 'pykotaLifeTimePaid' => array( + "Headline" => _('Total paid'), 'attr' => 'pykotaLifeTimePaid', + "Text" => _('Total money paid by the user.') + ), + 'pykotaOverCharge' => array( + "Headline" => _('Overcharge factor'), 'attr' => 'pykotaOverCharge', + "Text" => _('Overcharge factor that is applied when computing the cost of a print job. The number of pages is not changed.') + ), + 'pykotaPayments' => array( + "Headline" => _('Payment and job history'), 'attr' => 'pykotaPayments', + "Text" => _('Payment and job history for this user.') + ), + 'pykotaPaymentsAdd' => array( + "Headline" => _('Payment'), 'attr' => 'pykotaBalance, pykotaPayments', + "Text" => _('Adds the amount to the user\'s balance. You can also specify a comment.') + ), + 'jobSuffix' => array( + "Headline" => _('Job suffix'), + "Text" => _('Please enter the LDAP suffix where the PyKota job entries are stored (configuration option "jobbase").') + ), + 'autoAdd' => array( + "Headline" => _("Automatically add this extension"), + "Text" => _("This will enable the extension automatically if this profile is loaded.") + ), + ); + // profile options + $profileContainer = new htmlTable(); + $pykotaLimitByProfileOption = new htmlTableExtendedSelect('pykotaUser_pykotaLimitBy', $this->limitOptions, array(), _('Limit type'), 'pykotaLimitBy'); + $pykotaLimitByProfileOption->setHasDescriptiveElements(true); + $pykotaLimitByProfileOption->setSortElements(false); + $profileContainer->addElement($pykotaLimitByProfileOption, true); + $profileContainer->addElement(new htmlTableExtendedInputField(_('Balance'), 'pykotaUser_pykotaBalance', '', 'pykotaBalance'), true); + $profileContainer->addElement(new htmlTableExtendedInputField(_('Overcharge factor'), 'pykotaUser_pykotaOverCharge', '', 'pykotaOverCharge'), true); + if (!$this->isStructural()) { + $profileContainer->addElement(new htmlTableExtendedInputCheckbox('pykotaUser_addExt', false, _('Automatically add this extension'), 'autoAdd'), true); + } + $return['profile_options'] = $profileContainer; + $return['profile_mappings']['pykotaUser_pykotaLimitBy'] = 'pykotaLimitBy'; + $return['profile_mappings']['pykotaUser_pykotaOverCharge'] = 'pykotaOverCharge'; + $return['profile_checks']['pykotaUser_pykotaBalance'] = array( + 'type' => 'ext_preg', + 'regex' => 'float', + 'error_message' => $this->messages['pykotaBalance'][0]); + $return['profile_checks']['pykotaUser_pykotaOverCharge'] = array( + 'type' => 'ext_preg', + 'regex' => 'float', + 'error_message' => $this->messages['pykotaOverCharge'][0]); + // upload fields + $return['upload_columns'] = array( + array( + 'name' => 'pykotaUser_pykotaUserName', + 'description' => _('PyKota user name'), + 'help' => 'pykotaUserName', + 'example' => _('smiller'), + 'unique' => true, + ) + ); + if ($this->isStructural()) { + $return['upload_columns'][] = array( + 'name' => 'pykotaUser_cn', + 'description' => _('Common name'), + 'help' => 'cn', + 'example' => _('Steve Miller'), + 'required' => true, + ); + } + if ($this->manageUid()) { + $return['upload_columns'][] = array( + 'name' => 'pykotaUser_uid', + 'description' => _('User name'), + 'help' => 'uid', + 'example' => _('smiller'), + 'required' => true, + 'unique' => true, + ); + } + if ($this->manageMail()) { + $return['upload_columns'][] = array( + 'name' => 'pykotaUser_mail', + 'description' => _('Email address'), + 'help' => 'mail', + 'example' => _('user@company.com'), + ); + } + if ($this->manageDescription()) { + $return['upload_columns'][] = array( + 'name' => 'pykotaUser_description', + 'description' => _('Description'), + 'help' => 'description', + 'example' => _('Temp, contract till December'), + ); + } + $return['upload_columns'][] = array( + 'name' => 'pykotaUser_pykotaLimitBy', + 'description' => _('Limit type'), + 'help' => 'pykotaLimitBy', + 'example' => _('Quota'), + 'default' => _('Quota'), + 'values' => implode(', ', array_keys($this->limitOptions)) + ); + $return['upload_columns'][] = array( + 'name' => 'pykotaUser_pykotaBalance', + 'description' => _('Balance'), + 'help' => 'pykotaBalance', + 'example' => '10.0', + ); + $return['upload_columns'][] = array( + 'name' => 'pykotaUser_pykotaBalanceComment', + 'description' => _('Balance comment'), + 'help' => 'pykotaBalanceComment', + 'example' => _('Initial payment'), + ); + $return['upload_columns'][] = array( + 'name' => 'pykotaUser_pykotaOverCharge', + 'description' => _('Overcharge factor'), + 'help' => 'pykotaOverCharge', + 'example' => '1.0', + 'default' => '1.0' + ); + // available PDF fields + $return['PDF_fields'] = array( + 'pykotaUserName' => _('PyKota user name'), + 'pykotaLimitBy' => _('Limit type'), + 'pykotaBalance' => _('Balance'), + 'pykotaOverCharge' => _('Overcharge factor'), + 'pykotaLifeTimePaid' => _('Total paid'), + 'pykotaPayments' => _('Payment history'), + ); + if ($this->manageUid()) { + $return['PDF_fields']['uid'] = _('User name'); + } + if ($this->isStructural()) { + $return['PDF_fields']['cn'] = _('Common name'); + } + if ($this->manageMail()) { + $return['PDF_fields']['mail'] = _('Email address'); + } + if ($this->manageDescription()) { + $return['PDF_fields']['description'] = _('Description'); + } + // self service + $return['selfServiceFieldSettings'] = array( + 'pykotaBalance' => _('Balance (read-only)'), + 'pykotaLifeTimePaid' => _('Total paid (read-only)'), + 'pykotaPayments' => _('Payment history'), + 'pykotaJobHistory' => _('Job history'), + ); + // self service settings + if (get_class($this) == 'pykotaUser') { + $selfServiceContainer = new htmlTable(); + $selfServiceContainer->addElement(new htmlTableExtendedInputField(_('Job suffix'), 'pykotaUser_jobSuffix', null)); + $selfServiceContainer->addElement(new htmlHelpLink('jobSuffix', get_class($this)), true); + $return['selfServiceSettings'] = $selfServiceContainer; + } + // config options + $configContainer = new htmlTable(); + $configContainer->addElement(new htmlTableExtendedInputField(_('Job suffix'), 'pykotaUser_jobSuffix', '', 'jobSuffix'), true); + $return['config_options']['all'] = $configContainer; + // configuration checks + $return['config_checks']['all']['pykotaUser_jobSuffix'] = array ( + 'type' => 'ext_preg', + 'regex' => 'dn', + 'required' => false, + 'required_message' => $this->messages['jobSuffix'][0], + 'error_message' => $this->messages['jobSuffix'][0]); + return $return; + } + + /** + * This function fills the $messages variable with output messages from this module. + */ + function load_Messages() { + $this->messages['uid'][0] = array('ERROR', _('User name'), _('User name contains invalid characters. Valid characters are: a-z, A-Z, 0-9 and .-_ !')); + $this->messages['uid'][1] = array('ERROR', _('Account %s:') . ' pykotaUser_uid', _('User name contains invalid characters. Valid characters are: a-z, A-Z, 0-9 and .-_ !')); + $this->messages['uid'][2] = array('ERROR', _('User name'), _('User name already exists!')); + $this->messages['uid'][3] = array('ERROR', _('Account %s:') . ' pykotaUser_uid', _('User name already exists!')); + $this->messages['pykotaUserName'][0] = array('ERROR', _('PyKota user name'), _('User name contains invalid characters. Valid characters are: a-z, A-Z, 0-9 and .-_ !')); + $this->messages['pykotaUserName'][1] = array('ERROR', _('Account %s:') . ' pykotaUser_pykotaUserName', _('User name contains invalid characters. Valid characters are: a-z, A-Z, 0-9 and .-_ !')); + $this->messages['pykotaUserName'][2] = array('ERROR', _('PyKota user name'), _('User name already exists!')); + $this->messages['pykotaUserName'][3] = array('ERROR', _('Account %s:') . ' pykotaUser_pykotaUserName', _('User name already exists!')); + $this->messages['mail'][0] = array('ERROR', _('Email address'), _('Please enter a valid email address!')); + $this->messages['mail'][1] = array('ERROR', _('Account %s:') . ' pykotaUser_mail', _('Please enter a valid email address!')); + $this->messages['pykotaLimitBy'][0] = array('ERROR', _('Account %s:') . ' pykotaUser_pykotaLimitBy', _('Please enter a valid limit type.')); + $this->messages['pykotaBalance'][0] = array('ERROR', _('Balance'), _('Please enter a valid number (e.g. "1.5").')); + $this->messages['pykotaBalance'][1] = array('ERROR', _('Account %s:') . ' pykotaUser_pykotaBalance', _('Please enter a valid number (e.g. "1.5").')); + $this->messages['pykotaOverCharge'][0] = array('ERROR', _('Overcharge factor'), _('Please enter a valid number (e.g. "1.5").')); + $this->messages['pykotaOverCharge'][1] = array('ERROR', _('Account %s:') . ' pykotaUser_pykotaOverCharge', _('Please enter a valid number (e.g. "1.5").')); + $this->messages['pykotaPayments'][0] = array('ERROR', _('Payment'), _('Please enter a valid number (e.g. "1.5").')); + $this->messages['jobSuffix'][0] = array('ERROR', _('Job suffix'), _('Please enter a valid job suffix.')); + } + + /** + * Returns the HTML meta data for the main account page. + * + * @return htmlElement HTML meta data + */ + function display_html_attributes() { + $container = new htmlTable(); + if ($this->isStructural() || (isset($this->attributes['objectClass']) && in_array('pykotaAccount', $this->attributes['objectClass']))) { + // uid + if ($this->manageUid()) { + $this->addSimpleInputTextField($container, 'uid', _('User name'), true); + } + else { + // require uid + $uid = $this->getCurrentUserName(); + if (empty($uid)) { + $page = ''; + if ($this->getAccountContainer()->getAccountModule('posixAccount') != null) { + $page = $this->getAccountContainer()->getAccountModule('posixAccount')->get_alias(); + } + elseif ($this->getAccountContainer()->getAccountModule('inetOrgPerson') != null) { + $page = $this->getAccountContainer()->getAccountModule('inetOrgPerson')->get_alias(); + } + $msg = new htmlStatusMessage('INFO', sprintf(_("Please enter an user name on this page: %s"), $page)); + $msg->colspan = 5; + $container->addElement($msg, true); + } + } + // pykotaUserName + $this->addSimpleInputTextField($container, 'pykotaUserName', _('Pykota user name')); + // balance + $pykotaBalance = ''; + if (isset($this->attributes['pykotaBalance'][0])) { + $pykotaBalance = $this->attributes['pykotaBalance'][0]; + } + $container->addElement(new htmlOutputText(_('Balance'))); + $container->addElement(new htmlOutputText($pykotaBalance)); + $container->addElement(new htmlHelpLink('pykotaBalance'), true); + // new payment and balance history + $container->addElement(new htmlOutputText(_('Payment'))); + $newPaymentGroup = new htmlGroup(); + $newPaymentAmount = new htmlInputField('pykotaBalanceAdd', '', '5em'); + $newPaymentAmount->setTitle(_('Amount')); + $newPaymentGroup->addElement($newPaymentAmount); + $newPaymentComment = new htmlInputField('pykotaBalanceComment', '', '20em'); + $newPaymentComment->setTitle(_('Comment')); + $newPaymentGroup->addElement($newPaymentComment); + $newPaymentBtn = new htmlButton('addPayment', _('Add')); + $newPaymentBtn->setIconClass('createButton'); + $newPaymentGroup->addElement($newPaymentBtn); + $container->addElement($newPaymentGroup); + $container->addElement(new htmlHelpLink('pykotaPaymentsAdd'), true); + $container->addElement(new htmlOutputText('')); + $historyGroup = new htmlGroup(); + $historyGroup->addElement(new htmlAccountPageButton(get_class($this), 'payments', 'open', _('Payment history'))); + if (!$this->getAccountContainer()->isNewAccount && !empty($this->moduleSettings['pykotaUser_jobSuffix'][0])) { + $historyGroup->addElement(new htmlSpacer('5px', null)); + $historyGroup->addElement(new htmlAccountPageButton(get_class($this), 'jobs', 'open', _('Job history'))); + } + $container->addElement($historyGroup); + $container->addElement(new htmlHelpLink('pykotaPayments'), true); + $container->addElement(new htmlSpacer(null, '10px'), true); + // limit by + $limitOption = 'quota'; + if (!empty($this->attributes['pykotaLimitBy'][0])) { + $limitOption = $this->attributes['pykotaLimitBy'][0]; + } + $limitSelect = new htmlTableExtendedSelect('pykotaLimitBy', $this->limitOptions, array($limitOption), _('Limit type'), 'pykotaLimitBy'); + $limitSelect->setHasDescriptiveElements(true); + $limitSelect->setSortElements(false); + $container->addElement($limitSelect, true); + // overcharge factor + $this->addSimpleInputTextField($container, 'pykotaOverCharge', _('Overcharge factor')); + // cn + if ($this->isStructural()) { + $this->addSimpleInputTextField($container, 'cn', _('Common name'), true); + } + // mail + if ($this->manageMail()) { + $this->addSimpleInputTextField($container, 'mail', _('Email address')); + } + // description + if ($this->manageDescription()) { + $this->addMultiValueInputTextField($container, 'description', _('Description'), false, null, true); + } + // remove button + if (!$this->isStructural()) { + $container->addElement(new htmlSpacer(null, '20px'), true); + $remButton = new htmlButton('remObjectClass', _('Remove PyKota extension')); + $remButton->colspan = 5; + $container->addElement($remButton); + } + } + else { + // add button + $container->addElement(new htmlButton('addObjectClass', _('Add PyKota extension'))); + } + return $container; + } + + /** + * Processes user input of the primary module page. + * It checks if all input values are correct and updates the associated LDAP attributes. + * + * @return array list of info/error messages + */ + function process_attributes() { + $errors = array(); + if (isset($_POST['addObjectClass'])) { + if (!isset($this->attributes['objectClass'])) { + $this->attributes['objectClass'] = array(); + } + if (!in_array('pykotaAccount', $this->attributes['objectClass'])) { + $this->attributes['objectClass'][] = 'pykotaAccount'; + } + if (!in_array('pykotaAccountBalance', $this->attributes['objectClass'])) { + $this->attributes['objectClass'][] = 'pykotaAccountBalance'; + } + if (!isset($this->attributes['pykotaUserName'][0])) { + $this->attributes['pykotaUserName'][0] = $this->getCurrentUserName(); + } + return $errors; + } + if (isset($_POST['remObjectClass'])) { + $this->attributes['objectClass'] = array_delete(array('pykotaAccount', 'pykotaAccountBalance'), $this->attributes['objectClass']); + $attrs = array('pykotaLimitBy', 'pykotaUserName'); + if ($this->manageDescription()) { + $attrs[] = 'description'; + } + if ($this->manageMail()) { + $attrs[] = 'mail'; + } + if ($this->manageUid()) { + $attrs[] = 'uid'; + } + foreach ($attrs as $name) { + if (isset($this->attributes[$name])) { + unset($this->attributes[$name]); + } + } + return $errors; + } + // skip processing if object class is not set + if (!isset($this->attributes['objectClass']) || !in_array('pykotaAccount', $this->attributes['objectClass'])) { + return $errors; + } + // uid + if ($this->manageUid()) { + if (isset($_POST['uid']) && ($_POST['uid'] != '')) { + if (!get_preg($_POST['uid'], 'username')) { + $errors[] = $this->messages['uid'][0]; + } + else { + $this->attributes['uid'][0] = $_POST['uid']; + if ((!isset($this->orig['uid'][0]) || ($this->attributes['uid'][0] != $this->orig['uid'][0])) + && $this->uidExists($_POST['uid'])) { + $errors[] = $this->messages['uid'][2]; + } + } + } + elseif (isset($this->attributes['uid'][0])) { + unset($this->attributes['uid'][0]); + } + } + // PyKota user name + if (!empty($_POST['pykotaUserName'])) { + if (!get_preg($_POST['pykotaUserName'], 'username')) { + $errors[] = $this->messages['pykotaUserName'][0]; + } + else { + $this->attributes['pykotaUserName'][0] = $_POST['pykotaUserName']; + if ((!isset($this->orig['pykotaUserName'][0]) || ($this->attributes['pykotaUserName'][0] != $this->orig['pykotaUserName'][0])) + && $this->pykotaUserNameExists($_POST['pykotaUserName'])) { + $errors[] = $this->messages['pykotaUserName'][2]; + } + } + } + else { + $this->attributes['pykotaUserName'][0] = $this->getCurrentUserName(); + } + // limit by + $this->attributes['pykotaLimitBy'][0] = $_POST['pykotaLimitBy']; + // cn + if ($this->isStructural()) { + $this->attributes['cn'][0] = $_POST['cn']; + if (empty($this->attributes['cn'][0])) { + $this->attributes['cn'][0] = $_POST['uid']; + } + } + // mail + if ($this->manageMail()) { + $this->attributes['mail'][0] = $_POST['mail']; + if (!empty($_POST['mail']) && !get_preg($_POST['mail'], 'email')) { + $errors[] = $this->messages['mail'][0]; + } + } + // description + if ($this->manageDescription()) { + $this->processMultiValueInputTextField('description', $errors); + } + // overcharge factor + if (!empty($_POST['pykotaOverCharge'])) { + $this->attributes['pykotaOverCharge'][0] = $_POST['pykotaOverCharge']; + } + else { + $this->attributes['pykotaOverCharge'][0] = '1.0'; + } + // add payment + if (isset($_POST['addPayment'])) { + $amount = $_POST['pykotaBalanceAdd']; + if (!empty($amount) && (strpos($amount, ',') !== false)) { + $amount = str_replace(',', '.', $amount); + } + if (!empty($amount) && (strpos($amount, '.') === false)) { + $amount .= '.0'; + } + $comment = $_POST['pykotaBalanceComment']; + if (!empty($comment)) { + $comment = base64_encode($comment); + } + if (empty($amount) || (floatval($amount) == 0.0)) { + $errors[] = $this->messages['pykotaPayments'][0]; + } + else { + $this->attributes['pykotaPayments'][] = date('Y-m-d H:i:s,00', time()) . ' # ' . $amount . ' # ' . $comment; + // new balance + $newBalance = 0.0; + if (!empty($this->attributes['pykotaBalance'][0])) { + $newBalance = floatval($this->attributes['pykotaBalance'][0]); + } + $newBalance += $amount; + $this->attributes['pykotaBalance'][0] = strval($newBalance); + // total paid + $total = 0.0; + if (!empty($this->attributes['pykotaLifeTimePaid'][0])) { + $total = floatval($this->attributes['pykotaLifeTimePaid'][0]); + } + $total += $amount; + $this->attributes['pykotaLifeTimePaid'][0] = strval($total); + } + } + return $errors; + } + + /** + * Returns the HTML meta data for the payments page. + * + * @return htmlElement HTML meta data + */ + function display_html_payments() { + $container = new htmlTable(); + // total paid + $total = ''; + if (!empty($this->attributes['pykotaLifeTimePaid'][0])) { + $total = $this->attributes['pykotaLifeTimePaid'][0]; + } + $container->addElement(new htmlOutputText(_('Total paid'))); + $container->addElement(new htmlOutputText($total)); + $container->addElement(new htmlHelpLink('pykotaLifeTimePaid'), true); + // payment/job history + if (!empty($this->attributes['pykotaPayments'][0])) { + $container->addElement(new htmlSubTitle(_('Payment history')), true); + $spacer = new htmlSpacer('10px', null); + $historyTable = new htmlTable(); + $historyTable->colspan = 5; + $historyTable->addElement(new htmlOutputText(_('Date')), false, true); + $historyTable->addElement($spacer); + $historyTable->addElement(new htmlOutputText(_('Amount')), false, true); + $historyTable->addElement($spacer); + $historyTable->addElement(new htmlOutputText(_('Comment')), true, true); + rsort($this->attributes['pykotaPayments']); + foreach ($this->attributes['pykotaPayments'] as $payment) { + $parts = explode(' # ', $payment); + $historyTable->addElement(new htmlOutputText($parts[0])); + $historyTable->addElement($spacer); + $amount = new htmlOutputText($parts[1]); + $amount->alignment = htmlElement::ALIGN_RIGHT; + $historyTable->addElement($amount); + $historyTable->addElement($spacer); + if (!empty($parts[2])) { + $historyTable->addElement(new htmlOutputText(base64_decode($parts[2]))); + } + $historyTable->addNewLine(); + } + $container->addElement($historyTable, true); + } + // back button + $container->addElement(new htmlSpacer(null, '20px'), true); + $backButton = new htmlAccountPageButton(get_class($this), 'attributes', 'back', _('Back')); + $backButton->colspan = 5; + $backButton->alignment = htmlElement::ALIGN_LEFT; + $container->addElement($backButton, true); + return $container; + } + + /** + * Processes user input of the payments module page. + * It checks if all input values are correct and updates the associated LDAP attributes. + * + * @return array list of info/error messages + */ + function process_payments() { + return array(); + } + + /** + * Returns the HTML meta data for the jobs page. + * + * @return htmlElement HTML meta data + */ + function display_html_jobs() { + $container = new htmlTable(); + // jobs + $jobs = $this->getJobs($this->getCurrentUserName(), $this->moduleSettings['pykotaUser_jobSuffix'][0]); + $spacer = new htmlSpacer('10px', null); + // header + $container->addElement(new htmlOutputText(_('Date')), false, true); + $container->addElement($spacer); + $container->addElement(new htmlOutputText(_('Printer')), false, true); + $container->addElement($spacer); + $container->addElement(new htmlOutputText(_('Price')), false, true); + $container->addElement($spacer); + $container->addElement(new htmlOutputText(_('Size')), false, true); + $container->addElement($spacer); + $title = new htmlOutputText(_('Title')); + $title->alignment = htmlElement::ALIGN_LEFT; + $container->addElement($title, true, true); + // jobs + foreach ($jobs as $job) { + $container->addElement(new htmlOutputText(formatLDAPTimestamp($job['createtimestamp'][0]))); + $container->addElement($spacer); + $container->addElement(new htmlOutputText($job['pykotaprintername'][0])); + $container->addElement($spacer); + $price = new htmlOutputText($job['pykotajobprice'][0]); + $price->alignment = htmlElement::ALIGN_RIGHT; + $container->addElement($price); + $container->addElement($spacer); + $size = new htmlOutputText($job['pykotajobsize'][0]); + $size->alignment = htmlElement::ALIGN_RIGHT; + $container->addElement($size); + $container->addElement($spacer); + $container->addElement(new htmlOutputText($job['pykotatitle'][0]), true); + } + // back button + $container->addElement(new htmlSpacer(null, '20px'), true); + $backButton = new htmlAccountPageButton(get_class($this), 'attributes', 'back', _('Back')); + $backButton->colspan = 5; + $backButton->alignment = htmlElement::ALIGN_LEFT; + $container->addElement($backButton, true); + return $container; + } + + /** + * Processes user input of the jobs module page. + * It checks if all input values are correct and updates the associated LDAP attributes. + * + * @return array list of info/error messages + */ + function process_jobs() { + return array(); + } + + /** + * Returns a list of modifications which have to be made to the LDAP account. + * + * @return array list of modifications + *
This function returns an array with 3 entries: + *
array( DN1 ('add' => array($attr), 'remove' => array($attr), 'modify' => array($attr)), DN2 .... ) + *
DN is the DN to change. It may be possible to change several DNs (e.g. create a new user and add him to some groups via attribute memberUid) + *
"add" are attributes which have to be added to LDAP entry + *
"remove" are attributes which have to be removed from LDAP entry + *
"modify" are attributes which have to been modified in LDAP entry + *
"info" are values with informational value (e.g. to be used later by pre/postModify actions) + */ + function save_attributes() { + if (!in_array('pykotaAccount', $this->attributes['objectClass']) && !in_array('pykotaAccount', $this->orig['objectClass'])) { + // skip saving if the extension was not added/modified + return array(); + } + return parent::save_attributes(); + } + + /** + * This function is used to check if all settings for this module have been made. + * + * Calling this method requires the existence of an enclosing {@link accountContainer}.
+ *
+ * This function tells LAM if it can create/modify the LDAP account. If your module needs any + * additional input then set this to false. The user will be notified that your module needs + * more input.
+ * This method's return value defaults to true. + * + * @return boolean true, if settings are complete + */ + public function module_complete() { + if (in_array('pykotaAccount', $this->attributes['objectClass'])) { + // require uid + $uid = $this->getCurrentUserName(); + return !empty($uid); + } + return true; + } + + /** + * Loads the values of an account profile into internal variables. + * + * @param array $profile hash array with profile values (identifier => value) + */ + function load_profile($profile) { + // profile mappings in meta data + parent::load_profile($profile); + // add extension + if (isset($profile['pykotaUser_addExt'][0]) && ($profile['pykotaUser_addExt'][0] == "true")) { + if (!in_array('pykotaAccount', $this->attributes['objectClass'])) { + $this->attributes['objectClass'][] = 'pykotaAccount'; + } + if (!in_array('pykotaAccountBalance', $this->attributes['objectClass'])) { + $this->attributes['objectClass'][] = 'pykotaAccountBalance'; + } + } + if (!empty($profile['pykotaUser_pykotaBalance']) && empty($this->attributes['pykotaBalance'][0])) { + $amount = $profile['pykotaUser_pykotaBalance'][0]; + if (strpos($amount, '.') === false) { + $amount .= '.0'; + } + $this->attributes['pykotaBalance'][0] = $amount; + $this->attributes['pykotaLifeTimePaid'][0] = $amount; + $this->attributes['pykotaPayments'][] = date('Y-m-d H:i:s,00', time()) . ' # ' . $amount . ' # '; + } + } + + /** + * In this function the LDAP account is built up. + * + * @param array $rawAccounts list of hash arrays (name => value) from user input + * @param array $ids list of IDs for column position (e.g. "posixAccount_uid" => 5) + * @param array $partialAccounts list of hash arrays (name => value) which are later added to LDAP + * @param array $selectedModules list of selected account modules + * @return array list of error messages if any + */ + function build_uploadAccounts($rawAccounts, $ids, &$partialAccounts, $selectedModules) { + $messages = array(); + for ($i = 0; $i < sizeof($rawAccounts); $i++) { + // add object classes + if (!in_array('pykotaAccount', $partialAccounts[$i]['objectClass'])) { + $partialAccounts[$i]['objectClass'][] = 'pykotaAccount'; + } + if (!in_array('pykotaAccountBalance', $partialAccounts[$i]['objectClass'])) { + $partialAccounts[$i]['objectClass'][] = 'pykotaAccountBalance'; + } + if ($this->isStructural() && !in_array('pykotaObject', $partialAccounts[$i]['objectClass'])) { + $partialAccounts[$i]['objectClass'][] = 'pykotaObject'; + } + // cn + if ($this->isStructural()) { + $partialAccounts[$i]['cn'] = $rawAccounts[$i][$ids['pykotaUser_cn']]; + } + // uid + if ($this->manageUid() && !empty($rawAccounts[$i][$ids['pykotaUser_uid']])) { + if (!get_preg($rawAccounts[$i][$ids['pykotaUser_uid']], 'username')) { + $errMsg = $this->messages['uid'][1]; + array_push($errMsg, array($i)); + $messages[] = $errMsg; + } + elseif ($this->uidExists($rawAccounts[$i][$ids['pykotaUser_uid']])) { + $errMsg = $this->messages['uid'][3]; + array_push($errMsg, array($i)); + $messages[] = $errMsg; + } + else { + $partialAccounts[$i]['uid'] = $rawAccounts[$i][$ids['pykotaUser_uid']]; + } + } + // mail + if ($this->manageUid() && !empty($rawAccounts[$i][$ids['pykotaUser_mail']])) { + if (!get_preg($rawAccounts[$i][$ids['pykotaUser_mail']], 'email')) { + $errMsg = $this->messages['mail'][1]; + array_push($errMsg, array($i)); + $messages[] = $errMsg; + } + else { + $partialAccounts[$i]['mail'] = $rawAccounts[$i][$ids['pykotaUser_mail']]; + } + } + // description + if ($this->manageDescription() && !empty($rawAccounts[$i][$ids['pykotaUser_description']])) { + $partialAccounts[$i]['description'] = $rawAccounts[$i][$ids['pykotaUser_description']]; + } + // PyKota user name + if (!empty($rawAccounts[$i][$ids['pykotaUser_pykotaUserName']])) { + if (!get_preg($rawAccounts[$i][$ids['pykotaUser_pykotaUserName']], 'username')) { + $errMsg = $this->messages['pykotaUserName'][1]; + array_push($errMsg, array($i)); + $messages[] = $errMsg; + } + elseif ($this->pykotaUserNameExists($rawAccounts[$i][$ids['pykotaUser_pykotaUserName']])) { + $errMsg = $this->messages['pykotaUserName'][3]; + array_push($errMsg, array($i)); + $messages[] = $errMsg; + } + else { + $partialAccounts[$i]['pykotaUserName'] = $rawAccounts[$i][$ids['pykotaUser_pykotaUserName']]; + } + } + // limit by + if (!empty($rawAccounts[$i][$ids['pykotaUser_pykotaLimitBy']])) { + if (isset($this->limitOptions[$rawAccounts[$i][$ids['pykotaUser_pykotaLimitBy']]])) { + $partialAccounts[$i]['pykotaLimitBy'] = $this->limitOptions[$rawAccounts[$i][$ids['pykotaUser_pykotaLimitBy']]]; + } + else { + $errMsg = $this->messages['pykotaLimitBy'][0]; + array_push($errMsg, array($i)); + $messages[] = $errMsg; + } + } + else { + $partialAccounts[$i]['pykotaLimitBy'] = 'quota'; + } + // overcharge factor + if (!empty($rawAccounts[$i][$ids['pykotaUser_pykotaOverCharge']])) { + $pykotaOverCharge = $rawAccounts[$i][$ids['pykotaUser_pykotaOverCharge']]; + if (strpos($pykotaOverCharge, '.') === false) { + $pykotaOverCharge .= '.0'; + } + if (!get_preg($pykotaOverCharge, 'float')) { + $errMsg = $this->messages['pykotaOverCharge'][1]; + array_push($errMsg, array($i)); + $messages[] = $errMsg; + } + else { + $partialAccounts[$i]['pykotaOverCharge'] = $pykotaOverCharge; + } + } + else { + $partialAccounts[$i]['pykotaOverCharge'] = '1.0'; + } + // balance + if (!empty($rawAccounts[$i][$ids['pykotaUser_pykotaBalance']])) { + $balance = $rawAccounts[$i][$ids['pykotaUser_pykotaBalance']]; + if (strpos($balance, '.') === false) { + $balance .= '.0'; + } + if (!get_preg($balance, 'float')) { + $errMsg = $this->messages['pykotaBalance'][1]; + array_push($errMsg, array($i)); + $messages[] = $errMsg; + } + else { + $partialAccounts[$i]['pykotaBalance'] = $balance; + $partialAccounts[$i]['pykotaLifeTimePaid'] = $balance; + $comment = ''; + if (!empty($rawAccounts[$i][$ids['pykotaUser_pykotaBalanceComment']])) { + $comment = base64_encode($rawAccounts[$i][$ids['pykotaUser_pykotaBalanceComment']]); + } + $partialAccounts[$i]['pykotaPayments'][0] = date('Y-m-d H:i:s,00', time()) . ' # ' . $balance . ' # ' . $comment; + } + } + } + return $messages; + } + + /** + * Returns a list of PDF entries + */ + function get_pdfEntries() { + $return = array(); + $this->addSimplePDFField($return, 'cn', _('Common name')); + $this->addSimplePDFField($return, 'uid', _('User name')); + $this->addSimplePDFField($return, 'mail', _('Email address')); + $this->addSimplePDFField($return, 'pykotaUserName', _('PyKota user name')); + $this->addSimplePDFField($return, 'description', _('Description')); + $limitByOptions = array_flip($this->limitOptions); + $limitByValue = ''; + if (!empty($this->attributes['pykotaLimitBy'][0]) && isset($limitByOptions[$this->attributes['pykotaLimitBy'][0]])) { + $limitByValue = $limitByOptions[$this->attributes['pykotaLimitBy'][0]]; + } + $return[get_class($this) . '_pykotaLimitBy'] = array('' . _('Limit type') . '' . $limitByValue . ''); + $this->addSimplePDFField($return, 'pykotaOverCharge', _('Overcharge factor')); + $this->addSimplePDFField($return, 'pykotaBalance', _('Balance')); + $this->addSimplePDFField($return, 'pykotaLifeTimePaid', _('Total paid')); + // payment history + if (!empty($this->attributes['pykotaPayments'][0])) { + $history[] = '' . + '' . _('Date') . '' . + '' . _('Amount') . '' . + '' . _('Comment') . ''; + for ($i = 0; $i < sizeof($this->attributes['pykotaPayments']); $i++) { + $parts = explode(' # ', $this->attributes['pykotaPayments'][$i]); + $comment = ' '; + if (!empty($parts[2])) { + $comment = base64_decode($parts[2]); + } + $history[] = '' . + '' . $parts[0] . '' . + '' . $parts[1] . '' . + '' . $comment . ''; + } + $return[get_class($this) . '_pykotaPayments'] = $history; + } + return $return; + } + + /** + * Returns the meta HTML code for each input field. + * format: array( => array(), ...) + * It is not possible to display help links. + * + * @param array $fields list of active fields + * @param array $attributes attributes of LDAP account + * @param boolean $passwordChangeOnly indicates that the user is only allowed to change his password and no LDAP content is readable + * @param array $readOnlyFields list of read-only fields + * @return array list of meta HTML elements (field name => htmlTableRow) + */ + function getSelfServiceOptions($fields, $attributes, $passwordChangeOnly, $readOnlyFields) { + if ($passwordChangeOnly) { + return array(); // no Kolab fields as long no LDAP content can be read + } + if (!in_array('pykotaAccount', $attributes['objectClass'])) { + return array(); + } + $return = array(); + if (in_array('pykotaBalance', $fields)) { + $pykotaBalance = ''; + if (isset($attributes['pykotaBalance'][0])) $pykotaBalance = $attributes['pykotaBalance'][0]; + $return['pykotaBalance'] = new htmlTableRow(array( + new htmlOutputText(_('Balance')), new htmlOutputText($pykotaBalance) + )); + } + if (in_array('pykotaLifeTimePaid', $fields)) { + $pykotaLifeTimePaid = ''; + if (isset($attributes['pykotaLifeTimePaid'][0])) $pykotaLifeTimePaid = $attributes['pykotaLifeTimePaid'][0]; + $return['pykotaLifeTimePaid'] = new htmlTableRow(array( + new htmlOutputText(_('Total paid')), new htmlOutputText($pykotaLifeTimePaid) + )); + } + // payment history + if (in_array('pykotaPayments', $fields)) { + $pykotaPayments = new htmlTable(); + $pykotaPayments->colspan = 5; + if (!empty($attributes['pykotaPayments'][0])) { + $spacer = new htmlSpacer('10px', null); + $pykotaPayments->addElement(new htmlOutputText(_('Date')), false, true); + $pykotaPayments->addElement($spacer); + $pykotaPayments->addElement(new htmlOutputText(_('Amount')), false, true); + $pykotaPayments->addElement($spacer); + $pykotaPayments->addElement(new htmlOutputText(_('Comment')), true, true); + rsort($attributes['pykotaPayments']); + foreach ($attributes['pykotaPayments'] as $payment) { + $parts = explode(' # ', $payment); + $pykotaPayments->addElement(new htmlOutputText($parts[0])); + $pykotaPayments->addElement($spacer); + $amount = new htmlOutputText($parts[1]); + $amount->alignment = htmlElement::ALIGN_RIGHT; + $pykotaPayments->addElement($amount); + $pykotaPayments->addElement($spacer); + if (!empty($parts[2])) { + $pykotaPayments->addElement(new htmlOutputText(base64_decode($parts[2]))); + } + $pykotaPayments->addNewLine(); + } + } + $pykotaPaymentsLabel = new htmlOutputText(_('Payment history')); + $pykotaPaymentsLabel->alignment = htmlElement::ALIGN_TOP; + $return['pykotaPayments'] = new htmlTableRow(array( + $pykotaPaymentsLabel, $pykotaPayments + )); + } + // job history + if (in_array('pykotaJobHistory', $fields) && !empty($this->selfServiceSettings->moduleSettings['pykotaUser_jobSuffix'][0]) && !empty($attributes['pykotaUserName'][0])) { + $jobs = $this->getJobs($attributes['pykotaUserName'][0], $this->selfServiceSettings->moduleSettings['pykotaUser_jobSuffix'][0]); + $pykotaJobs = new htmlTable(); + $pykotaJobs->colspan = 5; + $spacer = new htmlSpacer('10px', null); + $pykotaJobs->addElement(new htmlOutputText(_('Date')), false, true); + $pykotaJobs->addElement($spacer); + $pykotaJobs->addElement(new htmlOutputText(_('Printer')), false, true); + $pykotaJobs->addElement($spacer); + $pykotaJobs->addElement(new htmlOutputText(_('Price')), false, true); + $pykotaJobs->addElement($spacer); + $pykotaJobs->addElement(new htmlOutputText(_('Size')), false, true); + $pykotaJobs->addElement($spacer); + $title = new htmlOutputText(_('Title')); + $title->alignment = htmlElement::ALIGN_LEFT; + $pykotaJobs->addElement($title, true, true); + foreach ($jobs as $job) { + $pykotaJobs->addElement(new htmlOutputText(formatLDAPTimestamp($job['createtimestamp'][0]))); + $pykotaJobs->addElement($spacer); + $pykotaJobs->addElement(new htmlOutputText($job['pykotaprintername'][0])); + $pykotaJobs->addElement($spacer); + $price = new htmlOutputText($job['pykotajobprice'][0]); + $price->alignment = htmlElement::ALIGN_RIGHT; + $pykotaJobs->addElement($price); + $pykotaJobs->addElement($spacer); + $size = new htmlOutputText($job['pykotajobsize'][0]); + $size->alignment = htmlElement::ALIGN_RIGHT; + $pykotaJobs->addElement($size); + $pykotaJobs->addElement($spacer); + $pykotaJobs->addElement(new htmlOutputText($job['pykotatitle'][0]), true); + } + $pykotaJobsLabel = new htmlOutputText(_('Job history')); + $pykotaJobsLabel->alignment = htmlElement::ALIGN_TOP; + $return['pykotaJobHistory'] = new htmlTableRow(array( + $pykotaJobsLabel, $pykotaJobs + )); + } + return $return; + } + + /** + * Checks if the self service settings are valid. + * + * Calling this method does not require the existence of an enclosing {@link accountContainer}.
+ *
+ * If the input data is invalid the return value is an array that contains arrays + * to build StatusMessages (message type, message head, message text). If no errors + * occured the function returns an empty array. + * + * @param array $options hash array (option name => value) that contains the input. The option values are all arrays containing one or more elements. + * @param selfServiceProfile $profile self service profile + * @return array error messages + */ + public function checkSelfServiceSettings(&$options, &$profile) { + $errors = array(); + if (get_class($this) == 'pykotaUser') { + if (!empty($options['pykotaUser_jobSuffix'][0]) && !get_preg($options['pykotaUser_jobSuffix'][0], 'dn')) { + $errors[] = $this->messages['jobSuffix'][0]; + } + } + return $errors; + } + + /** + * Returns if the uid attribute should be managed. + * + * @return boolean manage uid attribute + */ + private function manageUid() { + if (isset($_SESSION['config'])) { + $conf = $_SESSION['config']; + if (in_array('inetOrgPerson', $conf->get_AccountModules($this->get_scope())) + || in_array('posixAccount', $conf->get_AccountModules($this->get_scope()))) { + return false; + } + else { + return true; + } + } + return false; + } + + /** + * Returns if the mail attribute should be managed. + * + * @return boolean manage mail attribute + */ + private function manageMail() { + if (isset($_SESSION['config'])) { + $conf = $_SESSION['config']; + if (in_array('inetOrgPerson', $conf->get_AccountModules($this->get_scope()))) { + return false; + } + else { + return true; + } + } + return false; + } + + /** + * Returns if the description attribute should be managed. + * + * @return boolean manage description attribute + */ + private function manageDescription() { + if (isset($_SESSION['config'])) { + $conf = $_SESSION['config']; + if (in_array('inetOrgPerson', $conf->get_AccountModules($this->get_scope()))) { + return false; + } + else { + return true; + } + } + return false; + } + + /** + * Returns if the given uid already exists. + * + * @param String $uid uid attribute value + * @return boolean uid exists + */ + private function uidExists($uid) { + if ($this->uidCache == null) { + $this->loadUserNameCache(); + } + return in_array($uid, $this->uidCache); + } + + /** + * Returns if the given pykotaUserName already exists. + * + * @param String $pykotaUserName pykotaUserName attribute value + * @return boolean pykotaUserName exists + */ + private function pykotaUserNameExists($pykotaUserName) { + if ($this->pykotaUserNameCache == null) { + $this->loadUserNameCache(); + } + return in_array($pykotaUserName, $this->pykotaUserNameCache); + } + + /** + * Loads the list of user names into the cache. + */ + private function loadUserNameCache() { + $results = searchLDAPByFilter('(|(uid=*)(pykotaUserName=*))', array('uid', 'pykotaUserName'), array($this->get_scope())); + $this->uidCache = array(); + $this->pykotaUserNameCache = array(); + foreach ($results as $result) { + if (isset($result['uid'][0])) { + $this->uidCache[] = $result['uid'][0]; + } + if (isset($result['pykotausername'][0])) { + $this->pykotaUserNameCache[] = $result['pykotausername'][0]; + } + } + } + + /** + * Returns the current user name (uid) of this account. + * + * @return String user name + */ + private function getCurrentUserName() { + if (!empty($this->attributes['uid'][0])) { + return $this->attributes['uid'][0]; + } + if ($this->getAccountContainer()->getAccountModule('posixAccount') != null) { + $posix = $this->getAccountContainer()->getAccountModule('posixAccount'); + $attrs = $posix->getAttributes(); + if (!empty($attrs['uid'][0])) { + return $attrs['uid'][0]; + } + } + if ($this->getAccountContainer()->getAccountModule('inetOrgPerson') != null) { + $personal = $this->getAccountContainer()->getAccountModule('inetOrgPerson'); + $attrs = $personal->getAttributes(); + if (!empty($attrs['uid'][0])) { + return $attrs['uid'][0]; + } + } + return ''; + } + + /** + * Returns a list of jobs for the given user reverse-sorted by date. + * The number of jobs is limited to 100. + * + * @param String $user user name + * @param String $suffix LDAP suffix for job objects + */ + private function getJobs($user, $suffix) { + $attrs = array('createTimestamp', 'pykotaTitle', 'pykotaPrinterName', 'pykotaJobPrice', 'pykotaJobSize'); + if (isset($_SESSION['ldapHandle']) && is_resource($_SESSION['ldapHandle'])) { + $handle = $_SESSION['ldapHandle']; + } + else { + $handle = $_SESSION['ldap']->server(); + } + $sr = @ldap_search($handle, $suffix, '(&(objectClass=pykotaJob)(pykotaUserName=' . $user . '))', $attrs); + if (!$sr) { + return array(); + } + $jobList = ldap_get_entries($handle, $sr); + if (!$jobList) { + return array(); + } + cleanLDAPResult($jobList); + @ldap_free_result($sr); + $jobs = array(); + foreach ($jobList as $index => $job) { + $jobs[$job['createtimestamp'][0] . $index] = $job; + } + krsort($jobs); + if (sizeof($jobs) > 100) { + $jobs = array_slice($jobs, 0, 100); + } + return array_values($jobs); + } + +} + + +?> diff --git a/lam/lib/modules/pykotaUserStructural.inc b/lam/lib/modules/pykotaUserStructural.inc new file mode 100644 index 00000000..17c9e7a4 --- /dev/null +++ b/lam/lib/modules/pykotaUserStructural.inc @@ -0,0 +1,70 @@ + array(), 'conflicts' => array('pykotaUser')); + // additional object class and attribute + $return['objectClasses'][] = 'pykotaObject'; + $return['attributes'][] = 'cn'; + // RDN attribute + $return["RDN"] = array("cn" => "normal", 'uid' => 'low'); + return $return; + } + +} diff --git a/lam/lib/types/pykotaBillingCodeType.inc b/lam/lib/types/pykotaBillingCodeType.inc new file mode 100644 index 00000000..d7555c2c --- /dev/null +++ b/lam/lib/types/pykotaBillingCodeType.inc @@ -0,0 +1,172 @@ +LABEL_CREATE_ANOTHER_ACCOUNT = _('Create another billing code'); + $this->LABEL_BACK_TO_ACCOUNT_LIST = _('Back to billing code list'); + } + + /** + * Returns the alias name of this account type. + * + * @return string alias name + */ + function getAlias() { + return _("Billing codes"); + } + + /** + * Returns the description of this account type. + * + * @return string description + */ + function getDescription() { + return _("PyKota billing codes"); + } + + /** + * Returns the class name for the list object. + * + * @return string class name + */ + function getListClassName() { + return "lamPykotaBillingCodeTypeList"; + } + + /** + * Returns the default attribute list for this account type. + * + * @return string attribute list + */ + function getDefaultListAttributes() { + return "#cn;#description;#pykotaBalance;#pykotaPageCounter"; + } + + /** + * Returns a list of attributes which have a translated description. + * This is used for the head row in the list view. + * + * @return array list of descriptions + */ + function getListAttributeDescriptions() { + return array ( + "cn" => _('Billing code'), + "description" => _('Description'), + 'pykotaBalance' => _('Balance'), + 'pykotaPageCounter' => _('Page count'), + ); + } + + /** + * Returns the the title text for the title bar on the new/edit page. + * + * @param accountContainer $container account container + * @return String title text + */ + public function getTitleBarTitle($container) { + // get attributes + $attributes = array(); + if ($container->getAccountModule('pykotaBillingCode') != null) { + $attributes = $container->getAccountModule('pykotaBillingCode')->getAttributes(); + } + // check if pykotaBillingCode is set + if (isset($attributes['pykotaBillingCode'][0])) { + return htmlspecialchars($attributes['pykotaBillingCode'][0]); + } + // show new label + if ($container->isNewAccount) { + return _("New billing code"); + } + // fall back to default + return parent::getTitleBarTitle($container); + } + + /** + * Returns the the subtitle text for the title bar on the new/edit page. + * + * @param accountContainer $container account container + * @return String title text + */ + public function getTitleBarSubtitle($container) { + // get attributes + $attributes = array(); + if ($container->getAccountModule('pykotaBillingCode') != null) { + $attributes = $container->getAccountModule('pykotaBillingCode')->getAttributes(); + } + // check if description is set + if (isset($attributes['description'][0])) { + return htmlspecialchars($attributes['description'][0]); + } + // fall back to default + return parent::getTitleBarSubtitle($container); + } + +} + + +/** + * Generates the list view. + * + * @package lists + * @author Roland Gruber + * + */ +class lamPykotaBillingCodeTypeList extends lamList { + + /** + * Constructor + * + * @param string $type account type + * @return lamList list object + */ + function __construct($type) { + parent::__construct($type); + $this->labels = array( + 'nav' => _("Billing code count: %s"), + 'error_noneFound' => _("No billing codes found!"), + 'newEntry' => _("New billing code"), + 'deleteEntry' => _("Delete selected billing codes")); + } + +} + + +?> \ No newline at end of file diff --git a/lam/lib/types/pykotaPrinterType.inc b/lam/lib/types/pykotaPrinterType.inc new file mode 100644 index 00000000..becee5f9 --- /dev/null +++ b/lam/lib/types/pykotaPrinterType.inc @@ -0,0 +1,173 @@ +LABEL_CREATE_ANOTHER_ACCOUNT = _('Create another printer'); + $this->LABEL_BACK_TO_ACCOUNT_LIST = _('Back to printer list'); + } + + /** + * Returns the alias name of this account type. + * + * @return string alias name + */ + function getAlias() { + return _("Printers"); + } + + /** + * Returns the description of this account type. + * + * @return string description + */ + function getDescription() { + return _("PyKota printers"); + } + + /** + * Returns the class name for the list object. + * + * @return string class name + */ + function getListClassName() { + return "lamPykotaPrinterTypeList"; + } + + /** + * Returns the default attribute list for this account type. + * + * @return string attribute list + */ + function getDefaultListAttributes() { + return "#cn;#description;#pykotaPricePerPage;#pykotaPricePerJob;#pykotaMaxJobSize"; + } + + /** + * Returns a list of attributes which have a translated description. + * This is used for the head row in the list view. + * + * @return array list of descriptions + */ + function getListAttributeDescriptions() { + return array ( + "cn" => _('Printer name'), + "description" => _('Description'), + 'pykotaPricePerPage' => _('Price per page'), + 'pykotaPricePerJob' => _('Price per job'), + 'pykotaMaxJobSize' => _('Maximum job size'), + ); + } + + /** + * Returns the the title text for the title bar on the new/edit page. + * + * @param accountContainer $container account container + * @return String title text + */ + public function getTitleBarTitle($container) { + // get attributes + $attributes = array(); + if ($container->getAccountModule('pykotaPrinter') != null) { + $attributes = $container->getAccountModule('pykotaPrinter')->getAttributes(); + } + // check if cn is set + if (isset($attributes['cn'][0])) { + return htmlspecialchars($attributes['cn'][0]); + } + // show new publication label + if ($container->isNewAccount) { + return _("New printer"); + } + // fall back to default + return parent::getTitleBarTitle($container); + } + + /** + * Returns the the subtitle text for the title bar on the new/edit page. + * + * @param accountContainer $container account container + * @return String title text + */ + public function getTitleBarSubtitle($container) { + // get attributes + $attributes = array(); + if ($container->getAccountModule('pykotaPrinter') != null) { + $attributes = $container->getAccountModule('pykotaPrinter')->getAttributes(); + } + // check if description is set + if (isset($attributes['description'][0])) { + return htmlspecialchars($attributes['description'][0]); + } + // fall back to default + return parent::getTitleBarSubtitle($container); + } + +} + + +/** + * Generates the list view. + * + * @package lists + * @author Roland Gruber + * + */ +class lamPykotaPrinterTypeList extends lamList { + + /** + * Constructor + * + * @param string $type account type + * @return lamList list object + */ + function __construct($type) { + parent::__construct($type); + $this->labels = array( + 'nav' => _("Printer count: %s"), + 'error_noneFound' => _("No printers found!"), + 'newEntry' => _("New printer"), + 'deleteEntry' => _("Delete selected printers")); + } + +} + + +?> \ No newline at end of file