/** * Retrieves HTML code of relating elements of initially related or * explicitly selected element. * * @param array $data custom data to be passed to template on rendering * @param string $template name of custom template to use instead of default one on rendering * @param int $listNodeAtIndex index of node to list properties of * (default: node at opposite end of relation) * @return string rendering result */ public function render($data = array(), $template = null, $listNodeAtIndex = -1) { $query = $this->query(); // extend query to fetch all properties of selected node's model $query->addProperty($this->datasource->qualifyDatasetName($this->nodeAtIndex($listNodeAtIndex)->getName()) . '.*'); // process query $matches = $query->execute()->all(); // start variable space initialized using provided set of custom data $data = variable_space::fromArray($data); // add fetched relation instances to variable space $data->update('matches', $matches); // add reference on current relation manager instance $data->update('relation', $this); // render variable space using selected or default template return view::render($template ? $template : 'model/relation/generic', $data); }
/** * Processes input while editing/adding user record. * * @param sql_user $provider provider used on creating new user record * @param int|false $userId ID of user to edit, false/0 on adding new user * @return sql_user|null edited or created user, null if creating user failed */ protected function processInputOnEditing($provider, $userId) { if ($userId) { $user = user::load($userId); $userData = array('id' => $user->getID(), 'loginname' => $user->getLoginName(), 'name' => $user->getName(), 'email' => $user->getProperty('email')); } else { $user = null; $userData = array(); } $form = $this->getForm($userData); if ($form->hasInput()) { if (input::vget('submit') == 'cancel') { txf::redirectTo($this->getUrls()->list); } /* * read in and normalize all provided information on user */ $loginName = $user ? $userData['loginname'] : trim(input::vget('loginname')); $name = trim(input::vget('name')); $email = trim(input::vget('email')); $passwordA = trim(input::vget('password')); $passwordB = trim(input::vget('repeat')); /* * validate all information on user */ if ($loginName === '') { $form->setRowError('loginname', \de\toxa\txf\_L('Provide login name of user!')); } else { if (strlen($loginName) > 64) { $form->setRowError('loginname', \de\toxa\txf\_L('Provided login name is too long!')); } } if ($name && strlen($name) > 128) { $form->setRowError('loginname', \de\toxa\txf\_L('Provided full name is too long!')); } if ($email) { if (strlen($name) > 128) { $form->setRowError('loginname', \de\toxa\txf\_L('Provided mail address is too long!')); } else { if (!\de\toxa\txf\mail::isValidAddress($email)) { $form->setRowError('email', \de\toxa\txf\_L('Provided mail address is invalid!')); } } } // validate optionally provided password if (!$user || $passwordA || $passwordB) { if ($passwordA === '' || $passwordB === '') { if ($user) { $form->setRowError('password', \de\toxa\txf\_l('Provide new password twice for excluding typos.')); } else { $form->setRowError('password', \de\toxa\txf\_l('Provide password of new user and repeat for excluding typos.')); } } else { if ($passwordA !== $passwordB) { $form->setRowError('password', \de\toxa\txf\_L('Doubly entered passwords don\'t match.')); } else { try { if (is_callable($this->passwordValidator)) { call_user_func($this->passwordValidator, $passwordA); } else { $this->passwordValidatorDefault($passwordA); } } catch (\InvalidArgumentException $e) { $form->setRowError('password', $e->getMessage()); } } } } /* * save changes to datasource */ $hasError = $form->hasAnyRowError(); if (!$hasError) { exception::enterSensitive(); if ($user) { try { $user->datasource()->transaction()->wrap(function (datasource\connection $conn) use($user, $name, $email, $passwordA) { $user->setProperty('name', $name); $user->setProperty('email', $email); if (trim($passwordA) !== '') { $user->changePassword($passwordA); if ($user->getUUID() === user::current()->getUUID()) { try { user::current()->authenticate($passwordA); } catch (unauthorized_exception $e) { view::flash(\de\toxa\txf\_L('Updating current session for using changed password failed. Probably you need to login, again.'), 'error'); } } } view::flash(\de\toxa\txf\_L('Successfully changed information on selected user.')); return true; }); } catch (\Exception $e) { $hasError = true; view::flash(\de\toxa\txf\_L('Failed to save information on user in datasource.'), 'error'); } } else { try { $user = $provider->create(array('loginname' => $loginName, 'name' => $name, 'password' => $passwordA, 'email' => $email, 'lock' => '')); view::flash(\de\toxa\txf\_L('Successfully created new user.')); } catch (\Exception $e) { $hasError = true; view::flash(\de\toxa\txf\_L('Failed to create new user record in datasource.'), 'error'); } } exception::leaveSensitive(); } if (!$hasError) { txf::redirectTo($this->getUrls()->list); } } return $user; }
public function performDelete() { if (!user::current()->isAuthenticated()) { throw new http_exception(403); } $this->prepareControl(); try { $this->getSelectedItem()->delete(); } catch (datasource_exception $e) { view::flash(\de\toxa\txf\_L('Failed deleting selected item.'), 'error'); } catch (\RuntimeException $e) { view::flash(\de\toxa\txf\_L('Selected item does not exist (anymore).'), 'error'); } txf::redirectTo($this->getUrls()->list); }
/** * Maps viewports onto regions of page. * * This mapping is supported to improve content/view abstraction by enabling * content of viewports being assembled into code of page's regions in a * configurable way ... * * @param array $viewports content of viewports * @return array content of regions */ protected function collectRegions($viewports) { $configs = array(config::getList('view.region'), array(array('name' => 'main', 'viewport' => array('flash', 'title', 'error', 'main')), array('name' => 'head', 'viewport' => array('header')), array('name' => 'foot', 'viewport' => array('footer', 'debug')), array('name' => 'left', 'viewport' => array('navigation')), array('name' => 'right', 'viewport' => array('aside')))); $regions = array(); foreach ($configs as $config) { if (is_array($config)) { foreach ($config as $region) { // get name of region to customize $name = trim(@$region['name']); if ($name === '') { log::debug('ignoring nameless region configuration'); continue; } if (!array_key_exists($name, $regions)) { // region haven't been collected before ... if (array_key_exists('code', $region)) { // there is a line of code containing markers selecting viewports to collect their content in current region // e.g. "{{title}}<some-literal-content-to-insert/>{{main}}" $regions[$name] = data::qualifyString($region['code'], $viewports); } else { if (is_array(@$region['viewport'])) { // collect set of viewports named in configuration foreach (@$region['viewport'] as $viewportName) { $regions[$name] .= \de\toxa\txf\view::wrapNotEmpty(@$viewports[$viewportName], config::get('view.viewport.wrap.' . $viewportName, '')); } } } // support default content to show if a region keeps empty finally if (trim($regions[$name]) === '') { $regions[$name] = trim(@$region['default']); } // process any additionally contained markers $regions[$name] = data::qualifyString($regions[$name]); } } } } return $regions; }
/** * Processes input on current editor. * * @param callable $validatorCallback * @return bool|string false on input failures requiring user action, * "saved" on input successfully saved to data source, * "cancel" on user pressing cancel button, * "delete" on user deleting record */ public function processInput($validatorCallback = null) { if ($this->hasInput()) { switch (input::vget('_cmd')) { case 'cancel': // permit closing editor due to user requesting to cancel editing return 'cancel'; case 'delete': // delete current edited item if ($this->may['delete'] && $this->item) { $ctx = $this; $item = $this->item; $fields = $this->fields; $this->datasource->transaction()->wrap(function () use($ctx, $item, $fields) { foreach ($fields as $field) { /** @var model_editor_field $field */ $field->type()->onDeleting($ctx, $item, $field); } $item->delete(); return true; }); $this->item = null; return 'delete'; } view::flash(\de\toxa\txf\_L('You must not delete this item.'), 'error'); return false; case 'save': // extract some protected properties from current instance to be used in transaction-wrapped callback $ctx = $this; $class = $this->class; $source = $this->datasource; $fields = $this->fields; $enabled = $this->enabled; $item = $this->item; $fixed = $this->getFixed(); $errors = array(); $this->onCreating = !$this->hasItem(); if (!$this->onCreating && !$this->may['edit']) { view::flash(\de\toxa\txf\_L('You must not edit this item.'), 'error'); return false; } // wrap modification on model in transaction $success = $source->transaction()->wrap(function () use($ctx, $class, $source, $fields, $enabled, $fixed, &$item, &$errors, $validatorCallback) { $properties = array(); foreach ($fields as $property => $definition) { /** @var model_editor_field $definition */ if (!count($enabled) || !@$enabled[$property]) { try { // normalize input $input = call_user_func(array($definition->type(), 'normalize'), $ctx->getValue($property, $definition->isCustom()), $property, $ctx); // validate input $success = call_user_func(array($definition->type(), 'validate'), $input, $property, $ctx); // save input if valid if ($success) { $properties[$property] = $input; } else { $errors[$property] = \de\toxa\txf\_L('Your input is invalid.'); } } catch (\Exception $e) { $errors[$property] = $e->getMessage(); } } } if (count($errors)) { return false; } if (is_callable($validatorCallback)) { // provide opportunity to qualify properties for validation $qualified = $properties; foreach ($fields as $field) { /** @var model_editor_field $field */ $qualified = $field->type()->beforeValidating($ctx, $item, $qualified, $field); } // invoke custom callback given those qualified copy of properties for validating $localErrors = call_user_func($validatorCallback, $qualified, $errors, $item ? $item->id() : null); if ($localErrors === false || is_string($localErrors) || is_array($localErrors) && count($localErrors)) { if (is_array($localErrors)) { $errors = array_merge($errors, $localErrors); } else { if (is_string($localErrors)) { view::flash($localErrors, 'error'); } } return false; } } if ($item) { // on updating item -> don't adjust values of // properties marked as fixed foreach ($fixed as $name => $value) { unset($properties[$name]); } } else { // creating new item -> ensure to use fixed initial // values provided additionally foreach ($fixed as $name => $value) { $properties[$name] = $value; } } // optionally pre-process saving properties of item foreach ($fields as $field) { /** @var model_editor_field $field */ $properties = $field->type()->beforeStoring($ctx, $item, $properties, $field); } if ($item) { // update properties of existing item foreach ($properties as $name => $value) { $item->__set($name, $value); } } else { // create new item $item = $class->getMethod('create')->invoke(null, $source, $properties); // tell all elements to have item now foreach ($fields as $field) { /** @var model_editor_field $field */ $field->type()->onSelectingItem($ctx, $item, $field); } } // optionally post-process saving properties of item foreach ($fields as $field) { /** @var model_editor_field $field */ $item = $field->type()->afterStoring($ctx, $item, $properties, $field); } return true; }); // transfer adjusted properties back to protected scope of current instance $this->errors = $errors; // write back item created or probably replaced by afterStoring() call in transaction $this->item = $item; if ($success) { // permit closing editor after having saved all current input view::flash(\de\toxa\txf\_L('Your changes have been saved.')); return 'saved'; } view::flash(\de\toxa\txf\_L('Failed to save your changes.'), 'error'); } } // don't close editor return false; }
/** * Processes input of widget updating its internal state. * * @return $this current instance */ public function processInput() { if (user::current()->isAuthenticated()) { view::flash(\de\toxa\txf\_L('You are logged in, already.')); $this->redirect(); } $form = $this->getForm(); if ($form->hasInput()) { if (input::vget('submit') == 'cancel') { $this->redirect(); } $username = input::vget('name'); if ($username) { try { user::setCurrent(user::load($username), input::vget('token')); $this->redirect(); } catch (unauthorized_exception $ex) { if ($ex->isAccountLocked()) { if ($this->resendUnlockMailUrl) { view::flash(sprintf(\de\toxa\txf\_L('Your account is locked! <a href="%s">Resend unlock mail now.</a>'), sprintf($this->resendUnlockMailUrl, $ex->getUser()->getID())), 'error'); } else { view::flash(sprintf(\de\toxa\txf\_L('Your account is locked!')), 'error'); } } else { sleep(3); if ($ex->isUserNotFound()) { view::flash(\de\toxa\txf\_L('User does not exist.'), 'error'); } else { view::flash(\de\toxa\txf\_L('Authentication failed.'), 'error'); } } } } else { view::flash(\de\toxa\txf\_L('Provide login name and password!')); } } else { $session =& txf::session(); $referrer = input::vget('referrer'); $session['referrer'] = url::isRelative($referrer) ? $referrer : null; } return $this; }
/** * Processes input of widget updating its internal state. * * @return $this current instance */ public function processInput() { if (!user::current()->isAuthenticated()) { view::flash(\de\toxa\txf\_L('You must be logged in.')); $this->redirect(); } $form = $this->getForm(); if ($form->hasInput()) { if (input::vget('submit') == 'cancel') { $this->redirect(); } $passwordOld = trim(input::vget('old')); $passwordNewA = trim(input::vget('new')); $passwordNewB = trim(input::vget('repeat')); if ($passwordOld === '') { $form->setRowError('old', \de\toxa\txf\_L('Provide current password!')); } if ($passwordNewA === '' || $passwordNewB === '') { $form->setRowError('new', \de\toxa\txf\_l('Provide new password twice for excluding typos.')); } else { if ($passwordNewA !== $passwordNewB) { $form->setRowError('new', \de\toxa\txf\_L('Doubly entered passwords don\'t match.')); } else { try { if (is_callable($this->passwordValidator)) { call_user_func($this->passwordValidator, $passwordNewA); } else { $this->passwordValidatorDefault($passwordNewA); } } catch (\InvalidArgumentException $e) { $form->setRowError('new', $e->getMessage()); } } } exception::enterSensitive(); if (!$form->hasAnyRowError()) { try { $user = user::load(user::current()->getID()); try { $user->authenticate($passwordOld); } catch (unauthorized_exception $e) { $form->setRowError('old', \de\toxa\txf\_L('Authenticating request using old password failed.')); } } catch (unauthorized_exception $e) { $form->setRowError('old', \de\toxa\txf\_L('Current user isn\'t available.')); } } $hasError = false; if (!$form->hasAnyRowError()) { try { user::current()->changePassword($passwordNewA); view::flash(\de\toxa\txf\_L('Password has been changed successfully.')); try { user::current()->authenticate($passwordNewA); } catch (unauthorized_exception $e) { view::flash(\de\toxa\txf\_L('Updating current session for using changed password failed. Probably you need to login, again.'), 'error'); } } catch (\RuntimeException $e) { $hasError = true; view::flash(\de\toxa\txf\_L('Your input is okay, but changing password failed nevertheless.'), 'error'); } } exception::leaveSensitive(); if (!$hasError && !$form->hasAnyRowError()) { $this->redirect(); } } else { $session =& txf::session(); $referrer = input::vget('referrer'); $session['referrer'] = url::isRelative($referrer) ? $referrer : null; } return $this; }
/** * Processes input of widget updating its internal state. * * @return widget current instance */ public function processInput() { if (user::current()->isAuthenticated()) { user::dropCurrent(); } view::flash(\de\toxa\txf\_L('You logged out successfully.')); $referrer = input::vget('referrer'); $referrer = url::isRelative($referrer) ? $referrer : null; txf::redirectTo(\de\toxa\txf\_1($referrer, 'home')); }