diff --git a/.gitignore b/.gitignore index 16f1349e2..d23cf16a8 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ client/html/tests/tmp *.junit.xml *.log *.ser + +.idea +vendor diff --git a/client/html/src/Client/Html/Account/Profile/Account/Standard.php b/client/html/src/Client/Html/Account/Profile/Account/Standard.php new file mode 100644 index 000000000..a4ddd65ec --- /dev/null +++ b/client/html/src/Client/Html/Account/Profile/Account/Standard.php @@ -0,0 +1,264 @@ +/subparts = array( "subclient1", "subclient2" ) + * + * You can also remove one or more parts if they shouldn't be rendered: + * + * client/html//subparts = array( "subclient1" ) + * + * As the clients only generates structural HTML, the layout defined via CSS + * should support adding, removing or readdressing content by a fluid like + * design. + * + * @param array List of sub-client names + * @since 2019.07 + * @category Developer + */ + private $subPartPath = 'client/html/account/profile/account/subparts'; + private $subPartNames = []; + + /** + * Returns the list of sub-client names configured for the client. + * + * @return array List of HTML client names + */ + protected function getSubClientNames() : array + { + return $this->getContext()->getConfig()->get( $this->subPartPath, $this->subPartNames ); + } + + /** + * Returns the sub-client given by its name. + * + * @param string $type Name of the client type + * @param string|null $name Name of the sub-client (Default if null) + * @return \Aimeos\Client\Html\Iface Sub-client object + * @throws \Aimeos\Client\Html\Exception + */ + public function getSubClient( string $type, string $name = null ) : \Aimeos\Client\Html\Iface + { + /** client/html/account/profile/account/decorators/excludes + * Excludes decorators added by the "common" option from the account profile account html client + * + * Decorators extend the functionality of a class by adding new aspects + * (e.g. log what is currently done), executing the methods of the underlying + * class only in certain conditions (e.g. only for logged in users) or + * modify what is returned to the caller. + * + * This option allows you to remove a decorator added via + * "client/html/common/decorators/default" before they are wrapped + * around the html client. + * + * client/html/account/profile/account/decorators/excludes = array( 'decorator1' ) + * + * This would remove the decorator named "decorator1" from the list of + * common decorators ("\Aimeos\Client\Html\Common\Decorator\*") added via + * "client/html/common/decorators/default" to the html client. + * + * @param array List of decorator names + * @since 2019.07 + * @category Developer + * @see client/html/common/decorators/default + * @see client/html/account/profile/account/decorators/global + * @see client/html/account/profile/account/decorators/local + */ + + /** client/html/account/profile/account/decorators/global + * Adds a list of globally available decorators only to the account profile account html client + * + * Decorators extend the functionality of a class by adding new aspects + * (e.g. log what is currently done), executing the methods of the underlying + * class only in certain conditions (e.g. only for logged in users) or + * modify what is returned to the caller. + * + * This option allows you to wrap global decorators + * ("\Aimeos\Client\Html\Common\Decorator\*") around the html client. + * + * client/html/account/profile/account/decorators/global = array( 'decorator1' ) + * + * This would add the decorator named "decorator1" defined by + * "\Aimeos\Client\Html\Common\Decorator\Decorator1" only to the html client. + * + * @param array List of decorator names + * @since 2019.07 + * @category Developer + * @see client/html/common/decorators/default + * @see client/html/account/profile/account/decorators/excludes + * @see client/html/account/profile/account/decorators/local + */ + + /** client/html/account/profile/account/decorators/local + * Adds a list of local decorators only to the account profile account html client + * + * Decorators extend the functionality of a class by adding new aspects + * (e.g. log what is currently done), executing the methods of the underlying + * class only in certain conditions (e.g. only for logged in users) or + * modify what is returned to the caller. + * + * This option allows you to wrap local decorators + * ("\Aimeos\Client\Html\Account\Decorator\*") around the html client. + * + * client/html/account/profile/account/decorators/local = array( 'decorator2' ) + * + * This would add the decorator named "decorator2" defined by + * "\Aimeos\Client\Html\Account\Decorator\Decorator2" only to the html client. + * + * @param array List of decorator names + * @since 2019.07 + * @category Developer + * @see client/html/common/decorators/default + * @see client/html/account/profile/account/decorators/excludes + * @see client/html/account/profile/account/decorators/global + */ + + return $this->createSubClient( 'account/profile/account/' . $type, $name ); + } + + /** + * Returns the HTML code for insertion into the body. + * + * @param string $uid Unique identifier for the output if the content is placed more than once on the same page + * @return string HTML code + * @throws \Aimeos\MW\View\Exception|\Aimeos\Client\Html\Exception + */ + public function getBody(string $uid = ''): string + { + $view = $this->getView(); + + $html = ''; + foreach( $this->getSubClients() as $subclient ) { + $html .= $subclient->setView( $view )->getBody( $uid ); + } + $view->accountBody = $html; + + /** client/html/account/profile/account/template-body + * Relative path to the HTML body template of the account profile account client. + * + * The template file contains the HTML code and processing instructions + * to generate the result shown in the body of the frontend. The + * configuration string is the path to the template file relative + * to the templates directory (usually in client/html/templates). + * + * You can overwrite the template file configuration in extensions and + * provide alternative templates. These alternative templates should be + * named like the default one but with the string "standard" replaced by + * an unique name. You may use the name of your project for this. If + * you've implemented an alternative client class as well, "standard" + * should be replaced by the name of the new class. + * + * @param string Relative path to the template creating code for the HTML page body + * @since 2019.07 + * @category Developer + * @see client/html/account/profile/account/template-header + */ + $tplconf = 'client/html/account/profile/account/template-body'; + $default = 'account/profile/account-body-standard'; + + return $view->render( $view->config( $tplconf, $default ) ); + } + + /** + * Processes the input, e.g. store given values. + * + * A view must be available and this method doesn't generate any output + * besides setting view variables if necessary. + * @throws \Aimeos\Client\Html\Exception|\Aimeos\Controller\Frontend\Exception + */ + public function process() + { + $view = $this->getView(); + + if( !$view->param( 'account/save' , false) ) { + parent::process(); + return; + } + + /** @var \Aimeos\Controller\Frontend\Customer\Standard $cntl */ + $cntl = \Aimeos\Controller\Frontend::create( $this->getContext(), 'customer' ); + $oldPassword = $cntl->get()->getPassword(); + $values = $view->param('account', []); + + $isNew = $values['customer.newpassword'] !== $values['customer.oldpassword']; + $confirmed = $values['customer.newpassword'] === $values['customer.confirmnewpassword']; + + $errors = []; + + $isValid = true; + try { + Validator::make([ + 'password' => $values['customer.newpassword'], + 'password_confirmation' => $values['customer.confirmnewpassword'] + ], [ + 'password' => ['required', 'string', new Password, 'confirmed'] + ])->validate(); + } catch (\Exception $ex) { + $isValid = false; + $errors['passwordRules'] = "The password does not meet the password requirements!"; + } + + if ($isValid) { + /** is the password realy new? */ + if (!$isNew) { + $errors['isNew'] = "The given password is not new!"; + } + + /** does the confirm password is the same? */ + if (!$confirmed) { + $errors["confirm"] = "New passwords doesnt match!"; + } + + $cntl = $cntl->add($values); + + } + /** if the pasword is new and confirmed, but is not changed, the given old password must be wrong */ + if ($isValid && $isNew && $confirmed && $oldPassword === $cntl->get()->getPassword() ) { + $errors['oldPassword'] = "Wrong password!"; + } + + $view->passwordChanged = count(array_keys($errors)) === 0; + + if (count(array_keys($errors)) > 0) { + $view->passwordErrors = $errors; + } else { + $cntl->store(); + } + + parent::process(); + } +} diff --git a/client/html/src/Client/Html/Account/Profile/Standard.php b/client/html/src/Client/Html/Account/Profile/Standard.php index df03a0f68..f3685a83c 100644 --- a/client/html/src/Client/Html/Account/Profile/Standard.php +++ b/client/html/src/Client/Html/Account/Profile/Standard.php @@ -66,7 +66,7 @@ class Standard * @since 2019.10 * @category Developer */ - private $subPartNames = ['address']; + private $subPartNames = ['address', 'account']; private $view; diff --git a/client/html/templates/account/profile/account-body-standard.php b/client/html/templates/account/profile/account-body-standard.php new file mode 100644 index 000000000..44f312943 --- /dev/null +++ b/client/html/templates/account/profile/account-body-standard.php @@ -0,0 +1,192 @@ +encoder(); + +/** client/html/account/profile/url/target + * Destination of the URL where the controller specified in the URL is known + * + * The destination can be a page ID like in a content management system or the + * module of a software development framework. This "target" must contain or know + * the controller that should be called by the generated URL. + * + * @param string Destination of the URL + * @since 2019.10 + * @category Developer + * @see client/html/account/profile/url/controller + * @see client/html/account/profile/url/action + * @see client/html/account/profile/url/config + */ + +/** client/html/account/profile/url/controller + * Name of the controller whose action should be called + * + * In Model-View-Controller (MVC) applications, the controller contains the methods + * that create parts of the output displayed in the generated HTML page. Controller + * names are usually alpha-numeric. + * + * @param string Name of the controller + * @since 2019.10 + * @category Developer + * @see client/html/account/profile/url/target + * @see client/html/account/profile/url/action + * @see client/html/account/profile/url/config + */ + +/** client/html/account/profile/url/action + * Name of the action that should create the output + * + * In Model-View-Controller (MVC) applications, actions are the methods of a + * controller that create parts of the output displayed in the generated HTML page. + * Action names are usually alpha-numeric. + * + * @param string Name of the action + * @since 2019.10 + * @category Developer + * @see client/html/account/profile/url/target + * @see client/html/account/profile/url/controller + * @see client/html/account/profile/url/config + */ + +/** client/html/account/profile/url/config + * Associative list of configuration options used for generating the URL + * + * You can specify additional options as key/value pairs used when generating + * the URLs, like + * + * client/html//url/config = array( 'absoluteUri' => true ) + * + * The available key/value pairs depend on the application that embeds the e-commerce + * framework. This is because the infrastructure of the application is used for + * generating the URLs. The full list of available config options is referenced + * in the "see also" section of this page. + * + * @param string Associative list of configuration options + * @since 2019.10 + * @category Developer + * @see client/html/account/profile/url/target + * @see client/html/account/profile/url/controller + * @see client/html/account/profile/url/action + * @see client/html/url/config + */ + +/** client/html/account/profile/url/filter + * Removes parameters for the detail page before generating the URL + * + * For SEO, it's nice to have URLs which contains only required parameters. + * This setting removes the listed parameters from the URLs. Keep care to + * remove no required parameters! + * + * @param array List of parameter names to remove + * @since 2019.10 + * @category User + * @category Developer + * @see client/html/account/profile/url/target + * @see client/html/account/profile/url/controller + * @see client/html/account/profile/url/action + * @see client/html/account/profile/url/config + */ + +$passwordErrors = $this->get('passwordErrors', []); + +?> +block()->start( 'account/profile/account' ) ?> + + diff --git a/client/html/themes/default/aimeos.css b/client/html/themes/default/aimeos.css index 073a7fdde..ab45cd71c 100644 --- a/client/html/themes/default/aimeos.css +++ b/client/html/themes/default/aimeos.css @@ -4129,6 +4129,29 @@ html.no-js .catalog-filter-price:hover .price-lists { min-width: 0; } +.account-profile-account .container-fluid { + padding: 1rem; +} + +.account-profile-account .password-button-group { + padding-top: 1rem; + display: flex; + justify-content: center; +} + +.account-profile-account .password-button-group .btn { + margin: 0; +} + +.account-profile-account .password-change-notifications { + display: flex; + justify-content: center; +} + +.account-profile-account .password-change > div { + flex-wrap: nowrap!important; +} + /*! jQuery UI - v1.10.0 - 2013-01-17 * https://jqueryui.com @@ -7182,4 +7205,4 @@ aside .catalog-session-pinned .pinned-items { .aimeos .card .btn { margin: 0.5rem 1rem; -} \ No newline at end of file +}