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 00000000..cc17b26b
Binary files /dev/null and b/lam/graphics/pykotaBillingCodeType.png differ
diff --git a/lam/graphics/pykotaPrinterType.png b/lam/graphics/pykotaPrinterType.png
new file mode 100644
index 00000000..cc17b26b
Binary files /dev/null and b/lam/graphics/pykotaPrinterType.png differ
diff --git a/lam/lib/modules/pykotaBillingCode.inc b/lam/lib/modules/pykotaBillingCode.inc
new file mode 100644
index 00000000..2148b996
--- /dev/null
+++ b/lam/lib/modules/pykotaBillingCode.inc
@@ -0,0 +1,300 @@
+ 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