From 49e4a4d799a6024828240ff4a9d5fafd862ad92c Mon Sep 17 00:00:00 2001 From: Ruslan Voitenko Date: Tue, 14 Jul 2015 14:38:54 +0300 Subject: [PATCH 001/471] AEIV-116: Override providers --- .../Compiler/HwiConfigurationPass.php | 5 ++ .../SSOBundle/Resources/config/services.yml | 4 ++ .../SSOBundle/Security/OAuthProvider.php | 19 +++++++- .../SSOBundle/Security/OAuthTokenFactory.php | 16 +++++++ .../Security/OAuthTokenFactoryInterface.php | 13 +++++ .../Tests/Unit/Security/OAuthProviderTest.php | 47 +++++++++++++++++-- ...zationRememberMeAuthenticationProvider.php | 35 +++++++++++--- ...wordOrganizationAuthenticationProvider.php | 24 +++++++++- .../OrganizationRememberMeTokenFactory.php | 25 ++++++++++ ...izationRememberMeTokenFactoryInterface.php | 19 ++++++++ ...ernamePasswordOrganizationTokenFactory.php | 29 ++++++++++++ ...swordOrganizationTokenFactoryInterface.php | 18 +++++++ ...rganizationBasicAuthenticationListener.php | 31 +++++++++--- .../Resources/config/services.yml | 14 ++++++ ...onRememberMeAuthenticationProviderTest.php | 15 ++++++ .../Compiler/EscapeWsseConfigurationPass.php | 21 +++++++++ src/Oro/Bundle/UserBundle/OroUserBundle.php | 11 +++++ .../UserBundle/Resources/config/services.yml | 5 ++ .../UserBundle/Security/WsseAuthProvider.php | 19 +++++++- .../UserBundle/Security/WsseTokenFactory.php | 14 ++++++ .../Security/WsseTokenFactoryInterface.php | 12 +++++ .../Unit/Security/WsseAuthProviderTest.php | 12 +++++ 22 files changed, 386 insertions(+), 22 deletions(-) create mode 100644 src/Oro/Bundle/SSOBundle/Security/OAuthTokenFactory.php create mode 100644 src/Oro/Bundle/SSOBundle/Security/OAuthTokenFactoryInterface.php create mode 100644 src/Oro/Bundle/SecurityBundle/Authentication/Token/OrganizationRememberMeTokenFactory.php create mode 100644 src/Oro/Bundle/SecurityBundle/Authentication/Token/OrganizationRememberMeTokenFactoryInterface.php create mode 100644 src/Oro/Bundle/SecurityBundle/Authentication/Token/UsernamePasswordOrganizationTokenFactory.php create mode 100644 src/Oro/Bundle/SecurityBundle/Authentication/Token/UsernamePasswordOrganizationTokenFactoryInterface.php create mode 100644 src/Oro/Bundle/UserBundle/DependencyInjection/Compiler/EscapeWsseConfigurationPass.php create mode 100644 src/Oro/Bundle/UserBundle/Security/WsseTokenFactory.php create mode 100644 src/Oro/Bundle/UserBundle/Security/WsseTokenFactoryInterface.php diff --git a/src/Oro/Bundle/SSOBundle/DependencyInjection/Compiler/HwiConfigurationPass.php b/src/Oro/Bundle/SSOBundle/DependencyInjection/Compiler/HwiConfigurationPass.php index 226ab63cc43..c51a6ed8643 100644 --- a/src/Oro/Bundle/SSOBundle/DependencyInjection/Compiler/HwiConfigurationPass.php +++ b/src/Oro/Bundle/SSOBundle/DependencyInjection/Compiler/HwiConfigurationPass.php @@ -28,5 +28,10 @@ public function process(ContainerBuilder $container) new Reference('oro_config.global'), ]); } + + if ($container->hasDefinition('hwi_oauth.authentication.provider.oauth')) { + $definition = $container->getDefinition('hwi_oauth.authentication.provider.oauth'); + $definition->addMethodCall('setTokenFactory', [new Reference('oro_sso.token.factory.oauth')]); + } } } diff --git a/src/Oro/Bundle/SSOBundle/Resources/config/services.yml b/src/Oro/Bundle/SSOBundle/Resources/config/services.yml index 7e9de4322ca..bf35d4d014a 100644 --- a/src/Oro/Bundle/SSOBundle/Resources/config/services.yml +++ b/src/Oro/Bundle/SSOBundle/Resources/config/services.yml @@ -3,6 +3,7 @@ parameters: oro_sso.event_listener.user_email_change_listener.class: Oro\Bundle\SSOBundle\EventListener\UserEmailChangeListener hwi_oauth.authentication.provider.oauth.class: Oro\Bundle\SSOBundle\Security\OAuthProvider hwi_oauth.resource_owner.google.class: Oro\Bundle\SSOBundle\OAuth\ResourceOwner\GoogleResourceOwner + oro_sso.token.factory.oauth.class: Oro\Bundle\SSOBundle\Security\OAuthTokenFactory services: oro_sso.oauth_provider: @@ -13,3 +14,6 @@ services: class: %oro_sso.event_listener.user_email_change_listener.class% tags: - { name: doctrine.event_listener, event: preUpdate } + + oro_sso.token.factory.oauth: + class: %oro_sso.token.factory.oauth.class% diff --git a/src/Oro/Bundle/SSOBundle/Security/OAuthProvider.php b/src/Oro/Bundle/SSOBundle/Security/OAuthProvider.php index f7b0e441d86..e735078aa20 100644 --- a/src/Oro/Bundle/SSOBundle/Security/OAuthProvider.php +++ b/src/Oro/Bundle/SSOBundle/Security/OAuthProvider.php @@ -32,6 +32,11 @@ class OAuthProvider extends HWIOAuthProvider */ protected $userChecker; + /** + * @var OAuthTokenFactoryInterface + */ + protected $tokenFactory; + /** * @param OAuthAwareUserProviderInterface $userProvider User provider * @param ResourceOwnerMap $resourceOwnerMap Resource owner map @@ -47,6 +52,14 @@ public function __construct( $this->userChecker = $userChecker; } + /** + * @param OAuthTokenFactoryInterface $tokenFactory + */ + public function setTokenFactory(OAuthTokenFactoryInterface $tokenFactory) + { + $this->tokenFactory = $tokenFactory; + } + /** * Attempts to authenticate a TokenInterface object. * @@ -58,6 +71,10 @@ public function __construct( */ public function authenticate(TokenInterface $token) { + if (null === $this->tokenFactory) { + throw new AuthenticationException('Token Factory is not set in OAuthProvider.'); + } + $resourceOwner = $this->resourceOwnerMap->getResourceOwnerByName($token->getResourceOwnerName()); try { @@ -72,7 +89,7 @@ public function authenticate(TokenInterface $token) $organization = $this->guessOrganization($user, $token); - $token = new OAuthToken($token->getRawToken(), $user->getRoles()); + $token = $this->tokenFactory->create($token->getRawToken(), $user->getRoles()); $token->setResourceOwnerName($resourceOwner->getName()); $token->setOrganizationContext($organization); $token->setUser($user); diff --git a/src/Oro/Bundle/SSOBundle/Security/OAuthTokenFactory.php b/src/Oro/Bundle/SSOBundle/Security/OAuthTokenFactory.php new file mode 100644 index 00000000000..9eadbedc8ed --- /dev/null +++ b/src/Oro/Bundle/SSOBundle/Security/OAuthTokenFactory.php @@ -0,0 +1,16 @@ +userProvider = $this @@ -22,18 +47,32 @@ public function setUp() ->disableOriginalConstructor() ->getMock(); $this->userChecker = $this->getMock('Symfony\Component\Security\Core\User\UserCheckerInterface'); - + + $this->tokenFactory = new OAuthTokenFactory(); + $this->oauthProvider = new OAuthProvider($this->userProvider, $this->resourceOwnerMap, $this->userChecker); } - public function testSupportsShuldReturnTrueForOAuthToken() + public function testSupportsShouldReturnTrueForOAuthToken() { $token = new OAuthToken('token'); $this->assertTrue($this->oauthProvider->supports($token)); } - + + /** + * @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationException + * @expectedExceptionMessage Token Factory is not set in OAuthProvider. + */ + public function testAuthenticateIfTokenFactoryIsNotSet() + { + $token = new OAuthToken('token'); + $this->oauthProvider->authenticate($token); + } + public function testTokenShouldBeAuthenticated() { + $this->oauthProvider->setTokenFactory($this->tokenFactory); + $token = new OAuthToken('token'); $token->setResourceOwnerName('google'); $organization = new Organization(); @@ -41,7 +80,7 @@ public function testTokenShouldBeAuthenticated() $token->setOrganizationContext($organization); $userResponse = $this->getMock('HWI\Bundle\OAuthBundle\OAuth\Response\UserResponseInterface'); - + $resourceOwner = $this->getMock('HWI\Bundle\OAuthBundle\OAuth\ResourceOwnerInterface'); $resourceOwner ->expects($this->any()) diff --git a/src/Oro/Bundle/SecurityBundle/Authentication/Provider/OrganizationRememberMeAuthenticationProvider.php b/src/Oro/Bundle/SecurityBundle/Authentication/Provider/OrganizationRememberMeAuthenticationProvider.php index eec23366d77..b3dd010e2e2 100644 --- a/src/Oro/Bundle/SecurityBundle/Authentication/Provider/OrganizationRememberMeAuthenticationProvider.php +++ b/src/Oro/Bundle/SecurityBundle/Authentication/Provider/OrganizationRememberMeAuthenticationProvider.php @@ -2,21 +2,41 @@ namespace Oro\Bundle\SecurityBundle\Authentication\Provider; +use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authentication\Provider\RememberMeAuthenticationProvider; use Oro\Bundle\UserBundle\Entity\User; use Oro\Bundle\SecurityBundle\Authentication\Guesser\UserOrganizationGuesser; -use Oro\Bundle\SecurityBundle\Authentication\Token\OrganizationRememberMeToken; +use Oro\Bundle\SecurityBundle\Authentication\Token\OrganizationRememberMeTokenFactoryInterface as TokenFactory; class OrganizationRememberMeAuthenticationProvider extends RememberMeAuthenticationProvider { + /** + * @var TokenFactory + */ + protected $tokenFactory; + + /** + * @param TokenFactory $tokenFactory + */ + public function setTokenFactory(TokenFactory $tokenFactory) + { + $this->tokenFactory = $tokenFactory; + } + /** * {@inheritdoc} */ public function authenticate(TokenInterface $token) { + if (null === $this->tokenFactory) { + throw new AuthenticationException( + 'Token Factory is not set in OrganizationRememberMeAuthenticationProvider.' + ); + } + $guesser = new UserOrganizationGuesser(); /** @var TokenInterface $token */ $authenticatedToken = parent::authenticate($token); @@ -33,12 +53,13 @@ public function authenticate(TokenInterface $token) ); } - $authenticatedToken = new OrganizationRememberMeToken( - $user, - $authenticatedToken->getProviderKey(), - $authenticatedToken->getKey(), - $organization - ); + $authenticatedToken = $this->tokenFactory + ->create( + $user, + $authenticatedToken->getProviderKey(), + $authenticatedToken->getKey(), + $organization + ); return $authenticatedToken; } diff --git a/src/Oro/Bundle/SecurityBundle/Authentication/Provider/UsernamePasswordOrganizationAuthenticationProvider.php b/src/Oro/Bundle/SecurityBundle/Authentication/Provider/UsernamePasswordOrganizationAuthenticationProvider.php index 8971dc68639..9b5d2449266 100644 --- a/src/Oro/Bundle/SecurityBundle/Authentication/Provider/UsernamePasswordOrganizationAuthenticationProvider.php +++ b/src/Oro/Bundle/SecurityBundle/Authentication/Provider/UsernamePasswordOrganizationAuthenticationProvider.php @@ -2,21 +2,41 @@ namespace Oro\Bundle\SecurityBundle\Authentication\Provider; +use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authentication\Provider\DaoAuthenticationProvider; use Oro\Bundle\UserBundle\Entity\User; use Oro\Bundle\SecurityBundle\Authentication\Guesser\UserOrganizationGuesser; -use Oro\Bundle\SecurityBundle\Authentication\Token\UsernamePasswordOrganizationToken; +use Oro\Bundle\SecurityBundle\Authentication\Token\UsernamePasswordOrganizationTokenFactoryInterface as TokenFactory; class UsernamePasswordOrganizationAuthenticationProvider extends DaoAuthenticationProvider { + /** + * @var TokenFactory + */ + protected $tokenFactory; + + /** + * @param TokenFactory $tokenFactory + */ + public function setTokenFactory(TokenFactory $tokenFactory) + { + $this->tokenFactory = $tokenFactory; + } + /** * {@inheritdoc} */ public function authenticate(TokenInterface $token) { + if (null === $this->tokenFactory) { + throw new AuthenticationException( + 'Token Factory is not set in UsernamePasswordOrganizationAuthenticationProvider.' + ); + } + $guesser = new UserOrganizationGuesser(); /** @var TokenInterface $token */ $authenticatedToken = parent::authenticate($token); @@ -33,7 +53,7 @@ public function authenticate(TokenInterface $token) ); } - $authenticatedToken = new UsernamePasswordOrganizationToken( + $authenticatedToken = $this->tokenFactory->create( $authenticatedToken->getUser(), $authenticatedToken->getCredentials(), $authenticatedToken->getProviderKey(), diff --git a/src/Oro/Bundle/SecurityBundle/Authentication/Token/OrganizationRememberMeTokenFactory.php b/src/Oro/Bundle/SecurityBundle/Authentication/Token/OrganizationRememberMeTokenFactory.php new file mode 100644 index 00000000000..8289c2e77e9 --- /dev/null +++ b/src/Oro/Bundle/SecurityBundle/Authentication/Token/OrganizationRememberMeTokenFactory.php @@ -0,0 +1,25 @@ +logger = $logger; $this->manager = $manager; $this->ignoreFailure = false; + $this->tokenFactory = $tokenFactory; } /** @@ -86,12 +102,13 @@ public function handle(GetResponseEvent $event) try { $organizationId = $request->headers->get('PHP_AUTH_ORGANIZATION'); if ($organizationId) { - $authToken = new UsernamePasswordOrganizationToken( - $username, - $request->headers->get('PHP_AUTH_PW'), - $this->providerKey, - $this->manager->getOrganizationById($organizationId) - ); + $authToken = $this->tokenFactory + ->create( + $username, + $request->headers->get('PHP_AUTH_PW'), + $this->providerKey, + $this->manager->getOrganizationById($organizationId) + ); } else { $authToken = new UsernamePasswordToken( $username, diff --git a/src/Oro/Bundle/SecurityBundle/Resources/config/services.yml b/src/Oro/Bundle/SecurityBundle/Resources/config/services.yml index f9f8bf5499f..d29986b467e 100644 --- a/src/Oro/Bundle/SecurityBundle/Resources/config/services.yml +++ b/src/Oro/Bundle/SecurityBundle/Resources/config/services.yml @@ -45,6 +45,9 @@ parameters: oro_security.encoder.mcrypt.class: Oro\Bundle\SecurityBundle\Encoder\Mcrypt + oro_security.token.factory.username_password_organization.class: Oro\Bundle\SecurityBundle\Authentication\Token\UsernamePasswordOrganizationTokenFactory + oro_security.token.factory.organization_rememberme.class: Oro\Bundle\SecurityBundle\Authentication\Token\OrganizationRememberMeTokenFactory + oro_security.acl_helper.class: Oro\Bundle\SecurityBundle\ORM\Walker\AclHelper oro_security.orm.ownership_sql_walker_builder.class: Oro\Bundle\SecurityBundle\ORM\Walker\OwnershipConditionDataBuilder oro_security.ownership_tree_provider.class: Oro\Bundle\SecurityBundle\Owner\OwnerTreeProvider @@ -396,6 +399,12 @@ services: tags: - { name: doctrine.event_listener, event: onFlush } + oro_security.token.factory.username_password_organization: + class: %oro_security.token.factory.username_password_organization.class% + + oro_security.token.factory.organization_rememberme: + class: %oro_security.token.factory.organization_rememberme.class% + oro_security.authentication.listener.basic: class: %oro-security.authentication.listener.basic.class% arguments: @@ -404,6 +413,7 @@ services: - null - null - @oro_organization.organization_manager + - @oro_security.token.factory.username_password_organization - @?logger public: false abstract: true @@ -416,6 +426,8 @@ services: - null - @security.encoder_factory - %security.authentication.hide_user_not_found% + calls: + - [setTokenFactory, [@oro_security.token.factory.username_password_organization]] abstract: true public: false @@ -423,6 +435,8 @@ services: class: %oro_security.authentication.provider.organization_rememberme.class% arguments: - @security.user_checker + calls: + - [setTokenFactory, [@oro_security.token.factory.organization_rememberme]] abstract: true public: false diff --git a/src/Oro/Bundle/SecurityBundle/Tests/Unit/Authentication/Provider/OrganizationRememberMeAuthenticationProviderTest.php b/src/Oro/Bundle/SecurityBundle/Tests/Unit/Authentication/Provider/OrganizationRememberMeAuthenticationProviderTest.php index b87ae7477de..54a0f9ba6e6 100644 --- a/src/Oro/Bundle/SecurityBundle/Tests/Unit/Authentication/Provider/OrganizationRememberMeAuthenticationProviderTest.php +++ b/src/Oro/Bundle/SecurityBundle/Tests/Unit/Authentication/Provider/OrganizationRememberMeAuthenticationProviderTest.php @@ -6,6 +6,7 @@ use Oro\Bundle\SecurityBundle\Tests\Unit\Acl\Domain\Fixtures\Entity\User; use Oro\Bundle\SecurityBundle\Authentication\Token\OrganizationRememberMeToken; +use Oro\Bundle\SecurityBundle\Authentication\Token\OrganizationRememberMeTokenFactory; use Oro\Bundle\SecurityBundle\Tests\Unit\Acl\Domain\Fixtures\Entity\Organization; use Oro\Bundle\SecurityBundle\Authentication\Provider\OrganizationRememberMeAuthenticationProvider; @@ -25,6 +26,7 @@ public function setUp() { $this->userChecker = $this->getMock('Symfony\Component\Security\Core\User\UserCheckerInterface'); $this->provider = new OrganizationRememberMeAuthenticationProvider($this->userChecker, 'testKey', 'provider'); + $this->provider->setTokenFactory(new OrganizationRememberMeTokenFactory()); } public function testSupports() @@ -43,6 +45,19 @@ public function testSupports() $this->assertFalse($this->provider->supports($token)); } + /** + * @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationException + * @expectedExceptionMessage Token Factory is not set in OrganizationRememberMeAuthenticationProvider. + */ + public function testAuthenticateIfTokenFactoryIsNotSet() + { + $user = new User(1); + $organization = new Organization(2); + $token = new OrganizationRememberMeToken($user, 'provider', 'testKey', $organization); + $provider = new OrganizationRememberMeAuthenticationProvider($this->userChecker, 'testKey', 'provider'); + $provider->authenticate($token); + } + public function testAuthenticate() { $organization = new Organization(2); diff --git a/src/Oro/Bundle/UserBundle/DependencyInjection/Compiler/EscapeWsseConfigurationPass.php b/src/Oro/Bundle/UserBundle/DependencyInjection/Compiler/EscapeWsseConfigurationPass.php new file mode 100644 index 00000000000..db91b89a25b --- /dev/null +++ b/src/Oro/Bundle/UserBundle/DependencyInjection/Compiler/EscapeWsseConfigurationPass.php @@ -0,0 +1,21 @@ +hasDefinition('escape_wsse_authentication.provider')) { + $definition = $container->getDefinition('escape_wsse_authentication.provider'); + $definition->addMethodCall('setTokenFactory', [new Reference('oro_user.token.factory.wsse')]); + } + } +} diff --git a/src/Oro/Bundle/UserBundle/OroUserBundle.php b/src/Oro/Bundle/UserBundle/OroUserBundle.php index 7f8e7e8be21..14f8b6ca082 100644 --- a/src/Oro/Bundle/UserBundle/OroUserBundle.php +++ b/src/Oro/Bundle/UserBundle/OroUserBundle.php @@ -2,8 +2,19 @@ namespace Oro\Bundle\UserBundle; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\Bundle; +use Oro\Bundle\UserBundle\DependencyInjection\Compiler\EscapeWsseConfigurationPass; + class OroUserBundle extends Bundle { + /** + * {@inheritdoc} + */ + public function build(ContainerBuilder $container) + { + parent::build($container); + $container->addCompilerPass(new EscapeWsseConfigurationPass()); + } } diff --git a/src/Oro/Bundle/UserBundle/Resources/config/services.yml b/src/Oro/Bundle/UserBundle/Resources/config/services.yml index 4533f94a9b8..6d2ed56877e 100644 --- a/src/Oro/Bundle/UserBundle/Resources/config/services.yml +++ b/src/Oro/Bundle/UserBundle/Resources/config/services.yml @@ -30,6 +30,8 @@ parameters: oro_user.placeholder.filter.class: Oro\Bundle\UserBundle\Placeholder\PlaceholderFilter oro_user.security.user_checker.class: Oro\Bundle\UserBundle\Security\UserChecker + + oro_user.token.factory.wsse.class: Oro\Bundle\UserBundle\Security\WsseTokenFactory services: oro_user.manager: class: %oro_user.manager.class% @@ -250,3 +252,6 @@ services: - @doctrine.orm.entity_manager tags: - { name: kernel.event_listener, event: oro_datagrid.datagrid.build.after.users-for-context-grid, method: onBuildAfter } + + oro_user.token.factory.wsse: + class: %oro_user.token.factory.wsse.class% diff --git a/src/Oro/Bundle/UserBundle/Security/WsseAuthProvider.php b/src/Oro/Bundle/UserBundle/Security/WsseAuthProvider.php index 16c5845ca26..d5b45e5609b 100644 --- a/src/Oro/Bundle/UserBundle/Security/WsseAuthProvider.php +++ b/src/Oro/Bundle/UserBundle/Security/WsseAuthProvider.php @@ -24,6 +24,19 @@ */ class WsseAuthProvider extends Provider { + /** + * @var WsseTokenFactoryInterface + */ + protected $tokenFactory; + + /** + * @param WsseTokenFactoryInterface $tokenFactory + */ + public function setTokenFactory(WsseTokenFactoryInterface $tokenFactory) + { + $this->tokenFactory = $tokenFactory; + } + /** * {@inheritdoc} */ @@ -59,6 +72,10 @@ protected function getSalt(UserInterface $user) */ public function authenticate(TokenInterface $token) { + if (null === $this->tokenFactory) { + throw new AuthenticationException('Token Factory is not set in WsseAuthProvider.'); + } + /** @var User $user */ $user = $this->getUserProvider()->loadUserByUsername($token->getUsername()); if ($user) { @@ -66,7 +83,7 @@ public function authenticate(TokenInterface $token) if ($secret instanceof PersistentCollection) { $validUserApi = $this->getValidUserApi($token, $secret, $user); if ($validUserApi) { - $authenticatedToken = new WsseToken($user->getRoles()); + $authenticatedToken = $this->tokenFactory->create($user->getRoles()); $authenticatedToken->setUser($user); $authenticatedToken->setOrganizationContext($validUserApi->getOrganization()); $authenticatedToken->setAuthenticated(true); diff --git a/src/Oro/Bundle/UserBundle/Security/WsseTokenFactory.php b/src/Oro/Bundle/UserBundle/Security/WsseTokenFactory.php new file mode 100644 index 00000000000..ba7008b23ee --- /dev/null +++ b/src/Oro/Bundle/UserBundle/Security/WsseTokenFactory.php @@ -0,0 +1,14 @@ +provider = new WsseAuthProvider($this->userProvider, $this->encoder, $cache); + $this->provider->setTokenFactory(new WsseTokenFactory()); } protected function tearDown() @@ -43,6 +45,16 @@ protected function tearDown() unset($this->userProvider, $this->encoder, $this->provider); } + /** + * @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationException + * @expectedExceptionMessage Token Factory is not set in WsseAuthProvider. + */ + public function testAuthenticateIfTokenFactoryIsNotSet() + { + $provider = new WsseAuthProvider($this->userProvider, $this->encoder, new ArrayCache()); + $provider->authenticate(new Token()); + } + /** * @dataProvider userProvider * From 3513ebdbcb77095a7a56ff2040fdbb0be37cade2 Mon Sep 17 00:00:00 2001 From: Ruslan Voitenko Date: Tue, 14 Jul 2015 14:58:17 +0300 Subject: [PATCH 002/471] AEIV-116: Override providers. Factory unit tests --- .../Unit/Security/OAuthTokenFactoryTest.php | 17 +++++++++++++ ...OrganizationRememberMeTokenFactoryTest.php | 24 ++++++++++++++++++ ...mePasswordOrganizationTokenFactoryTest.php | 25 +++++++++++++++++++ .../Unit/Security/WsseTokenFactoryTest.php | 16 ++++++++++++ 4 files changed, 82 insertions(+) create mode 100644 src/Oro/Bundle/SSOBundle/Tests/Unit/Security/OAuthTokenFactoryTest.php create mode 100644 src/Oro/Bundle/SecurityBundle/Tests/Unit/Authentication/Token/OrganizationRememberMeTokenFactoryTest.php create mode 100644 src/Oro/Bundle/SecurityBundle/Tests/Unit/Authentication/Token/UsernamePasswordOrganizationTokenFactoryTest.php create mode 100644 src/Oro/Bundle/UserBundle/Tests/Unit/Security/WsseTokenFactoryTest.php diff --git a/src/Oro/Bundle/SSOBundle/Tests/Unit/Security/OAuthTokenFactoryTest.php b/src/Oro/Bundle/SSOBundle/Tests/Unit/Security/OAuthTokenFactoryTest.php new file mode 100644 index 00000000000..0f3d55ad4ca --- /dev/null +++ b/src/Oro/Bundle/SSOBundle/Tests/Unit/Security/OAuthTokenFactoryTest.php @@ -0,0 +1,17 @@ +create('accessToken'); + + $this->assertInstanceOf('Oro\Bundle\SSOBundle\Security\OAuthToken', $token); + $this->assertEquals('accessToken', $token->getAccessToken()); + } +} diff --git a/src/Oro/Bundle/SecurityBundle/Tests/Unit/Authentication/Token/OrganizationRememberMeTokenFactoryTest.php b/src/Oro/Bundle/SecurityBundle/Tests/Unit/Authentication/Token/OrganizationRememberMeTokenFactoryTest.php new file mode 100644 index 00000000000..f366fd687e6 --- /dev/null +++ b/src/Oro/Bundle/SecurityBundle/Tests/Unit/Authentication/Token/OrganizationRememberMeTokenFactoryTest.php @@ -0,0 +1,24 @@ +create($user, 'testProvider', 'testKey', $organization); + + $this->assertInstanceOf('Oro\Bundle\SecurityBundle\Authentication\Token\OrganizationRememberMeToken', $token); + $this->assertEquals($user, $token->getUser()); + $this->assertEquals($organization, $token->getOrganizationContext()); + $this->assertEquals('testProvider', $token->getProviderKey()); + $this->assertEquals('testKey', $token->getKey()); + } +} diff --git a/src/Oro/Bundle/SecurityBundle/Tests/Unit/Authentication/Token/UsernamePasswordOrganizationTokenFactoryTest.php b/src/Oro/Bundle/SecurityBundle/Tests/Unit/Authentication/Token/UsernamePasswordOrganizationTokenFactoryTest.php new file mode 100644 index 00000000000..a20ae07afb1 --- /dev/null +++ b/src/Oro/Bundle/SecurityBundle/Tests/Unit/Authentication/Token/UsernamePasswordOrganizationTokenFactoryTest.php @@ -0,0 +1,25 @@ +create('username', 'credentials', 'testProvider', $organization); + + $this->assertInstanceOf( + 'Oro\Bundle\SecurityBundle\Authentication\Token\UsernamePasswordOrganizationToken', + $token + ); + $this->assertEquals($organization, $token->getOrganizationContext()); + $this->assertEquals('username', $token->getUser()); + $this->assertEquals('credentials', $token->getCredentials()); + $this->assertEquals('testProvider', $token->getProviderKey()); + } +} diff --git a/src/Oro/Bundle/UserBundle/Tests/Unit/Security/WsseTokenFactoryTest.php b/src/Oro/Bundle/UserBundle/Tests/Unit/Security/WsseTokenFactoryTest.php new file mode 100644 index 00000000000..7ab9992608e --- /dev/null +++ b/src/Oro/Bundle/UserBundle/Tests/Unit/Security/WsseTokenFactoryTest.php @@ -0,0 +1,16 @@ +create(); + + $this->assertInstanceOf('Oro\Bundle\UserBundle\Security\WsseToken', $token); + } +} From 3df059c9fab40e966beabcd939cd2d30a1ac1bad Mon Sep 17 00:00:00 2001 From: alex gotgelf Date: Thu, 6 Aug 2015 19:20:04 +0200 Subject: [PATCH 003/471] AEIV-173: Validate implementation with business requirements --- .../Firewall/OrganizationBasicAuthenticationListener.php | 9 +++++++-- .../Bundle/SecurityBundle/Resources/config/services.yml | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Oro/Bundle/SecurityBundle/Http/Firewall/OrganizationBasicAuthenticationListener.php b/src/Oro/Bundle/SecurityBundle/Http/Firewall/OrganizationBasicAuthenticationListener.php index 979035ef17a..5268ccceda0 100644 --- a/src/Oro/Bundle/SecurityBundle/Http/Firewall/OrganizationBasicAuthenticationListener.php +++ b/src/Oro/Bundle/SecurityBundle/Http/Firewall/OrganizationBasicAuthenticationListener.php @@ -49,7 +49,6 @@ class OrganizationBasicAuthenticationListener * @param string $providerKey * @param AuthenticationEntryPointInterface $authenticationEntryPoint * @param OrganizationManager $manager - * @param TokenFactory $tokenFactory * @param LoggerInterface $logger */ public function __construct( @@ -58,7 +57,6 @@ public function __construct( $providerKey, AuthenticationEntryPointInterface $authenticationEntryPoint, OrganizationManager $manager, - TokenFactory $tokenFactory, LoggerInterface $logger = null ) { if (empty($providerKey)) { @@ -72,6 +70,13 @@ public function __construct( $this->logger = $logger; $this->manager = $manager; $this->ignoreFailure = false; + } + + /** + * @param TokenFactory $tokenFactory + */ + public function setTokenFactory(TokenFactory $tokenFactory) + { $this->tokenFactory = $tokenFactory; } diff --git a/src/Oro/Bundle/SecurityBundle/Resources/config/services.yml b/src/Oro/Bundle/SecurityBundle/Resources/config/services.yml index d2c9605fc71..ff811be916f 100644 --- a/src/Oro/Bundle/SecurityBundle/Resources/config/services.yml +++ b/src/Oro/Bundle/SecurityBundle/Resources/config/services.yml @@ -460,8 +460,9 @@ services: - null - null - @oro_organization.organization_manager - - @oro_security.token.factory.username_password_organization - @?logger + calls: + - [setTokenFactory, [@oro_security.token.factory.username_password_organization]] public: false abstract: true From 3767096806c4071ea0b61ad1fe7cc97e321d273d Mon Sep 17 00:00:00 2001 From: Ruslan Voitenko Date: Wed, 12 Aug 2015 14:43:36 +0300 Subject: [PATCH 004/471] Fix of usage of non existent method in OrganizationRepository --- .../Autocomplete/OrganizationSearchHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Oro/Bundle/OrganizationBundle/Autocomplete/OrganizationSearchHandler.php b/src/Oro/Bundle/OrganizationBundle/Autocomplete/OrganizationSearchHandler.php index 1548a0dd216..7a39ac1331f 100644 --- a/src/Oro/Bundle/OrganizationBundle/Autocomplete/OrganizationSearchHandler.php +++ b/src/Oro/Bundle/OrganizationBundle/Autocomplete/OrganizationSearchHandler.php @@ -60,7 +60,7 @@ public function search($query, $page, $perPage, $searchById = false) $user = $this->securityContextLink->getService()->getToken()->getUser(); $repository = $this->managerRegistry->getRepository($this->className); if (!$searchById) { - $items = $repository->getEnabledUserOrganizationsByName($user, $query); + $items = $repository->getEnabledByUserAndName($user, $query); } else { $items = $repository->getEnabledUserOrganizationById($user, $query); } From ef09e5d23104693a0d6132d3991974349a079405 Mon Sep 17 00:00:00 2001 From: Ruslan Voitenko Date: Wed, 12 Aug 2015 15:35:50 +0300 Subject: [PATCH 005/471] Update of Upgrade.md (introducing of security token factories). --- UPGRADE-1.8.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/UPGRADE-1.8.md b/UPGRADE-1.8.md index 3cfd93ead5a..904da2fa521 100644 --- a/UPGRADE-1.8.md +++ b/UPGRADE-1.8.md @@ -176,7 +176,8 @@ Removed parameters `websocket_host` and `websocket_port` from `parameters.yml`. - `Oro\Bundle\UserBundle\Mailer\Processor` is now based on `Oro\Bundle\UserBundle\Mailer\BaseProcessor` - `Oro\Bundle\UserBundle\Mailer\Processor` - first argument `$user` of `sendChangePasswordEmail`, `sendResetPasswordEmail` and `sendResetPasswordAsAdminEmail` methods must implement `Oro\Bundle\UserBundle\Entity\UserInterface` - First argument `Doctrine\Common\Persistence\ObjectManager $objectManager` and fourth argument `Oro\Bundle\UserBundle\Entity\UserManager $userManager` of `Oro\Bundle\UserBundle\Mailer\Processor` constructor (which now is located in `Oro\Bundle\UserBundle\Mailer\BaseProcessor`) replaced by `Doctrine\Common\Persistence\ManagerRegistry $managerRegistry` and `Oro\Bundle\EmailBundle\Tools\EmailHolderHelper $emailHolderHelper` accordingly -- `Oro\Bundle\UserBundle\Form\Handler\AclRoleHandler` is now accepts Manager Registry instead of Entity Manager, added method `setManagerRegistry`, method `setEntityManager` marked as deprecated +- `Oro\Bundle\UserBundle\Form\Handler\AclRoleHandler` is now accepts Manager Registry instead of Entity Manager, added method `setManagerRegistry`, method `setEntityManager` marked as deprecated +- `Oro\Bundle\UserBundle\Security\WsseTokenFactoryInterface` and its implementation `Oro\Bundle\UserBundle\Security\WsseTokenFactory` were introduced to encapsulate creation of `WsseToken` in `Oro\Bundle\UserBundle\Security\WsseAuthProvider` ####SecurityBundle - `Oro\Bundle\SecurityBundle\Owner\Metadata\OwnershipMetadataInterface` was introduced and based on access levels, considered to use in security layer instead of direct `Oro\Bundle\SecurityBundle\Owner\Metadata\OwnershipMetadata` usage @@ -229,6 +230,8 @@ Removed parameters `websocket_host` and `websocket_port` from `parameters.yml`. * `oro_security.ownership_tree.cache.warmer` * `oro_security.orm.ownership_sql_walker_builder` - Service `@oro_security.link.ownership_tree_provider` is deprecated, please use `Symfony\Component\DependencyInjection\ContainerInterface` directly +- `Oro\Bundle\SecurityBundle\Authentication\Token\UsernamePasswordOrganizationTokenFactoryInterface` and its implementation `Oro\Bundle\SecurityBundle\Authentication\Token\UsernamePasswordOrganizationTokenFactory` were introduced to encapsulate creation of `UsernamePasswordOrganizationToken` in `Oro\Bundle\SecurityBundle\Authentication\Provider\UsernamePasswordOrganizationAuthenticationProvider` and `Oro\Bundle\SecurityBundle\Http\Firewall\OrganizationBasicAuthenticationListener` +- `Oro\Bundle\SecurityBundle\Authentication\Token\OrganizationRememberMeTokenFactoryInterface` and its implementation `Oro\Bundle\SecurityBundle\Authentication\Token\OrganizationRememberMeTokenFactory` were introduced to encapsulate creation of `OrganizationRememberMeToken` in `Oro\Bundle\SecurityBundle\Authentication\Provider\UsernamePasswordOrganizationAuthenticationProvider` ####AddressBundle - `Oro\Bundle\AddressBundle\Form\EventListener\FixAddressesPrimaryAndTypesSubscriber` marked deprecated. Use `Oro\Bundle\AddressBundle\Form\EventListener\FixAddressesPrimarySubscriber` and `Oro\Bundle\AddressBundle\Form\EventListener\FixAddressesTypesSubscriber` instead. @@ -238,3 +241,6 @@ Removed parameters `websocket_host` and `websocket_port` from `parameters.yml`. ####DataAuditBundle - `Oro\Bundle\DataAuditBundle\Loggable\LoggableManager` `logEntityClass` and `logEntityFieldClass` parameters replaced by `oro_dataaudit.loggable.audit_entity_mapper` service `getAuditEntryClass` and `getAuditEntryFieldClass` methods + +####SSOBundle +- `Oro\Bundle\SSOBundle\Security\OAuthTokenFactoryInterface` and its implementation `Oro\Bundle\SSOBundle\Security\OAuthTokenFactory` were introduced to encapsulate creation of `OAuthToken` in `Oro\Bundle\SSOBundle\Security\OAuthProvider` From 720fdd6715b9473b032cccedcd5f3208ff6b7bb2 Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Thu, 27 Aug 2015 18:47:41 +0200 Subject: [PATCH 006/471] CRM-3997: Make all mailboxes available under System organization - choose organization in system organization when in system configuration --- .../Resources/views/Configuration/Mailbox/update.html.twig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Oro/Bundle/EmailBundle/Resources/views/Configuration/Mailbox/update.html.twig b/src/Oro/Bundle/EmailBundle/Resources/views/Configuration/Mailbox/update.html.twig index fc7ac6299a4..886716897f6 100644 --- a/src/Oro/Bundle/EmailBundle/Resources/views/Configuration/Mailbox/update.html.twig +++ b/src/Oro/Bundle/EmailBundle/Resources/views/Configuration/Mailbox/update.html.twig @@ -22,10 +22,14 @@ }, mailboxTitle ] %} +{% set formActionParams = app.request.attributes.get('_route_params')|merge({redirectData: redirectData}) %} +{% if app.request.query.has('form') %} + {% set formActionParams = formActionParams|merge({form: app.request.query.get('form')}) %} +{% endif %} {% set formAction = path( app.request.attributes.get('_route'), - app.request.attributes.get('_route_params')|merge({redirectData: redirectData}) + formActionParams ) %} {% set routeName = 'oro_config_configuration_system' %} From 15f7d1df457e8ad4c024e5494a21f0f776c5c878 Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Thu, 27 Aug 2015 19:48:50 +0200 Subject: [PATCH 007/471] CRM-3997: Make all mailboxes available under System organization - fix mailbox choice --- .../EmailBundle/Datagrid/MailboxChoiceList.php | 11 ++++++++++- .../Entity/Repository/MailboxRepository.php | 13 +++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/Oro/Bundle/EmailBundle/Datagrid/MailboxChoiceList.php b/src/Oro/Bundle/EmailBundle/Datagrid/MailboxChoiceList.php index 3e5805b066e..11f7025f937 100644 --- a/src/Oro/Bundle/EmailBundle/Datagrid/MailboxChoiceList.php +++ b/src/Oro/Bundle/EmailBundle/Datagrid/MailboxChoiceList.php @@ -7,6 +7,7 @@ use Oro\Bundle\EmailBundle\Entity\EmailOrigin; use Oro\Bundle\EmailBundle\Entity\Mailbox; use Oro\Bundle\SecurityBundle\SecurityFacade; +use Oro\Bundle\OrganizationBundle\Entity\Organization; class MailboxChoiceList { @@ -38,7 +39,7 @@ public function getChoiceList() /** @var Mailbox[] $systemMailboxes */ $systemMailboxes = $repo->findAvailableMailboxes( $this->securityFacade->getLoggedUser(), - $this->securityFacade->getOrganization() + $this->getOrganization() ); $origins = $this->getOriginsList(); @@ -58,6 +59,14 @@ public function getChoiceList() return $choiceList; } + /** + * @return Organization|null + */ + protected function getOrganization() + { + return $this->securityFacade->getOrganization(); + } + /** * @return EmailOrigin[] */ diff --git a/src/Oro/Bundle/EmailBundle/Entity/Repository/MailboxRepository.php b/src/Oro/Bundle/EmailBundle/Entity/Repository/MailboxRepository.php index 3e436a192b2..22efdfb329d 100644 --- a/src/Oro/Bundle/EmailBundle/Entity/Repository/MailboxRepository.php +++ b/src/Oro/Bundle/EmailBundle/Entity/Repository/MailboxRepository.php @@ -70,7 +70,7 @@ public function findAvailableMailboxIds($user, $organization) /** * @param User|integer $user User or user id - * @param Organization $organization + * @param Organization|integer|null $organization * * @return \Doctrine\ORM\QueryBuilder */ @@ -79,7 +79,8 @@ protected function createAvailableMailboxesQuery($user, $organization) if (!$user instanceof User) { $user = $this->getEntityManager()->getRepository('OroUserBundle:User')->find($user); } - if (!$organization instanceof Organization) { + + if ($organization !== null && !$organization instanceof Organization) { $organization = $this->getEntityManager() ->getRepository('OroOrganizationBundle:Organization') ->find($organization); @@ -92,7 +93,6 @@ protected function createAvailableMailboxesQuery($user, $organization) ->from('OroEmailBundle:Mailbox', 'mb') ->leftJoin('mb.authorizedUsers', 'au') ->leftJoin('mb.authorizedRoles', 'ar') - ->andWhere('mb.organization = :organization') ->andWhere( $qb->expr()->orX( 'au = :user', @@ -101,7 +101,12 @@ protected function createAvailableMailboxesQuery($user, $organization) ); $qb->setParameter('user', $user); $qb->setParameter('roles', $roles); - $qb->setParameter('organization', $organization); + + if ($organization) { + $qb + ->andWhere('mb.organization = :organization') + ->setParameter('organization', $organization);; + } return $qb; } From 387a9c3b18549ab21d52baa6539eaf4da01865d0 Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Fri, 28 Aug 2015 09:31:00 +0200 Subject: [PATCH 008/471] CRM-3997: Make all mailboxes available under System organization - fix phpcs --- .../Bundle/EmailBundle/Entity/Repository/MailboxRepository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Oro/Bundle/EmailBundle/Entity/Repository/MailboxRepository.php b/src/Oro/Bundle/EmailBundle/Entity/Repository/MailboxRepository.php index 22efdfb329d..56dd22ec26f 100644 --- a/src/Oro/Bundle/EmailBundle/Entity/Repository/MailboxRepository.php +++ b/src/Oro/Bundle/EmailBundle/Entity/Repository/MailboxRepository.php @@ -105,7 +105,7 @@ protected function createAvailableMailboxesQuery($user, $organization) if ($organization) { $qb ->andWhere('mb.organization = :organization') - ->setParameter('organization', $organization);; + ->setParameter('organization', $organization); } return $qb; From 55c0d28166cd99310fbea2f36567c6ded391f330 Mon Sep 17 00:00:00 2001 From: jakubbabiak Date: Fri, 28 Aug 2015 17:21:27 +0200 Subject: [PATCH 009/471] CRM-3997: Make all mailboxes available under System organization - Replaced repository for available mailboxes by manager. --- .../EmailBundle/Acl/Voter/EmailVoter.php | 4 +- .../Helper/EmailModelBuilderHelper.php | 13 +++- .../Datagrid/EmailQueryFactory.php | 12 ++-- .../Datagrid/MailboxChoiceList.php | 12 ++-- .../Entity/Manager/MailboxManager.php | 60 +++++++++++++++++++ .../Entity/Repository/MailboxRepository.php | 38 +----------- .../Provider/EmailActivityListProvider.php | 14 ++--- .../EmailBundle/Resources/config/services.yml | 16 ++++- .../Unit/Datagrid/EmailQueryFactoryTest.php | 30 +++------- 9 files changed, 119 insertions(+), 80 deletions(-) create mode 100644 src/Oro/Bundle/EmailBundle/Entity/Manager/MailboxManager.php diff --git a/src/Oro/Bundle/EmailBundle/Acl/Voter/EmailVoter.php b/src/Oro/Bundle/EmailBundle/Acl/Voter/EmailVoter.php index bb725b5eb08..a73d68d83f8 100644 --- a/src/Oro/Bundle/EmailBundle/Acl/Voter/EmailVoter.php +++ b/src/Oro/Bundle/EmailBundle/Acl/Voter/EmailVoter.php @@ -85,8 +85,8 @@ public function vote(TokenInterface $token, $object, array $attributes) if ($mailbox = $emailUser->getMailboxOwner() !== null && $token instanceof UsernamePasswordOrganizationToken ) { - $repo = $this->container->get('doctrine')->getRepository('OroEmailBundle:Mailbox'); - $mailboxes = $repo->findAvailableMailboxes( + $manager = $this->container->get('oro_email.mailbox.manager'); + $mailboxes = $manager->findAvailableMailboxes( $token->getUser(), $token->getOrganizationContext() ); diff --git a/src/Oro/Bundle/EmailBundle/Builder/Helper/EmailModelBuilderHelper.php b/src/Oro/Bundle/EmailBundle/Builder/Helper/EmailModelBuilderHelper.php index f6b04a9a670..03d1524dfce 100644 --- a/src/Oro/Bundle/EmailBundle/Builder/Helper/EmailModelBuilderHelper.php +++ b/src/Oro/Bundle/EmailBundle/Builder/Helper/EmailModelBuilderHelper.php @@ -13,6 +13,7 @@ use Oro\Bundle\EmailBundle\Entity\EmailOwnerInterface; use Oro\Bundle\EmailBundle\Entity\Mailbox; use Oro\Bundle\EmailBundle\Entity\Manager\EmailAddressManager; +use Oro\Bundle\EmailBundle\Entity\Manager\MailboxManager; use Oro\Bundle\EmailBundle\Exception\LoadEmailBodyException; use Oro\Bundle\EmailBundle\Model\EmailHolderInterface; use Oro\Bundle\EmailBundle\Tools\EmailAddressHelper; @@ -67,6 +68,11 @@ class EmailModelBuilderHelper */ protected $templating; + /** + * @var MailboxManager + */ + protected $mailboxManager; + /** * @param EntityRoutingHelper $entityRoutingHelper * @param EmailAddressHelper $emailAddressHelper @@ -76,6 +82,7 @@ class EmailModelBuilderHelper * @param EntityManager $entityManager * @param EmailCacheManager $emailCacheManager * @param EngineInterface $engineInterface + * @param MailboxManager $mailboxManager */ public function __construct( EntityRoutingHelper $entityRoutingHelper, @@ -85,7 +92,8 @@ public function __construct( EmailAddressManager $emailAddressManager, EntityManager $entityManager, EmailCacheManager $emailCacheManager, - EngineInterface $engineInterface + EngineInterface $engineInterface, + MailboxManager $mailboxManager ) { $this->entityRoutingHelper = $entityRoutingHelper; $this->emailAddressHelper = $emailAddressHelper; @@ -95,6 +103,7 @@ public function __construct( $this->entityManager = $entityManager; $this->emailCacheManager = $emailCacheManager; $this->templating = $engineInterface; + $this->mailboxManager = $mailboxManager; } /** @@ -262,7 +271,7 @@ public function getTargetEntity($entityClass, $entityId) */ public function getMailboxes() { - $mailboxes = $this->entityManager->getRepository('OroEmailBundle:Mailbox')->findAvailableMailboxes( + $mailboxes = $this->mailboxManager->findAvailableMailboxes( $this->getUser(), $this->getOrganization() ); diff --git a/src/Oro/Bundle/EmailBundle/Datagrid/EmailQueryFactory.php b/src/Oro/Bundle/EmailBundle/Datagrid/EmailQueryFactory.php index c72eeadcb8a..02b84bb545b 100644 --- a/src/Oro/Bundle/EmailBundle/Datagrid/EmailQueryFactory.php +++ b/src/Oro/Bundle/EmailBundle/Datagrid/EmailQueryFactory.php @@ -5,6 +5,7 @@ use Doctrine\Bundle\DoctrineBundle\Registry; use Doctrine\ORM\QueryBuilder; +use Oro\Bundle\EmailBundle\Entity\Manager\MailboxManager; use Oro\Bundle\EntityBundle\Provider\EntityNameResolver; use Oro\Bundle\EmailBundle\Entity\Provider\EmailOwnerProviderStorage; use Oro\Bundle\SecurityBundle\SecurityFacade; @@ -21,7 +22,7 @@ class EmailQueryFactory protected $fromEmailExpression; /** @var Registry */ - protected $doctrine; + protected $mailboxManager; /** @var SecurityFacade */ protected $securityFacade; @@ -29,18 +30,18 @@ class EmailQueryFactory /** * @param EmailOwnerProviderStorage $emailOwnerProviderStorage * @param EntityNameResolver $entityNameResolver - * @param Registry $doctrine + * @param MailboxManager $mailboxManager * @param SecurityFacade $securityFacade */ public function __construct( EmailOwnerProviderStorage $emailOwnerProviderStorage, EntityNameResolver $entityNameResolver, - Registry $doctrine, + MailboxManager $mailboxManager, SecurityFacade $securityFacade ) { $this->emailOwnerProviderStorage = $emailOwnerProviderStorage; $this->entityNameResolver = $entityNameResolver; - $this->doctrine = $doctrine; + $this->mailboxManager = $mailboxManager; $this->securityFacade = $securityFacade; } @@ -68,8 +69,7 @@ public function applyAcl(QueryBuilder $qb) $user = $this->securityFacade->getLoggedUser(); $organization = $this->securityFacade->getOrganization(); - $mailboxIds = $this->doctrine->getRepository('OroEmailBundle:Mailbox') - ->findAvailableMailboxIds($user, $organization); + $mailboxIds = $this->mailboxManager->findAvailableMailboxIds($user, $organization); $uoCheck = $qb->expr()->andX( $qb->expr()->eq('eu.owner', ':owner'), $qb->expr()->eq('eu.organization ', ':organization') diff --git a/src/Oro/Bundle/EmailBundle/Datagrid/MailboxChoiceList.php b/src/Oro/Bundle/EmailBundle/Datagrid/MailboxChoiceList.php index 11f7025f937..81c134df08d 100644 --- a/src/Oro/Bundle/EmailBundle/Datagrid/MailboxChoiceList.php +++ b/src/Oro/Bundle/EmailBundle/Datagrid/MailboxChoiceList.php @@ -6,6 +6,7 @@ use Oro\Bundle\EmailBundle\Entity\EmailOrigin; use Oro\Bundle\EmailBundle\Entity\Mailbox; +use Oro\Bundle\EmailBundle\Entity\Manager\MailboxManager; use Oro\Bundle\SecurityBundle\SecurityFacade; use Oro\Bundle\OrganizationBundle\Entity\Organization; @@ -17,14 +18,19 @@ class MailboxChoiceList /** @var SecurityFacade */ private $securityFacade; + /** @var MailboxManager */ + private $mailboxManager; + /** * @param Registry $doctrine * @param SecurityFacade $securityFacade + * @param MailboxManager $mailboxManager */ - public function __construct(Registry $doctrine, SecurityFacade $securityFacade) + public function __construct(Registry $doctrine, SecurityFacade $securityFacade, MailboxManager $mailboxManager) { $this->doctrine = $doctrine; $this->securityFacade = $securityFacade; + $this->mailboxManager = $mailboxManager; } /** @@ -34,10 +40,8 @@ public function __construct(Registry $doctrine, SecurityFacade $securityFacade) */ public function getChoiceList() { - $repo = $this->doctrine->getRepository('OroEmailBundle:Mailbox'); - /** @var Mailbox[] $systemMailboxes */ - $systemMailboxes = $repo->findAvailableMailboxes( + $systemMailboxes = $this->mailboxManager->findAvailableMailboxes( $this->securityFacade->getLoggedUser(), $this->getOrganization() ); diff --git a/src/Oro/Bundle/EmailBundle/Entity/Manager/MailboxManager.php b/src/Oro/Bundle/EmailBundle/Entity/Manager/MailboxManager.php new file mode 100644 index 00000000000..05c7c1d407f --- /dev/null +++ b/src/Oro/Bundle/EmailBundle/Entity/Manager/MailboxManager.php @@ -0,0 +1,60 @@ +registry = $registry; + } + + /** + * Returns a list of ids of mailboxes available to user logged under organization. + * + * @param User|integer $user User or user id + * @param Organization $organization + * + * @return array Array of ids + */ + public function findAvailableMailboxIds($user, $organization) + { + $mailboxes = $this->findAvailableMailboxes($user, $organization); + + $ids = []; + foreach ($mailboxes as $mailbox) { + $ids[] = $mailbox->getId(); + } + + return $ids; + } + + /** + * Returns a list of mailboxes available to user logged under organization. + * + * @param User|integer $user User or user id + * @param Organization $organization + * + * @return Collection|Mailbox[] Array or collection of Mailboxes + */ + public function findAvailableMailboxes($user, Organization $organization) + { + $qb = $this->registry->getRepository('OroEmailBundle:Mailbox') + ->createAvailableMailboxesQuery($user, $organization); + + return $qb->getQuery()->getResult(); + } +} diff --git a/src/Oro/Bundle/EmailBundle/Entity/Repository/MailboxRepository.php b/src/Oro/Bundle/EmailBundle/Entity/Repository/MailboxRepository.php index 56dd22ec26f..73fefd53e29 100644 --- a/src/Oro/Bundle/EmailBundle/Entity/Repository/MailboxRepository.php +++ b/src/Oro/Bundle/EmailBundle/Entity/Repository/MailboxRepository.php @@ -34,47 +34,15 @@ public function findOneByEmail($email) } /** - * Returns a list of mailboxes available to user. + * Creates query for mailboxes available to user logged under organization. + * If no organization is provided, does not filter by it (useful when looking for mailboxes across organizations). * - * @param User|integer $user User or user id - * @param Organization $organization - * - * @return Collection|Mailbox[] Array or collection of Mailboxes - */ - public function findAvailableMailboxes($user, $organization) - { - $qb = $this->createAvailableMailboxesQuery($user, $organization); - - return $qb->getQuery()->getResult(); - } - - /** - * Returns a list of ids of mailboxes available to user. - * - * @param User|integer $user User or user id - * @param Organization $organization - * - * @return array Array of ids - */ - public function findAvailableMailboxIds($user, $organization) - { - $mailboxes = $this->findAvailableMailboxes($user, $organization); - - $ids = []; - foreach ($mailboxes as $mailbox) { - $ids[] = $mailbox->getId(); - } - - return $ids; - } - - /** * @param User|integer $user User or user id * @param Organization|integer|null $organization * * @return \Doctrine\ORM\QueryBuilder */ - protected function createAvailableMailboxesQuery($user, $organization) + public function createAvailableMailboxesQuery($user, $organization = null) { if (!$user instanceof User) { $user = $this->getEntityManager()->getRepository('OroUserBundle:User')->find($user); diff --git a/src/Oro/Bundle/EmailBundle/Provider/EmailActivityListProvider.php b/src/Oro/Bundle/EmailBundle/Provider/EmailActivityListProvider.php index f6b995150b0..37a8cac4a22 100644 --- a/src/Oro/Bundle/EmailBundle/Provider/EmailActivityListProvider.php +++ b/src/Oro/Bundle/EmailBundle/Provider/EmailActivityListProvider.php @@ -68,14 +68,14 @@ class EmailActivityListProvider implements /** @var HtmlTagHelper */ protected $htmlTagHelper; - /** @var ServiceLink */ + /** @var ServiceLink */ protected $securityContextLink; /** @var ServiceLink */ protected $securityFacadeLink; - /** @var MailboxProcessStorage */ - protected $mailboxProcessStorage; + /** @var ServiceLink */ + protected $mailboxProcessStorageLink; /** * @param DoctrineHelper $doctrineHelper @@ -86,7 +86,7 @@ class EmailActivityListProvider implements * @param EmailThreadProvider $emailThreadProvider * @param HtmlTagHelper $htmlTagHelper * @param ServiceLink $securityFacadeLink - * @param MailboxProcessStorage $mailboxProcessStorage + * @param ServiceLink $mailboxProcessStorageLink */ public function __construct( DoctrineHelper $doctrineHelper, @@ -97,7 +97,7 @@ public function __construct( EmailThreadProvider $emailThreadProvider, HtmlTagHelper $htmlTagHelper, ServiceLink $securityFacadeLink, - MailboxProcessStorage $mailboxProcessStorage + ServiceLink $mailboxProcessStorageLink ) { $this->doctrineHelper = $doctrineHelper; $this->doctrineRegistryLink = $doctrineRegistryLink; @@ -107,7 +107,7 @@ public function __construct( $this->emailThreadProvider = $emailThreadProvider; $this->htmlTagHelper = $htmlTagHelper; $this->securityFacadeLink = $securityFacadeLink; - $this->mailboxProcessStorage = $mailboxProcessStorage; + $this->mailboxProcessStorageLink = $mailboxProcessStorageLink; } /** @@ -231,7 +231,7 @@ public function getOrganization($activityEntity) return $token->getOrganizationContext(); } - $processes = $this->mailboxProcessStorage->getProcesses(); + $processes = $this->mailboxProcessStorageLink->getService()->getProcesses(); foreach ($processes as $process) { $settingsClass = $process->getSettingsEntityFQCN(); diff --git a/src/Oro/Bundle/EmailBundle/Resources/config/services.yml b/src/Oro/Bundle/EmailBundle/Resources/config/services.yml index 2ae430ad7c6..5966b307446 100644 --- a/src/Oro/Bundle/EmailBundle/Resources/config/services.yml +++ b/src/Oro/Bundle/EmailBundle/Resources/config/services.yml @@ -121,6 +121,7 @@ parameters: oro_email.listener.mailbox.authorization.class: Oro\Bundle\EmailBundle\EventListener\MailboxAuthorizationListener oro_email.listener.datagrid.mailbox_grid.class: Oro\Bundle\EmailBundle\EventListener\Datagrid\MailboxGridListener oro_email.autocomplete.mailbox_user_search_handler.class: Oro\Bundle\EmailBundle\Autocomplete\MailboxUserSearchHandler + oro_email.mailbox.manager.class: Oro\Bundle\EmailBundle\Entity\Manager\MailboxManager # Workflow conditions oro_email.workflow.condition.instanceof.class: Oro\Bundle\EmailBundle\Model\Condition\IsInstanceOf @@ -212,6 +213,7 @@ services: - @doctrine.orm.entity_manager - @oro_email.email.cache.manager - @templating + - @oro_email.mailbox.manager oro_email.email.entity.builder: class: %oro_email.email.entity.builder.class% @@ -442,7 +444,7 @@ services: arguments: - @oro_email.email.owner.provider.storage - @oro_entity.entity_name_resolver - - @doctrine + - @oro_email.mailbox.manager - @oro_security.security_facade oro_email.emailtemplate.datagrid_view_list: @@ -610,6 +612,10 @@ services: tags: - { name: kernel.event_listener, event: oro_search.prepare_entity_map, method: prepareEntityMapEvent, priority: 10 } + oro_email.mailbox.process_storage.link: + tags: + - { name: oro_service_link, service: oro_email.mailbox.process_storage } + oro_email.activity_list.provider: class: %oro_email.activity_list.provider.class% arguments: @@ -621,7 +627,7 @@ services: - @oro_email.email.thread.provider - @oro_ui.html_tag_helper - @oro_security.security_facade.link - - @oro_email.mailbox.process_storage + - @oro_email.mailbox.process_storage.link calls: - [ setSecurityContextLink, [@security.context.link] ] tags: @@ -802,6 +808,7 @@ services: arguments: - @doctrine - @oro_security.security_facade + - @oro_email.mailbox.manager oro_email.mailbox.manager.api: class: %oro_email.mailbox.manager.api.class% @@ -854,6 +861,11 @@ services: - { name: kernel.event_listener, event: oro_datagrid.datagrid.build.pre, method: onPreBuild } - { name: kernel.event_listener, event: oro_datagrid.datagrid.build.after.base-mailboxes-grid, method: onBuildAfter } + oro_email.mailbox.manager: + class: %oro_email.mailbox.manager.class% + arguments: + - @doctrine + oro_email.workflow.condition.instanceof: class: %oro_email.workflow.condition.instanceof.class% tags: diff --git a/src/Oro/Bundle/EmailBundle/Tests/Unit/Datagrid/EmailQueryFactoryTest.php b/src/Oro/Bundle/EmailBundle/Tests/Unit/Datagrid/EmailQueryFactoryTest.php index 75ca70423f3..e3124589bbc 100644 --- a/src/Oro/Bundle/EmailBundle/Tests/Unit/Datagrid/EmailQueryFactoryTest.php +++ b/src/Oro/Bundle/EmailBundle/Tests/Unit/Datagrid/EmailQueryFactoryTest.php @@ -2,11 +2,9 @@ namespace Oro\Bundle\EmailBundle\Tests\Unit\Datagrid; -use Doctrine\Bundle\DoctrineBundle\Registry; - use Oro\Bundle\EmailBundle\Datagrid\EmailQueryFactory; +use Oro\Bundle\EmailBundle\Entity\Manager\MailboxManager; use Oro\Bundle\EmailBundle\Entity\Provider\EmailOwnerProviderStorage; -use Oro\Bundle\EmailBundle\Entity\Repository\MailboxRepository; use Oro\Bundle\EntityBundle\Provider\EntityNameResolver; use Oro\Bundle\OrganizationBundle\Entity\Organization; use Oro\Bundle\SecurityBundle\SecurityFacade; @@ -28,14 +26,11 @@ class EmailQueryFactoryTest extends OrmTestCase /** @var EmailQueryFactory */ protected $factory; - /** @var Registry */ - protected $doctrine; - /** @var SecurityFacade */ protected $securityFacade; - /** @var MailboxRepository */ - protected $mailboxRepository; + /** @var MailboxManager */ + protected $mailboxManager; public function setUp() { @@ -44,19 +39,10 @@ public function setUp() $this->entityNameResolver = $this->getMockBuilder('Oro\Bundle\EntityBundle\Provider\EntityNameResolver') ->disableOriginalConstructor()->getMock(); - $this->mailboxRepository = $this->getMockBuilder('Oro\Bundle\EmailBundle\Entity\Repository\MailboxRepository') - ->disableOriginalConstructor() - ->getMock(); - - $this->doctrine = $this->getMockBuilder('Doctrine\Bundle\DoctrineBundle\Registry') + $this->mailboxManager = $this->getMockBuilder('Oro\Bundle\EmailBundle\Entity\Manager\MailboxManager') ->disableOriginalConstructor() ->getMock(); - $this->doctrine->expects($this->any()) - ->method('getRepository') - ->with($this->equalTo('OroEmailBundle:Mailbox')) - ->will($this->returnValue($this->mailboxRepository)); - $this->securityFacade = $this->getMockBuilder('Oro\Bundle\SecurityBundle\SecurityFacade') ->disableOriginalConstructor() ->getMock(); @@ -64,7 +50,7 @@ public function setUp() $this->factory = new EmailQueryFactory( $this->providerStorage, $this->entityNameResolver, - $this->doctrine, + $this->mailboxManager, $this->securityFacade ); } @@ -75,7 +61,7 @@ public function tearDown() $this->factory, $this->entityNameResolver, $this->providerStorage, - $this->mailboxRepository, + $this->mailboxManager, $this->doctrine ); } @@ -141,7 +127,7 @@ public function testFilterQueryByUserIdWhenMailboxesAreFound() ->method('getOrganization') ->will($this->returnValue($organization)); - $this->mailboxRepository->expects($this->any()) + $this->mailboxManager->expects($this->any()) ->method('findAvailableMailboxIds') ->with($user, $organization) ->will($this->returnValue([1, 3, 5])); @@ -174,7 +160,7 @@ public function testFilterQueryByUserIdWhenNoMailboxesFound() ->method('getOrganization') ->will($this->returnValue($organization)); - $this->mailboxRepository->expects($this->any()) + $this->mailboxManager->expects($this->any()) ->method('findAvailableMailboxIds') ->with($user, $organization) ->will($this->returnValue([1, 3, 5])); From 61698ae68a43579857218fe69f77abfbe42058a3 Mon Sep 17 00:00:00 2001 From: Ignat Shcheglovskyi Date: Tue, 22 Sep 2015 16:27:13 -0700 Subject: [PATCH 010/471] AEIV-41: Organization Specific Roles - update upgrade instruction --- UPGRADE-1.9.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/UPGRADE-1.9.md b/UPGRADE-1.9.md index 8fb46f3e5dc..af9fa773bba 100644 --- a/UPGRADE-1.9.md +++ b/UPGRADE-1.9.md @@ -9,3 +9,16 @@ UPGRADE FROM 1.8 to 1.9 - `Oro\Bundle\ActivityListBundle\Model\ActivityListProviderInterface::getOwner` added. ####EntityConfigBundle - Removed `optionSet` field type deprecated since v1.4. Existing options sets are converted to `Select` or `Multi-Select` automatically during the Platform update. + +####NoteBundle + - Added parameter `DoctrineHelper $doctrineHelper` to constructor of `\Oro\Bundle\NoteBundle\Placeholder\PlaceholderFilter` + +####SecurityBundle +- `Oro\Bundle\SecurityBundle\Authentication\Token\UsernamePasswordOrganizationTokenFactoryInterface` and its implementation `Oro\Bundle\SecurityBundle\Authentication\Token\UsernamePasswordOrganizationTokenFactory` were introduced to encapsulate creation of `UsernamePasswordOrganizationToken` in `Oro\Bundle\SecurityBundle\Authentication\Provider\UsernamePasswordOrganizationAuthenticationProvider` and `Oro\Bundle\SecurityBundle\Http\Firewall\OrganizationBasicAuthenticationListener` +- `Oro\Bundle\SecurityBundle\Authentication\Token\OrganizationRememberMeTokenFactoryInterface` and its implementation `Oro\Bundle\SecurityBundle\Authentication\Token\OrganizationRememberMeTokenFactory` were introduced to encapsulate creation of `OrganizationRememberMeToken` in `Oro\Bundle\SecurityBundle\Authentication\Provider\UsernamePasswordOrganizationAuthenticationProvider` + +####SSOBundle +- `Oro\Bundle\SSOBundle\Security\OAuthTokenFactoryInterface` and its implementation `Oro\Bundle\SSOBundle\Security\OAuthTokenFactory` were introduced to encapsulate creation of `OAuthToken` in `Oro\Bundle\SSOBundle\Security\OAuthProvider` + +####UserBundle +- `Oro\Bundle\UserBundle\Security\WsseTokenFactoryInterface` and its implementation `Oro\Bundle\UserBundle\Security\WsseTokenFactory` were introduced to encapsulate creation of `WsseToken` in `Oro\Bundle\UserBundle\Security\WsseAuthProvider` From 70cc16992c518260530730d36a25ec6f65b27e21 Mon Sep 17 00:00:00 2001 From: Ignat Shcheglovskyi Date: Mon, 28 Sep 2015 14:18:58 -0700 Subject: [PATCH 011/471] AEIV-172: Review Organization Specific Roles - fixed ResultRecord::getRootEntity to get root entity from result - updated roles-grid, added root entity to select to be able to check permissions in enterprise edition --- .../Datasource/ResultRecord.php | 6 ++-- .../Unit/Datasource/ResultRecordTest.php | 35 +++++++++++++++++++ .../FormBundle/Autocomplete/SearchHandler.php | 1 - .../UserBundle/Resources/config/datagrid.yml | 3 ++ 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/Oro/Bundle/DataGridBundle/Datasource/ResultRecord.php b/src/Oro/Bundle/DataGridBundle/Datasource/ResultRecord.php index 818e4d95260..c715e7f8538 100644 --- a/src/Oro/Bundle/DataGridBundle/Datasource/ResultRecord.php +++ b/src/Oro/Bundle/DataGridBundle/Datasource/ResultRecord.php @@ -76,8 +76,10 @@ public function getValue($name) */ public function getRootEntity() { - if (array_key_exists(0, $this->valueContainers) && is_object($this->valueContainers[0])) { - return $this->valueContainers[0]; + foreach ($this->valueContainers as $value) { + if (is_object($value)) { + return $value; + } } return null; diff --git a/src/Oro/Bundle/DataGridBundle/Tests/Unit/Datasource/ResultRecordTest.php b/src/Oro/Bundle/DataGridBundle/Tests/Unit/Datasource/ResultRecordTest.php index 47c8b273331..479d01989ea 100644 --- a/src/Oro/Bundle/DataGridBundle/Tests/Unit/Datasource/ResultRecordTest.php +++ b/src/Oro/Bundle/DataGridBundle/Tests/Unit/Datasource/ResultRecordTest.php @@ -74,4 +74,39 @@ public function getValueProvider() ], ]; } + + /** + * @dataProvider getRootEntityProvider + */ + public function testGetRootEntity($data, $expectedValue) + { + $resultRecord = new ResultRecord($data); + + $this->assertEquals($expectedValue, $resultRecord->getRootEntity()); + } + + public function getRootEntityProvider() + { + $obj = new \stdClass(); + $obj->item1 = 'val1'; + + return [ + [ + 'data' => [], + 'expectedValue' => null + ], + [ + 'data' => ['item1' => 'val1'], + 'expectedValue' => null + ], + [ + 'data' => $obj, + 'expectedValue' => $obj + ], + [ + 'data' => [['item1' => 'val1'], $obj], + 'expectedValue' => $obj + ], + ]; + } } diff --git a/src/Oro/Bundle/FormBundle/Autocomplete/SearchHandler.php b/src/Oro/Bundle/FormBundle/Autocomplete/SearchHandler.php index 79fb764aee5..fdaa55db9e1 100644 --- a/src/Oro/Bundle/FormBundle/Autocomplete/SearchHandler.php +++ b/src/Oro/Bundle/FormBundle/Autocomplete/SearchHandler.php @@ -230,7 +230,6 @@ protected function findById($query) return $this->getEntitiesByIds(explode(',', $query)); } - /** * @param array $items * @return array diff --git a/src/Oro/Bundle/UserBundle/Resources/config/datagrid.yml b/src/Oro/Bundle/UserBundle/Resources/config/datagrid.yml index 2c44421ab21..01f6ad47d13 100644 --- a/src/Oro/Bundle/UserBundle/Resources/config/datagrid.yml +++ b/src/Oro/Bundle/UserBundle/Resources/config/datagrid.yml @@ -196,6 +196,7 @@ datagrid: type: orm query: select: + - r - r.id - r.role - r.label @@ -237,6 +238,8 @@ datagrid: icon: trash acl_resource: oro_user_role_delete + action_configuration: @oro_user.role.datagrid_helper->getActionConfigurationClosure + sorters: columns: label: From 66e1dfc4c5f37fcaa8138bc305c55daf8afe85ac Mon Sep 17 00:00:00 2001 From: AlexandrDmitriev Date: Wed, 30 Sep 2015 17:25:32 +0300 Subject: [PATCH 012/471] AEIV-172: Review Organization Specific Roles --- ...UsernamePasswordOrganizationAuthenticationProvider.php | 8 ++++---- .../Token/UsernamePasswordOrganizationTokenFactory.php | 7 +------ src/Oro/Bundle/UserBundle/Resources/config/datagrid.yml | 2 -- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/Oro/Bundle/SecurityBundle/Authentication/Provider/UsernamePasswordOrganizationAuthenticationProvider.php b/src/Oro/Bundle/SecurityBundle/Authentication/Provider/UsernamePasswordOrganizationAuthenticationProvider.php index 9b5d2449266..99051f757f7 100644 --- a/src/Oro/Bundle/SecurityBundle/Authentication/Provider/UsernamePasswordOrganizationAuthenticationProvider.php +++ b/src/Oro/Bundle/SecurityBundle/Authentication/Provider/UsernamePasswordOrganizationAuthenticationProvider.php @@ -9,19 +9,19 @@ use Oro\Bundle\UserBundle\Entity\User; use Oro\Bundle\SecurityBundle\Authentication\Guesser\UserOrganizationGuesser; -use Oro\Bundle\SecurityBundle\Authentication\Token\UsernamePasswordOrganizationTokenFactoryInterface as TokenFactory; +use Oro\Bundle\SecurityBundle\Authentication\Token\UsernamePasswordOrganizationTokenFactoryInterface; class UsernamePasswordOrganizationAuthenticationProvider extends DaoAuthenticationProvider { /** - * @var TokenFactory + * @var UsernamePasswordOrganizationTokenFactoryInterface */ protected $tokenFactory; /** - * @param TokenFactory $tokenFactory + * @param UsernamePasswordOrganizationTokenFactoryInterface $tokenFactory */ - public function setTokenFactory(TokenFactory $tokenFactory) + public function setTokenFactory(UsernamePasswordOrganizationTokenFactoryInterface $tokenFactory) { $this->tokenFactory = $tokenFactory; } diff --git a/src/Oro/Bundle/SecurityBundle/Authentication/Token/UsernamePasswordOrganizationTokenFactory.php b/src/Oro/Bundle/SecurityBundle/Authentication/Token/UsernamePasswordOrganizationTokenFactory.php index 635a68c545f..762ef1464b1 100644 --- a/src/Oro/Bundle/SecurityBundle/Authentication/Token/UsernamePasswordOrganizationTokenFactory.php +++ b/src/Oro/Bundle/SecurityBundle/Authentication/Token/UsernamePasswordOrganizationTokenFactory.php @@ -7,12 +7,7 @@ class UsernamePasswordOrganizationTokenFactory implements UsernamePasswordOrganizationTokenFactoryInterface { /** - * @param string $user - * @param string $credentials - * @param string $providerKey - * @param Organization $organizationContext - * @param array $roles - * @return UsernamePasswordOrganizationToken + * {@inheritdoc} */ public function create($user, $credentials, $providerKey, Organization $organizationContext, array $roles = []) { diff --git a/src/Oro/Bundle/UserBundle/Resources/config/datagrid.yml b/src/Oro/Bundle/UserBundle/Resources/config/datagrid.yml index e6b5bda8990..f954b273a2b 100644 --- a/src/Oro/Bundle/UserBundle/Resources/config/datagrid.yml +++ b/src/Oro/Bundle/UserBundle/Resources/config/datagrid.yml @@ -238,8 +238,6 @@ datagrid: icon: trash acl_resource: oro_user_role_delete - action_configuration: @oro_user.role.datagrid_helper->getActionConfigurationClosure - sorters: columns: label: From 0d1533936316d73c2cf2aa5057f8393982d9a99c Mon Sep 17 00:00:00 2001 From: AlexandrDmitriev Date: Wed, 30 Sep 2015 17:41:32 +0300 Subject: [PATCH 013/471] AEIV-172: Review Organization Specific Roles - fix code styles --- .../OrganizationRememberMeAuthenticationProvider.php | 8 ++++---- .../Firewall/OrganizationBasicAuthenticationListener.php | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Oro/Bundle/SecurityBundle/Authentication/Provider/OrganizationRememberMeAuthenticationProvider.php b/src/Oro/Bundle/SecurityBundle/Authentication/Provider/OrganizationRememberMeAuthenticationProvider.php index b3dd010e2e2..0f7341b80c9 100644 --- a/src/Oro/Bundle/SecurityBundle/Authentication/Provider/OrganizationRememberMeAuthenticationProvider.php +++ b/src/Oro/Bundle/SecurityBundle/Authentication/Provider/OrganizationRememberMeAuthenticationProvider.php @@ -9,19 +9,19 @@ use Oro\Bundle\UserBundle\Entity\User; use Oro\Bundle\SecurityBundle\Authentication\Guesser\UserOrganizationGuesser; -use Oro\Bundle\SecurityBundle\Authentication\Token\OrganizationRememberMeTokenFactoryInterface as TokenFactory; +use Oro\Bundle\SecurityBundle\Authentication\Token\OrganizationRememberMeTokenFactoryInterface; class OrganizationRememberMeAuthenticationProvider extends RememberMeAuthenticationProvider { /** - * @var TokenFactory + * @var OrganizationRememberMeTokenFactoryInterface */ protected $tokenFactory; /** - * @param TokenFactory $tokenFactory + * @param OrganizationRememberMeTokenFactoryInterface $tokenFactory */ - public function setTokenFactory(TokenFactory $tokenFactory) + public function setTokenFactory(OrganizationRememberMeTokenFactoryInterface $tokenFactory) { $this->tokenFactory = $tokenFactory; } diff --git a/src/Oro/Bundle/SecurityBundle/Http/Firewall/OrganizationBasicAuthenticationListener.php b/src/Oro/Bundle/SecurityBundle/Http/Firewall/OrganizationBasicAuthenticationListener.php index 5268ccceda0..b1bf33e6ce7 100644 --- a/src/Oro/Bundle/SecurityBundle/Http/Firewall/OrganizationBasicAuthenticationListener.php +++ b/src/Oro/Bundle/SecurityBundle/Http/Firewall/OrganizationBasicAuthenticationListener.php @@ -13,7 +13,7 @@ use Oro\Bundle\OrganizationBundle\Entity\Manager\OrganizationManager; use Oro\Bundle\SecurityBundle\Authentication\Token\OrganizationContextTokenInterface; -use Oro\Bundle\SecurityBundle\Authentication\Token\UsernamePasswordOrganizationTokenFactoryInterface as TokenFactory; +use Oro\Bundle\SecurityBundle\Authentication\Token\UsernamePasswordOrganizationTokenFactoryInterface; class OrganizationBasicAuthenticationListener { @@ -39,7 +39,7 @@ class OrganizationBasicAuthenticationListener protected $manager; /** - * @var TokenFactory + * @var UsernamePasswordOrganizationTokenFactoryInterface */ protected $tokenFactory; @@ -73,9 +73,9 @@ public function __construct( } /** - * @param TokenFactory $tokenFactory + * @param UsernamePasswordOrganizationTokenFactoryInterface $tokenFactory */ - public function setTokenFactory(TokenFactory $tokenFactory) + public function setTokenFactory(UsernamePasswordOrganizationTokenFactoryInterface $tokenFactory) { $this->tokenFactory = $tokenFactory; } From 42de3a7a72dd3d0e94e54166468c08133fdfe1f8 Mon Sep 17 00:00:00 2001 From: AlexandrDmitriev Date: Wed, 30 Sep 2015 20:20:01 +0300 Subject: [PATCH 014/471] AEIV-172: Review Organization Specific Roles - fix grid --- .../UserBundle/Datagrid/RoleGridHelper.php | 41 +++++++++++++++++++ .../UserBundle/Resources/config/datagrid.yml | 2 + .../UserBundle/Resources/config/services.yml | 7 ++++ 3 files changed, 50 insertions(+) create mode 100644 src/Oro/Bundle/UserBundle/Datagrid/RoleGridHelper.php diff --git a/src/Oro/Bundle/UserBundle/Datagrid/RoleGridHelper.php b/src/Oro/Bundle/UserBundle/Datagrid/RoleGridHelper.php new file mode 100644 index 00000000000..58c2298afdd --- /dev/null +++ b/src/Oro/Bundle/UserBundle/Datagrid/RoleGridHelper.php @@ -0,0 +1,41 @@ +authorizationChecker = $authorizationChecker; + } + + /** + * Returns callback for configuration of grid/actions visibility per row + * + * @return callable + */ + public function getActionConfigurationClosure() + { + return function (ResultRecordInterface $record) { + $role = $record->getRootEntity(); + return [ + 'update' => $this->authorizationChecker->isGranted('EDIT', $role), + 'delete' => $this->authorizationChecker->isGranted('DELETE', $role), + ]; + }; + } +} diff --git a/src/Oro/Bundle/UserBundle/Resources/config/datagrid.yml b/src/Oro/Bundle/UserBundle/Resources/config/datagrid.yml index f954b273a2b..e6b5bda8990 100644 --- a/src/Oro/Bundle/UserBundle/Resources/config/datagrid.yml +++ b/src/Oro/Bundle/UserBundle/Resources/config/datagrid.yml @@ -238,6 +238,8 @@ datagrid: icon: trash acl_resource: oro_user_role_delete + action_configuration: @oro_user.role.datagrid_helper->getActionConfigurationClosure + sorters: columns: label: diff --git a/src/Oro/Bundle/UserBundle/Resources/config/services.yml b/src/Oro/Bundle/UserBundle/Resources/config/services.yml index fb7dc629de5..c89d87a305c 100644 --- a/src/Oro/Bundle/UserBundle/Resources/config/services.yml +++ b/src/Oro/Bundle/UserBundle/Resources/config/services.yml @@ -37,6 +37,8 @@ parameters: oro_user.token.factory.wsse.class: Oro\Bundle\UserBundle\Security\WsseTokenFactory + oro_user.role.datagrid_helper.class: Oro\Bundle\UserBundle\Datagrid\RoleGridHelper + services: oro_user.manager: class: %oro_user.manager.class% @@ -300,3 +302,8 @@ services: oro_user.token.factory.wsse: class: %oro_user.token.factory.wsse.class% + + oro_user.role.datagrid_helper: + class: %oro_user.role.datagrid_helper.class% + arguments: + - @security.authorization_checker From 4a5a86e68f8db9baaa856b951ebb8b13e4d947d0 Mon Sep 17 00:00:00 2001 From: AlexandrDmitriev Date: Thu, 1 Oct 2015 18:09:08 +0300 Subject: [PATCH 015/471] AEIV-172: Review Organization Specific Roles - fix selenium tests --- .../Bundle/UserBundle/Tests/Selenium/Pages/User.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Oro/Bundle/UserBundle/Tests/Selenium/Pages/User.php b/src/Oro/Bundle/UserBundle/Tests/Selenium/Pages/User.php index 80938f6f29b..1991983c979 100644 --- a/src/Oro/Bundle/UserBundle/Tests/Selenium/Pages/User.php +++ b/src/Oro/Bundle/UserBundle/Tests/Selenium/Pages/User.php @@ -254,17 +254,22 @@ public function setRoles($roles = array(), $oneOf = false) if ($condition != '') { $condition .= ' or '; } - $condition .= "normalize-space(text()) = '{$role}'"; + $condition .= "contains(., '{$role}')"; } $element = $this->roles->element( - $this->test->using('xpath')->value("div[label[{$condition}]]/input") + $this->test->using('xpath')->value( + "div[@data-ftid='oro_user_user_form_roles']/div[label[{$condition}]]/input" + ) ); $this->test->moveto($element); $element->click(); } else { foreach ($roles as $role) { $element = $this->roles->element( - $this->test->using('xpath')->value("div[label[normalize-space(text()) = '{$role}']]/input") + $this->test->using('xpath')->value( + "div[@data-ftid='oro_user_user_form_roles']". + "/div[label[normalize-space(text()) = '{$role}']]/input" + ) ); $this->test->moveto($element); $element->click(); From c1573fa5fb6ef9c41ba6bbbe094b55394b725801 Mon Sep 17 00:00:00 2001 From: AlexandrDmitriev Date: Fri, 2 Oct 2015 13:40:25 +0300 Subject: [PATCH 016/471] AEIV-172: Review Organization Specific Roles - fix selenium tests --- src/Oro/Bundle/UserBundle/Tests/Selenium/Pages/User.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Oro/Bundle/UserBundle/Tests/Selenium/Pages/User.php b/src/Oro/Bundle/UserBundle/Tests/Selenium/Pages/User.php index 1991983c979..816258cef29 100644 --- a/src/Oro/Bundle/UserBundle/Tests/Selenium/Pages/User.php +++ b/src/Oro/Bundle/UserBundle/Tests/Selenium/Pages/User.php @@ -258,7 +258,7 @@ public function setRoles($roles = array(), $oneOf = false) } $element = $this->roles->element( $this->test->using('xpath')->value( - "div[@data-ftid='oro_user_user_form_roles']/div[label[{$condition}]]/input" + "//div[@data-ftid='oro_user_user_form_roles']/div[label[{$condition}]]/input" ) ); $this->test->moveto($element); @@ -267,8 +267,8 @@ public function setRoles($roles = array(), $oneOf = false) foreach ($roles as $role) { $element = $this->roles->element( $this->test->using('xpath')->value( - "div[@data-ftid='oro_user_user_form_roles']". - "/div[label[normalize-space(text()) = '{$role}']]/input" + "//div[@data-ftid='oro_user_user_form_roles']". + "/div[label[contains(normalize-space(text()), '{$role}')]]/input" ) ); $this->test->moveto($element); From 394ac26cf454712aca691721cb29d4332135d6c5 Mon Sep 17 00:00:00 2001 From: AlexandrDmitriev Date: Fri, 2 Oct 2015 16:35:28 +0300 Subject: [PATCH 017/471] AEIV-172: Review Organization Specific Roles - fix selenium tests --- .../UserBundle/Tests/Selenium/RolesTest.php | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/Oro/Bundle/UserBundle/Tests/Selenium/RolesTest.php b/src/Oro/Bundle/UserBundle/Tests/Selenium/RolesTest.php index 66dfc8b1daf..44fc48f20c6 100644 --- a/src/Oro/Bundle/UserBundle/Tests/Selenium/RolesTest.php +++ b/src/Oro/Bundle/UserBundle/Tests/Selenium/RolesTest.php @@ -16,25 +16,26 @@ class RolesTest extends Selenium2TestCase protected $newRole = array('LABEL' => 'NEW_LABEL_', 'ROLE_NAME' => 'NEW_ROLE_'); protected $defaultRoles = array( - 'header' => array( - 'ROLE' => 'ROLE', - 'LABEL' => 'LABEL', - '' => 'ACTION' + 'header' => array( + 'ROLE' => 'ROLE', + 'LABEL' => 'LABEL', + 'ORGANIZATION' => 'ORGANIZATION', + '' => 'ACTION' ), - 'ROLE_MANAGER' => array( + 'ROLE_MANAGER' => array( 'ROLE_MANAGER' => 'ROLE_MANAGER', - 'Manager' => 'Manager', - '...' => 'ACTION' + 'Manager' => 'Manager', + '...' => 'ACTION' ), 'ROLE_ADMINISTRATOR' => array( 'ROLE_ADMINISTRATOR' => 'ROLE_ADMINISTRATOR', - 'Administrator' => 'Administrator', - '...' => 'ACTION' + 'Administrator' => 'Administrator', + '...' => 'ACTION' ), - 'ROLE_USER' => array( + 'ROLE_USER' => array( 'ROLE_USER' => 'ROLE_USER', - 'User' => 'User', - '...' => 'ACTION' + 'User' => 'User', + '...' => 'ACTION' ) ); @@ -86,12 +87,14 @@ public function testRolesAdd() $randomPrefix = WebTestCase::generateRandomString(5); $login = $this->login(); + $roleLabel = $this->newRole['LABEL'] . $randomPrefix; + /** @var Roles $login */ $roles = $login->openRoles('Oro\Bundle\UserBundle') ->assertTitle('All - Roles - User Management - System') ->add() ->assertTitle('Create Role - Roles - User Management - System') - ->setLabel($this->newRole['LABEL'] . $randomPrefix) + ->setLabel($roleLabel) ->save() ->assertMessage('Role saved') ->close(); @@ -99,7 +102,7 @@ public function testRolesAdd() //verify new Role $roles->refresh(); - static::assertTrue($roles->entityExists(array('name' => $this->newRole['LABEL'] . $randomPrefix))); + static::assertTrue($roles->entityExists(array('name' => $roleLabel))); return $randomPrefix; } From 7a202419de258205a7d090f2a6cf7405ab765672 Mon Sep 17 00:00:00 2001 From: Sergey Zhuravel Date: Fri, 9 Oct 2015 14:52:09 +0300 Subject: [PATCH 018/471] BB-1334: Add STI for audit --- .../DataAuditBundle/Entity/AbstractAudit.php | 10 +++++++- .../Entity/AbstractAuditField.php | 9 ++++++- .../Bundle/DataAuditBundle/Entity/Audit.php | 3 --- .../DataAuditBundle/Entity/AuditField.php | 5 +--- .../Schema/OroDataAuditBundleInstaller.php | 9 ++++++- .../Schema/v1_6/OroDataAuditBundle.php | 25 +++++++++++++++++++ 6 files changed, 51 insertions(+), 10 deletions(-) create mode 100644 src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/OroDataAuditBundle.php diff --git a/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAudit.php b/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAudit.php index cf72f0cb974..db53da55a05 100644 --- a/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAudit.php +++ b/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAudit.php @@ -12,7 +12,15 @@ use Oro\Bundle\UserBundle\Entity\AbstractUser; /** - * @ORM\MappedSuperclass + * @ORM\MappedSuperclass() + * @ORM\Entity() + * @ORM\Table(name="oro_audit", indexes={ + * @ORM\Index(name="idx_oro_audit_logged_at", columns={"logged_at"}), + * @ORM\Index(name="idx_oro_audit_type", columns={"type"}) + * }) + * @ORM\InheritanceType("SINGLE_TABLE") + * @ORM\DiscriminatorColumn(name="type", type="string") + * @ORM\DiscriminatorMap({"audit" = "Audit"}) */ abstract class AbstractAudit extends AbstractLogEntry { diff --git a/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAuditField.php b/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAuditField.php index 8ffed588692..aa2e8bafab3 100644 --- a/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAuditField.php +++ b/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAuditField.php @@ -9,7 +9,14 @@ use Oro\Bundle\DataAuditBundle\Model\AuditFieldTypeRegistry; /** - * @ORM\MappedSuperclass + * @ORM\MappedSuperclass() + * @ORM\Entity() + * @ORM\Table(name="oro_audit_field", indexes={ + * @ORM\Index(name="idx_oro_audit_field_type", columns={"type"}) + * }) + * @ORM\InheritanceType("SINGLE_TABLE") + * @ORM\DiscriminatorColumn(name="type", type="string") + * @ORM\DiscriminatorMap({"audit_field" = "AuditField"}) */ abstract class AbstractAuditField { diff --git a/src/Oro/Bundle/DataAuditBundle/Entity/Audit.php b/src/Oro/Bundle/DataAuditBundle/Entity/Audit.php index 9f14e64c274..5f6405caf57 100644 --- a/src/Oro/Bundle/DataAuditBundle/Entity/Audit.php +++ b/src/Oro/Bundle/DataAuditBundle/Entity/Audit.php @@ -14,9 +14,6 @@ /** * @ORM\Entity(repositoryClass="Oro\Bundle\DataAuditBundle\Entity\Repository\AuditRepository") - * @ORM\Table(name="oro_audit", indexes={ - * @ORM\Index(name="idx_oro_audit_logged_at", columns={"logged_at"}) - * }) */ class Audit extends AbstractAudit { diff --git a/src/Oro/Bundle/DataAuditBundle/Entity/AuditField.php b/src/Oro/Bundle/DataAuditBundle/Entity/AuditField.php index 8321a2c8474..b96ecdbe5e7 100644 --- a/src/Oro/Bundle/DataAuditBundle/Entity/AuditField.php +++ b/src/Oro/Bundle/DataAuditBundle/Entity/AuditField.php @@ -9,10 +9,7 @@ /** * @ORM\Entity - * @ORM\Table(name="oro_audit_field") - * @Config( - * mode="hidden" - * ) + * @Config(mode="hidden") */ class AuditField extends ExtendAuditField { diff --git a/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/OroDataAuditBundleInstaller.php b/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/OroDataAuditBundleInstaller.php index 8afbf51ff61..d813ce77f81 100644 --- a/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/OroDataAuditBundleInstaller.php +++ b/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/OroDataAuditBundleInstaller.php @@ -14,7 +14,7 @@ class OroDataAuditBundleInstaller implements Installation */ public function getMigrationVersion() { - return 'v1_5'; + return 'v1_6'; } /** @@ -41,9 +41,13 @@ private function createAudit(Schema $schema) $auditTable->addColumn('object_name', 'string', ['length' => 255]); $auditTable->addColumn('version', 'integer', []); $auditTable->addColumn('organization_id', 'integer', ['notnull' => false]); + $auditTable->addColumn('type', 'string', ['length' => 255]); + $auditTable->setPrimaryKey(['id']); $auditTable->addIndex(['user_id'], 'IDX_5FBA427CA76ED395', []); + $auditTable->addIndex(['type'], 'idx_oro_audit_type'); + $auditTable->addForeignKeyConstraint( $schema->getTable('oro_user'), ['user_id'], @@ -110,6 +114,9 @@ private function createAuditField(Schema $schema) 'comment' => '(DC2Type:json_array)', ]); $auditFieldTable->setPrimaryKey(['id']); + $auditFieldTable->addColumn('type', 'string', ['length' => 255]); + + $auditFieldTable->addIndex(['type'], 'idx_oro_audit_field_type'); $auditFieldTable->addIndex(['audit_id'], 'IDX_9A31A824BD29F359', []); $auditFieldTable->addForeignKeyConstraint( diff --git a/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/OroDataAuditBundle.php b/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/OroDataAuditBundle.php new file mode 100644 index 00000000000..b3ab2bdcde3 --- /dev/null +++ b/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/OroDataAuditBundle.php @@ -0,0 +1,25 @@ +getTable('oro_audit'); + $auditTable->addColumn('type', 'string', ['length' => 255]); + $auditTable->addIndex(['type'], 'idx_oro_audit_type'); + + $auditFieldTable = $schema->getTable('oro_audit_field'); + $auditFieldTable->addColumn('type', 'string', ['length' => 255]); + $auditFieldTable->addIndex(['type'], 'idx_oro_audit_field_type'); + } +} From a6f1a4a60da2b976474fa80861adc49c803a91eb Mon Sep 17 00:00:00 2001 From: Sergey Zhuravel Date: Fri, 9 Oct 2015 15:14:48 +0300 Subject: [PATCH 019/471] BB-1334: Add STI for audit - fix migrations --- .../{OroDataAuditBundle.php => AddColumn.php} | 15 ++++--- .../Migrations/Schema/v1_6/SetNotNullable.php | 32 ++++++++++++++ .../Migrations/Schema/v1_6/SetValue.php | 42 +++++++++++++++++++ 3 files changed, 84 insertions(+), 5 deletions(-) rename src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/{OroDataAuditBundle.php => AddColumn.php} (67%) create mode 100644 src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/SetNotNullable.php create mode 100644 src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/SetValue.php diff --git a/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/OroDataAuditBundle.php b/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/AddColumn.php similarity index 67% rename from src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/OroDataAuditBundle.php rename to src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/AddColumn.php index b3ab2bdcde3..ce855ca4ce0 100644 --- a/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/OroDataAuditBundle.php +++ b/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/AddColumn.php @@ -5,21 +5,26 @@ use Doctrine\DBAL\Schema\Schema; use Oro\Bundle\MigrationBundle\Migration\Migration; +use Oro\Bundle\MigrationBundle\Migration\OrderedMigrationInterface; use Oro\Bundle\MigrationBundle\Migration\QueryBag; -class OroDataAuditBundle implements Migration +class AddColumn implements Migration, OrderedMigrationInterface { + /** {@inheritdoc} */ + public function getOrder() + { + return 10; + } + /** * {@inheritdoc} */ public function up(Schema $schema, QueryBag $queries) { $auditTable = $schema->getTable('oro_audit'); - $auditTable->addColumn('type', 'string', ['length' => 255]); - $auditTable->addIndex(['type'], 'idx_oro_audit_type'); + $auditTable->addColumn('type', 'string', ['length' => 255, 'notnull' => false]); $auditFieldTable = $schema->getTable('oro_audit_field'); - $auditFieldTable->addColumn('type', 'string', ['length' => 255]); - $auditFieldTable->addIndex(['type'], 'idx_oro_audit_field_type'); + $auditFieldTable->addColumn('type', 'string', ['length' => 255, 'notnull' => false]); } } diff --git a/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/SetNotNullable.php b/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/SetNotNullable.php new file mode 100644 index 00000000000..4913018216a --- /dev/null +++ b/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/SetNotNullable.php @@ -0,0 +1,32 @@ +getTable('oro_audit'); + $auditTable->getColumn('type')->setOptions(['length' => 255]); + $auditTable->addIndex(['type'], 'idx_oro_audit_type'); + + $auditFieldTable = $schema->getTable('oro_audit_field'); + $auditFieldTable->getColumn('type')->setOptions(['length' => 255]); + $auditFieldTable->addIndex(['type'], 'idx_oro_audit_field_type'); + } +} diff --git a/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/SetValue.php b/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/SetValue.php new file mode 100644 index 00000000000..cda73823e17 --- /dev/null +++ b/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/SetValue.php @@ -0,0 +1,42 @@ +addPreQuery( + new ParametrizedSqlMigrationQuery( + 'UPDATE oro_audit SET type = :type', + ['type' => 'audit'], + ['type' => Type::STRING] + ) + ); + + $queries->addPreQuery( + new ParametrizedSqlMigrationQuery( + 'UPDATE oro_audit_field SET type = :type', + ['type' => 'audit_field'], + ['type' => Type::STRING] + ) + ); + } +} From 57e67d0826e87f14843da03247c17a3deed331cf Mon Sep 17 00:00:00 2001 From: Sergey Zhuravel Date: Fri, 9 Oct 2015 16:12:38 +0300 Subject: [PATCH 020/471] BB-1334: Add STI for audit - fix migrations --- .../Migrations/Schema/v1_6/SetNotNullable.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/SetNotNullable.php b/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/SetNotNullable.php index 4913018216a..1f0ff9a0a96 100644 --- a/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/SetNotNullable.php +++ b/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/SetNotNullable.php @@ -4,6 +4,7 @@ use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Types\Type; use Oro\Bundle\MigrationBundle\Migration\Migration; use Oro\Bundle\MigrationBundle\Migration\OrderedMigrationInterface; use Oro\Bundle\MigrationBundle\Migration\QueryBag; @@ -22,11 +23,11 @@ public function getOrder() public function up(Schema $schema, QueryBag $queries) { $auditTable = $schema->getTable('oro_audit'); - $auditTable->getColumn('type')->setOptions(['length' => 255]); + $auditTable->getColumn('type')->setType(Type::getType(Type::STRING))->setOptions(['length' => 255]); $auditTable->addIndex(['type'], 'idx_oro_audit_type'); $auditFieldTable = $schema->getTable('oro_audit_field'); - $auditFieldTable->getColumn('type')->setOptions(['length' => 255]); + $auditFieldTable->getColumn('type')->setType(Type::getType(Type::STRING))->setOptions(['length' => 255]); $auditFieldTable->addIndex(['type'], 'idx_oro_audit_field_type'); } } From 866b6a07079300735b385a927880ccd95e3cdeba Mon Sep 17 00:00:00 2001 From: Sergey Zhuravel Date: Fri, 9 Oct 2015 16:59:21 +0300 Subject: [PATCH 021/471] BB-1334: Add STI for audit - fix migrations --- .../Migrations/Schema/v1_6/SetNotNullable.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/SetNotNullable.php b/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/SetNotNullable.php index 1f0ff9a0a96..5f3c143a4ac 100644 --- a/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/SetNotNullable.php +++ b/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/SetNotNullable.php @@ -23,11 +23,15 @@ public function getOrder() public function up(Schema $schema, QueryBag $queries) { $auditTable = $schema->getTable('oro_audit'); - $auditTable->getColumn('type')->setType(Type::getType(Type::STRING))->setOptions(['length' => 255]); + $auditTable->getColumn('type') + ->setType(Type::getType(Type::STRING)) + ->setOptions(['length' => 255, 'notnull' => true]); $auditTable->addIndex(['type'], 'idx_oro_audit_type'); $auditFieldTable = $schema->getTable('oro_audit_field'); - $auditFieldTable->getColumn('type')->setType(Type::getType(Type::STRING))->setOptions(['length' => 255]); + $auditFieldTable->getColumn('type') + ->setType(Type::getType(Type::STRING)) + ->setOptions(['length' => 255, 'notnull' => true]); $auditFieldTable->addIndex(['type'], 'idx_oro_audit_field_type'); } } From 919e1dada59ec25ccbbf994abd055030e608bb19 Mon Sep 17 00:00:00 2001 From: Sergey Zhuravel Date: Wed, 28 Oct 2015 15:16:05 +0200 Subject: [PATCH 022/471] BB-1334: Add STI for audit - fix commit order --- src/Oro/Bundle/DataAuditBundle/Entity/AbstractAuditField.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAuditField.php b/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAuditField.php index aa2e8bafab3..efaa1286892 100644 --- a/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAuditField.php +++ b/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAuditField.php @@ -38,9 +38,6 @@ abstract class AbstractAuditField /** * @var AbstractAudit - * - * @ORM\ManyToOne(targetEntity="AbstractAudit", inversedBy="fields", cascade={"persist"}) - * @ORM\JoinColumn(name="audit_id", referencedColumnName="id", nullable=false, onDelete="CASCADE") */ protected $audit; From 6be359eddcfa1218fef17f953eaa265bf82ae797 Mon Sep 17 00:00:00 2001 From: Sergey Zhuravel Date: Wed, 28 Oct 2015 15:35:33 +0200 Subject: [PATCH 023/471] BB-1334: Add STI for audit - fix commit order --- src/Oro/Bundle/DataAuditBundle/Entity/AbstractAudit.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAudit.php b/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAudit.php index db53da55a05..a4870754c18 100644 --- a/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAudit.php +++ b/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAudit.php @@ -47,8 +47,6 @@ abstract class AbstractAudit extends AbstractLogEntry /** * @var AbstractAuditField[]|Collection - * - * @ORM\OneToMany(targetEntity="AbstractAuditField", mappedBy="audit", cascade={"persist"}) */ protected $fields; From 1a48a93d9785f4f9830962beaabe4c1259441381 Mon Sep 17 00:00:00 2001 From: Sergey Zhuravel Date: Wed, 28 Oct 2015 17:14:35 +0200 Subject: [PATCH 024/471] BB-1334: Add STI for audit - fix commit order --- src/Oro/Bundle/DataAuditBundle/Entity/AbstractAudit.php | 2 ++ src/Oro/Bundle/DataAuditBundle/Entity/AbstractAuditField.php | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAudit.php b/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAudit.php index a4870754c18..db53da55a05 100644 --- a/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAudit.php +++ b/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAudit.php @@ -47,6 +47,8 @@ abstract class AbstractAudit extends AbstractLogEntry /** * @var AbstractAuditField[]|Collection + * + * @ORM\OneToMany(targetEntity="AbstractAuditField", mappedBy="audit", cascade={"persist"}) */ protected $fields; diff --git a/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAuditField.php b/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAuditField.php index efaa1286892..aa2e8bafab3 100644 --- a/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAuditField.php +++ b/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAuditField.php @@ -38,6 +38,9 @@ abstract class AbstractAuditField /** * @var AbstractAudit + * + * @ORM\ManyToOne(targetEntity="AbstractAudit", inversedBy="fields", cascade={"persist"}) + * @ORM\JoinColumn(name="audit_id", referencedColumnName="id", nullable=false, onDelete="CASCADE") */ protected $audit; From 248dbedb48f7a96df825307fdae46d2dc1468f58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1gi-Kaz=C3=A1r=20M=C3=A1rk?= Date: Fri, 6 Nov 2015 23:55:02 +0100 Subject: [PATCH 025/471] Update Sensio Distribution Bundle, closes #339 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 6045b5d6073..9c34dd44514 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,7 @@ "symfony/icu": "~1.1", "symfony/swiftmailer-bundle": "2.3.*", "symfony/monolog-bundle": "2.7.*", - "sensio/distribution-bundle": "2.3.14", + "sensio/distribution-bundle": "4.0.3", "sensio/framework-extra-bundle": "2.3.4", "incenteev/composer-parameter-handler": "2.1.0", "jms/job-queue-bundle": "1.2.*", From 4112a18de05961d4efc87229f5ff4d52401e9e8f Mon Sep 17 00:00:00 2001 From: Ignat Shcheglovskyi Date: Mon, 9 Nov 2015 11:26:21 -0800 Subject: [PATCH 026/471] BAP-9220: Upgrade fails on not existing roles - fix bug when role is not exist --- .../Migrations/Data/ORM/UpdateAclRoles.php | 69 +++++++++++-------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/src/Oro/Bundle/CalendarBundle/Migrations/Data/ORM/UpdateAclRoles.php b/src/Oro/Bundle/CalendarBundle/Migrations/Data/ORM/UpdateAclRoles.php index cea2ba64f09..b14ed14e9cd 100644 --- a/src/Oro/Bundle/CalendarBundle/Migrations/Data/ORM/UpdateAclRoles.php +++ b/src/Oro/Bundle/CalendarBundle/Migrations/Data/ORM/UpdateAclRoles.php @@ -9,6 +9,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface; use Oro\Bundle\SecurityBundle\Acl\Persistence\AclManager; +use Oro\Bundle\UserBundle\Entity\Role; use Oro\Bundle\UserBundle\Migrations\Data\ORM\LoadRolesData; class UpdateAclRoles extends AbstractFixture implements DependentFixtureInterface, ContainerAwareInterface @@ -60,40 +61,48 @@ public function load(ObjectManager $manager) protected function updateUserRole(AclManager $manager) { - $sid = $manager->getSid($this->getRole(LoadRolesData::ROLE_USER)); - - // grant to manage own calendar events - $oid = $manager->getOid('entity:Oro\Bundle\CalendarBundle\Entity\CalendarEvent'); - $maskBuilder = $manager->getMaskBuilder($oid) - // ->add('VIEW_BASIC') - // ->add('CREATE_BASIC') - // ->add('EDIT_BASIC') - // ->add('DELETE_BASIC'); - // @todo now only SYSTEM level is supported - ->add('VIEW_SYSTEM') - ->add('CREATE_SYSTEM') - ->add('EDIT_SYSTEM') - ->add('DELETE_SYSTEM'); - $manager->setPermission($sid, $oid, $maskBuilder->get()); + $role = $this->getRole(LoadRolesData::ROLE_USER); + + if ($role) { + $sid = $manager->getSid($role); + + // grant to manage own calendar events + $oid = $manager->getOid('entity:Oro\Bundle\CalendarBundle\Entity\CalendarEvent'); + $maskBuilder = $manager->getMaskBuilder($oid) + // ->add('VIEW_BASIC') + // ->add('CREATE_BASIC') + // ->add('EDIT_BASIC') + // ->add('DELETE_BASIC'); + // @todo now only SYSTEM level is supported + ->add('VIEW_SYSTEM') + ->add('CREATE_SYSTEM') + ->add('EDIT_SYSTEM') + ->add('DELETE_SYSTEM'); + $manager->setPermission($sid, $oid, $maskBuilder->get()); + } } protected function updateManagerRole(AclManager $manager) { - $sid = $manager->getSid($this->getRole(LoadRolesData::ROLE_MANAGER)); - - // grant to manage own calendar events - $oid = $manager->getOid('entity:Oro\Bundle\CalendarBundle\Entity\CalendarEvent'); - $maskBuilder = $manager->getMaskBuilder($oid) - // ->add('VIEW_BASIC') - // ->add('CREATE_BASIC') - // ->add('EDIT_BASIC') - // ->add('DELETE_BASIC'); - // @todo now only SYSTEM level is supported - ->add('VIEW_SYSTEM') - ->add('CREATE_SYSTEM') - ->add('EDIT_SYSTEM') - ->add('DELETE_SYSTEM'); - $manager->setPermission($sid, $oid, $maskBuilder->get()); + $role = $this->getRole(LoadRolesData::ROLE_MANAGER); + + if ($role) { + $sid = $manager->getSid($role); + + // grant to manage own calendar events + $oid = $manager->getOid('entity:Oro\Bundle\CalendarBundle\Entity\CalendarEvent'); + $maskBuilder = $manager->getMaskBuilder($oid) + // ->add('VIEW_BASIC') + // ->add('CREATE_BASIC') + // ->add('EDIT_BASIC') + // ->add('DELETE_BASIC'); + // @todo now only SYSTEM level is supported + ->add('VIEW_SYSTEM') + ->add('CREATE_SYSTEM') + ->add('EDIT_SYSTEM') + ->add('DELETE_SYSTEM'); + $manager->setPermission($sid, $oid, $maskBuilder->get()); + } } /** From bb4ddfe65e2a6e49ab9f1e2bb32230922bf591ff Mon Sep 17 00:00:00 2001 From: Olexandr Radchenko Date: Thu, 12 Nov 2015 17:18:32 +0200 Subject: [PATCH 027/471] Problem with process parameters in EntityPaginationBundle --- .../Storage/StorageDataCollector.php | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Oro/Bundle/EntityPaginationBundle/Storage/StorageDataCollector.php b/src/Oro/Bundle/EntityPaginationBundle/Storage/StorageDataCollector.php index 8ebc7c46e80..a4a36b3656f 100644 --- a/src/Oro/Bundle/EntityPaginationBundle/Storage/StorageDataCollector.php +++ b/src/Oro/Bundle/EntityPaginationBundle/Storage/StorageDataCollector.php @@ -83,10 +83,18 @@ public function collect(Request $request, $scope) $isDataCollected = false; - $gridNames = array_keys($request->query->get('grid', [])); + $gridNames = array(); + if ($request->query->get('grid')) { + $gridNames = array_keys($request->query->get('grid', [])); + } foreach ($gridNames as $gridName) { - // datagrid manager automatically extracts all required parameters from request - $dataGrid = $this->datagridManager->getDatagridByRequestParams($gridName); + try { + // datagrid manager automatically extracts all required parameters from request + $dataGrid = $this->datagridManager->getDatagridByRequestParams($gridName); + } catch (\RuntimeException $e) { + continue; + } + if (!$this->paginationManager->isDatagridApplicable($dataGrid)) { continue; } From d7fd9dcd55c0e0753b97094748ef406149478eef Mon Sep 17 00:00:00 2001 From: Oleksandr Nechyporenko Date: Thu, 12 Nov 2015 20:32:10 +0200 Subject: [PATCH 028/471] BB-1532: Dump layout styles in css - add ArrayOptionValueBuilder - fix OptionValueBag --- .../Layout/ArrayOptionValueBuilder.php | 83 +++++++++++++++++++ src/Oro/Component/Layout/OptionValueBag.php | 22 +++-- 2 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 src/Oro/Component/Layout/ArrayOptionValueBuilder.php diff --git a/src/Oro/Component/Layout/ArrayOptionValueBuilder.php b/src/Oro/Component/Layout/ArrayOptionValueBuilder.php new file mode 100644 index 00000000000..749e116b3cf --- /dev/null +++ b/src/Oro/Component/Layout/ArrayOptionValueBuilder.php @@ -0,0 +1,83 @@ +prepareValueType($value))) { + return; + } + + $this->values = array_merge($this->values, $value); + } + + /** + * @inheritdoc + */ + public function remove($value) + { + if (!($value = $this->prepareValueType($value))) { + return; + } + + $this->values = array_values(array_diff($this->values, $value)); + } + + /** + * @inheritdoc + */ + public function replace($oldValues, $newValue) + { + if (!($value = $this->prepareValueType($oldValues))) { + return; + } + + if (!($value = $this->prepareValueType($newValue))) { + return; + } + + if (count($oldValues) !== count($newValue)) { + throw new InvalidArgumentException(sprintf('$oldValues should be the same as $newValue size.')); + } + + foreach ($oldValues as $index => $oldValue) { + $key = array_search($oldValue, $this->values, true); + if (false !== $key) { + $this->values[$key] = $newValue[$index]; + } + } + } + + /** + * @inheritdoc + */ + public function get() + { + return $this->values; + } +} diff --git a/src/Oro/Component/Layout/OptionValueBag.php b/src/Oro/Component/Layout/OptionValueBag.php index 7058feb6e60..e5f56283407 100644 --- a/src/Oro/Component/Layout/OptionValueBag.php +++ b/src/Oro/Component/Layout/OptionValueBag.php @@ -60,20 +60,32 @@ public function all() */ public function buildValue(OptionValueBuilderInterface $builder) { + $actions = [ + 'add' => [], + 'replace' => [], + 'remove' => [], + ]; + foreach ($this->actions as $action) { switch ($action->getName()) { case 'add': - $builder->add($action->getArgument(0)); - break; - case 'remove': - $builder->remove($action->getArgument(0)); + $actions['add'][] = [$action->getArgument(0)]; break; case 'replace': - $builder->replace($action->getArgument(0), $action->getArgument(1)); + $actions['replace'][] = [$action->getArgument(0), $action->getArgument(1)]; + break; + case 'remove': + $actions['remove'][] = [$action->getArgument(0)]; break; } } + foreach ($actions as $action => $calls) { + foreach ($calls as $arguments) { + call_user_func_array([$builder, $action], $arguments); + } + } + return $builder->get(); } } From ec3bc84d8f9809c128adb37a3d8daf740eb8769f Mon Sep 17 00:00:00 2001 From: Oleksandr Nechyporenko Date: Thu, 12 Nov 2015 20:32:56 +0200 Subject: [PATCH 029/471] BB-1532: Dump layout styles in css - add block StylesheetsType --- .../Layout/Block/Type/StylesheetsType.php | 45 +++++++++++++++++++ .../Resources/config/block_types.yml | 5 +++ .../views/Layout/div_layout.html.twig | 6 +++ 3 files changed, 56 insertions(+) create mode 100644 src/Oro/Bundle/LayoutBundle/Layout/Block/Type/StylesheetsType.php diff --git a/src/Oro/Bundle/LayoutBundle/Layout/Block/Type/StylesheetsType.php b/src/Oro/Bundle/LayoutBundle/Layout/Block/Type/StylesheetsType.php new file mode 100644 index 00000000000..4815520f3aa --- /dev/null +++ b/src/Oro/Bundle/LayoutBundle/Layout/Block/Type/StylesheetsType.php @@ -0,0 +1,45 @@ +setRequired(['styles']); + } + + /** + * {@inheritdoc} + */ + public function finishView(BlockView $view, BlockInterface $block, array $options) + { + $styles = $block->getOptions()['styles']; + if ($styles instanceof OptionValueBag) { + $styles = $styles->buildValue(new ArrayOptionValueBuilder()); + } + + $view->vars['styles'] = $styles; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return self::NAME; + } +} diff --git a/src/Oro/Bundle/LayoutBundle/Resources/config/block_types.yml b/src/Oro/Bundle/LayoutBundle/Resources/config/block_types.yml index 17a23227d80..cfe5fc0a04a 100644 --- a/src/Oro/Bundle/LayoutBundle/Resources/config/block_types.yml +++ b/src/Oro/Bundle/LayoutBundle/Resources/config/block_types.yml @@ -29,6 +29,11 @@ services: tags: - { name: layout.block_type, alias: script } + oro_layout.block_type.stylesheets: + class: Oro\Bundle\LayoutBundle\Layout\Block\Type\StylesheetsType + tags: + - { name: layout.block_type, alias: stylesheets } + oro_layout.block_type.style: class: Oro\Bundle\LayoutBundle\Layout\Block\Type\StyleType tags: diff --git a/src/Oro/Bundle/LayoutBundle/Resources/views/Layout/div_layout.html.twig b/src/Oro/Bundle/LayoutBundle/Resources/views/Layout/div_layout.html.twig index 3883c5692a5..94a826621c8 100644 --- a/src/Oro/Bundle/LayoutBundle/Resources/views/Layout/div_layout.html.twig +++ b/src/Oro/Bundle/LayoutBundle/Resources/views/Layout/div_layout.html.twig @@ -57,6 +57,12 @@ {% endif %} {% endblock %} +{% block stylesheets_widget %} + {% for style in styles %} + + {% endfor %} +{% endblock %} + {% block style_widget %} {% if attr.href is defined and attr.href is not empty %} From 6984e95935869f4d795897b4e7ead202f070ef00 Mon Sep 17 00:00:00 2001 From: Oleksandr Nechyporenko Date: Thu, 12 Nov 2015 20:34:01 +0200 Subject: [PATCH 030/471] BB-1532: Dump layout styles in css - add empty LayoutFormulaLoader and LayoutResource --- .../Assetic/LayoutFormulaLoader.php | 18 +++++++++++++++ .../LayoutBundle/Assetic/LayoutResource.php | 23 +++++++++++++++++++ .../Resources/config/services.yml | 20 +++++++++++++--- 3 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 src/Oro/Bundle/LayoutBundle/Assetic/LayoutFormulaLoader.php create mode 100644 src/Oro/Bundle/LayoutBundle/Assetic/LayoutResource.php diff --git a/src/Oro/Bundle/LayoutBundle/Assetic/LayoutFormulaLoader.php b/src/Oro/Bundle/LayoutBundle/Assetic/LayoutFormulaLoader.php new file mode 100644 index 00000000000..546a425f5d7 --- /dev/null +++ b/src/Oro/Bundle/LayoutBundle/Assetic/LayoutFormulaLoader.php @@ -0,0 +1,18 @@ +getContent() : []; + } +} diff --git a/src/Oro/Bundle/LayoutBundle/Assetic/LayoutResource.php b/src/Oro/Bundle/LayoutBundle/Assetic/LayoutResource.php new file mode 100644 index 00000000000..b5115d59fab --- /dev/null +++ b/src/Oro/Bundle/LayoutBundle/Assetic/LayoutResource.php @@ -0,0 +1,23 @@ + Date: Mon, 16 Nov 2015 12:15:39 +0200 Subject: [PATCH 031/471] BB-1532: Dump layout styles in css - rename options "styles" to "inputs" - dump layout styles in one file --- .../LayoutBundle/Assetic/LayoutResource.php | 72 ++++++++++++++++++- .../Layout/Block/Type/StylesheetsType.php | 40 +++++++++-- .../Resources/config/services.yml | 3 + .../views/Layout/div_layout.html.twig | 4 +- 4 files changed, 110 insertions(+), 9 deletions(-) diff --git a/src/Oro/Bundle/LayoutBundle/Assetic/LayoutResource.php b/src/Oro/Bundle/LayoutBundle/Assetic/LayoutResource.php index b5115d59fab..fe55c711300 100644 --- a/src/Oro/Bundle/LayoutBundle/Assetic/LayoutResource.php +++ b/src/Oro/Bundle/LayoutBundle/Assetic/LayoutResource.php @@ -4,8 +4,28 @@ use Assetic\Factory\Resource\ResourceInterface; +use Oro\Bundle\LayoutBundle\Layout\Block\Type\StylesheetsType; +use Oro\Component\Layout\Layout; +use Oro\Component\Layout\BlockView; +use Oro\Component\Layout\LayoutManager; +use Oro\Component\Layout\LayoutContext; +use Oro\Component\Layout\Extension\Theme\Model\ThemeManager; +use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; + class LayoutResource implements ResourceInterface { + /** @var ThemeManager */ + protected $themeManager; + + /** @var LayoutManager $layoutManager */ + protected $layoutManager; + + public function __construct(ThemeManager $themeManager, LayoutManager $layoutManager) + { + $this->themeManager = $themeManager; + $this->layoutManager = $layoutManager; + } + public function isFresh($timestamp) { return true; @@ -13,7 +33,57 @@ public function isFresh($timestamp) public function getContent() { - return []; + $formulae = []; + $themes = $this->themeManager->getThemeNames(); + foreach ($themes as $theme) { + $layout = $this->getLayout($theme); + if ($layout) { + $formulae += $this->collectViewStylesheets($theme, $layout->getView()); + } + } + return $formulae; + } + + /** + * @param string $theme + * @return Layout + */ + protected function getLayout($theme) + { + $builder = $this->layoutManager->getLayoutBuilder(); + $builder->add('root', null, 'root'); + + $context = new LayoutContext(); + $context->set('theme', $theme); + + try { + $layout = $builder->getLayout($context); + } catch (NoSuchPropertyException $ex) { + $ex; + $layout = null; + } + + return $layout; + } + + protected function collectViewStylesheets($theme, BlockView $view, $formulae = []) + { + if ($view->vars['block_type'] === StylesheetsType::NAME) { + $name = 'layout_' . $theme . $view->vars['cache_key']; + $formulae[$name] = [ + $view->vars['inputs'], + $view->vars['filters'], + [ + 'output' => $view->vars['output'], + 'name' => $name, + ], + ]; + } else { + foreach ($view as $childView) { + $formulae = $this->collectViewStylesheets($theme, $childView, $formulae); + } + } + return $formulae; } public function __toString() diff --git a/src/Oro/Bundle/LayoutBundle/Layout/Block/Type/StylesheetsType.php b/src/Oro/Bundle/LayoutBundle/Layout/Block/Type/StylesheetsType.php index 4815520f3aa..109b669b399 100644 --- a/src/Oro/Bundle/LayoutBundle/Layout/Block/Type/StylesheetsType.php +++ b/src/Oro/Bundle/LayoutBundle/Layout/Block/Type/StylesheetsType.php @@ -19,7 +19,25 @@ class StylesheetsType extends AbstractType */ public function setDefaultOptions(OptionsResolverInterface $resolver) { - $resolver->setRequired(['styles']); + $resolver->setRequired( + [ + 'inputs' + ] + ); + $resolver->setDefaults( + [ + 'filters' => [ + 'cssrewrite', + 'lessphp', + 'cssmin', + ], + 'output' => null, + ] + ); + + $resolver->setAllowedTypes('inputs', ['array', 'Oro\Component\Layout\OptionValueBag']); + $resolver->setAllowedTypes('filters', ['array', 'Oro\Component\Layout\OptionValueBag']); + $resolver->setAllowedTypes('output', ['null', 'string']); } /** @@ -27,12 +45,24 @@ public function setDefaultOptions(OptionsResolverInterface $resolver) */ public function finishView(BlockView $view, BlockInterface $block, array $options) { - $styles = $block->getOptions()['styles']; - if ($styles instanceof OptionValueBag) { - $styles = $styles->buildValue(new ArrayOptionValueBuilder()); + $theme = $block->getContext()->get('theme'); + + $inputs = $block->getOptions()['inputs']; + if ($inputs instanceof OptionValueBag) { + $inputs = $inputs->buildValue(new ArrayOptionValueBuilder()); + } + $view->vars['inputs'] = $inputs; + + $filters = $block->getOptions()['filters']; + if ($filters instanceof OptionValueBag) { + $filters = $filters->buildValue(new ArrayOptionValueBuilder()); } + $view->vars['filters'] = $filters; - $view->vars['styles'] = $styles; + $view->vars['output'] = $block->getOptions()['output']; + if (!$view->vars['output']) { + $view->vars['output'] = 'css/layout/'.$theme.'/'.$view->vars['cache_key'].'.css'; + } } /** diff --git a/src/Oro/Bundle/LayoutBundle/Resources/config/services.yml b/src/Oro/Bundle/LayoutBundle/Resources/config/services.yml index e1048a93c6a..dad44a3ee7e 100644 --- a/src/Oro/Bundle/LayoutBundle/Resources/config/services.yml +++ b/src/Oro/Bundle/LayoutBundle/Resources/config/services.yml @@ -145,5 +145,8 @@ services: oro_layout.assetic.layout_resource: public: false class: %oro_layout.assetic.layout_resource.class% + arguments: + - @oro_layout.theme_manager + - @oro_layout.layout_manager tags: - { name: assetic.formula_resource, loader: layout } diff --git a/src/Oro/Bundle/LayoutBundle/Resources/views/Layout/div_layout.html.twig b/src/Oro/Bundle/LayoutBundle/Resources/views/Layout/div_layout.html.twig index 94a826621c8..eafb9e0daac 100644 --- a/src/Oro/Bundle/LayoutBundle/Resources/views/Layout/div_layout.html.twig +++ b/src/Oro/Bundle/LayoutBundle/Resources/views/Layout/div_layout.html.twig @@ -58,9 +58,7 @@ {% endblock %} {% block stylesheets_widget %} - {% for style in styles %} - - {% endfor %} + {% endblock %} {% block style_widget %} From a1aee92eb41b002d42bbfcd9561f9105c27e42cb Mon Sep 17 00:00:00 2001 From: Ivan Shakuta Date: Mon, 16 Nov 2015 14:02:05 +0200 Subject: [PATCH 032/471] Fix typo in select2-entity-field-choice-component - fix typo --- .../components/select2-entity-field-choice-component.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Oro/Bundle/EntityBundle/Resources/public/js/components/select2-entity-field-choice-component.js b/src/Oro/Bundle/EntityBundle/Resources/public/js/components/select2-entity-field-choice-component.js index c2db68fe60b..160303cc003 100644 --- a/src/Oro/Bundle/EntityBundle/Resources/public/js/components/select2-entity-field-choice-component.js +++ b/src/Oro/Bundle/EntityBundle/Resources/public/js/components/select2-entity-field-choice-component.js @@ -1,15 +1,16 @@ define(function(require) { 'use strict'; - var Select2EntityFieldChoiseComponent; + var Select2EntityFieldChoiceComponent; var EntityFieldUtil = require('oroentity/js/entity-field-choice-util'); var Select2EntityFieldComponent = require('oro/select2-entity-field-component'); - Select2EntityFieldChoiseComponent = Select2EntityFieldComponent.extend({ + Select2EntityFieldChoiceComponent = Select2EntityFieldComponent.extend({ initialize: function(options) { this.util = new EntityFieldUtil(options._sourceElement); - Select2EntityFieldChoiseComponent.__super__.initialize.call(this, options); + Select2EntityFieldChoiceComponent.__super__.initialize.call(this, options); } }); - return Select2EntityFieldChoiseComponent; + + return Select2EntityFieldChoiceComponent; }); From 0f695568409b56ce9dd1741b4d2ecf0c2fb16ef6 Mon Sep 17 00:00:00 2001 From: Oleksandr Nechyporenko Date: Tue, 17 Nov 2015 15:53:49 +0200 Subject: [PATCH 033/471] BB-1582: Move theme info file to theme folder - move layout theme definition to theme folder --- UPGRADE-1.9.md | 4 ++ .../layouts/embedded_default/theme.yml} | 0 .../OroLayoutExtension.php | 44 +++++++++++++++++-- .../LayoutBundle/Resources/doc/quick_start.md | 3 +- .../Resources/doc/theme_definition.md | 13 +++++- .../OroLayoutExtensionTest.php | 11 +++-- .../Resources/views/layouts/base/theme.yml | 4 ++ 7 files changed, 69 insertions(+), 10 deletions(-) rename src/Oro/Bundle/EmbeddedFormBundle/Resources/{config/oro/layout.yml => views/layouts/embedded_default/theme.yml} (100%) create mode 100644 src/Oro/Bundle/LayoutBundle/Tests/Unit/Stubs/Bundles/TestBundle/Resources/views/layouts/base/theme.yml diff --git a/UPGRADE-1.9.md b/UPGRADE-1.9.md index b42256e5a31..f5209c8df4d 100644 --- a/UPGRADE-1.9.md +++ b/UPGRADE-1.9.md @@ -49,6 +49,10 @@ UPGRADE FROM 1.8 to 1.9 ####FormBundle - Add new form type: `oro_autocomplete`. See [text_autocomplete_form_type.md](./src/Oro/Bundle/FormBundle/Resources/doc/reference/text_autocomplete_form_type.md) for more detailed info. +####LayoutBundle +- The theme definition should be placed at theme folder and named `theme.yml`, for example `DemoBundle/Resources/views/layouts/first_theme/theme.yml` +- Deprecated method: placed at `Resources/config/oro/` and named `layout.yml`, for example `DemoBundle/Resources/config/oro/layout.yml` + ####SecurityBundle - `Oro\Bundle\SecurityBundle\Owner\OwnerTreeInterface` is changed. New method `buildTree` added (due to performance issues). It should be called once after all `addDeepEntity` calls. See [OwnerTreeProvider](./src/Oro/Bundle/SecurityBundle/Owner/OwnerTreeProvider.php) method `fillTree`. Implementation example [OwnerTree](./src/Oro/Bundle/SecurityBundle/Owner/OwnerTree.php). - Bundle now contains part of Symfony security configuration (ACL configuration and access decision manager strategy) diff --git a/src/Oro/Bundle/EmbeddedFormBundle/Resources/config/oro/layout.yml b/src/Oro/Bundle/EmbeddedFormBundle/Resources/views/layouts/embedded_default/theme.yml similarity index 100% rename from src/Oro/Bundle/EmbeddedFormBundle/Resources/config/oro/layout.yml rename to src/Oro/Bundle/EmbeddedFormBundle/Resources/views/layouts/embedded_default/theme.yml diff --git a/src/Oro/Bundle/LayoutBundle/DependencyInjection/OroLayoutExtension.php b/src/Oro/Bundle/LayoutBundle/DependencyInjection/OroLayoutExtension.php index 3b57865e4b0..237b35efb10 100644 --- a/src/Oro/Bundle/LayoutBundle/DependencyInjection/OroLayoutExtension.php +++ b/src/Oro/Bundle/LayoutBundle/DependencyInjection/OroLayoutExtension.php @@ -10,6 +10,7 @@ use Oro\Component\Config\Loader\CumulativeConfigLoader; use Oro\Component\Config\Loader\YamlCumulativeFileLoader; use Oro\Component\Config\Loader\FolderContentCumulativeLoader; +use Oro\Component\Config\Loader\FolderingCumulativeFileLoader; class OroLayoutExtension extends Extension { @@ -23,10 +24,19 @@ public function load(array $configs, ContainerBuilder $container) { $configLoader = new CumulativeConfigLoader( 'oro_layout', - new YamlCumulativeFileLoader('Resources/config/oro/layout.yml') + [ + new FolderingCumulativeFileLoader( + '{folder}', + '\w+', + new YamlCumulativeFileLoader('Resources/views/layouts/{folder}/theme.yml') + ), + new YamlCumulativeFileLoader('Resources/config/oro/layout.yml') + ] ); - $resources = $configLoader->load($container); - foreach ($resources as $resource) { + $themesResources = $configLoader->load($container); + $existThemePaths = []; + foreach ($themesResources as $resource) { + $existThemePaths[$resource->path] = true; $configs[] = $resource->data['oro_layout']; } @@ -94,9 +104,35 @@ public function load(array $configs, ContainerBuilder $container) * ] * ] */ - $foundThemeLayoutUpdates = array_merge_recursive($foundThemeLayoutUpdates, $resource->data); + $foundThemeLayoutUpdates = array_merge_recursive( + $foundThemeLayoutUpdates, + $this->filterThemeLayoutUpdates($existThemePaths, $resource->data) + ); } $container->setParameter('oro_layout.theme_updates_resources', $foundThemeLayoutUpdates); } + + /** + * @param array $existThemePaths + * @param array $themes + * @return array + */ + protected function filterThemeLayoutUpdates($existThemePaths, array $themes) + { + foreach ($themes as $theme => $themePaths) { + foreach ($themePaths as $pathIndex => $path) { + if (is_string($path) && isset($existThemePaths[$path])) { + unset($themePaths[$pathIndex]); + } + } + if (empty($themePaths)) { + unset($themes[$theme]); + } else { + $themes[$theme] = $themePaths; + } + } + + return $themes; + } } diff --git a/src/Oro/Bundle/LayoutBundle/Resources/doc/quick_start.md b/src/Oro/Bundle/LayoutBundle/Resources/doc/quick_start.md index 30e2d20ccc6..4eb08725708 100644 --- a/src/Oro/Bundle/LayoutBundle/Resources/doc/quick_start.md +++ b/src/Oro/Bundle/LayoutBundle/Resources/doc/quick_start.md @@ -6,7 +6,8 @@ The following examples may help to start using layouts in your application. Create the layout theme ----------------------- -The theme definition should be placed at `Resources/config/oro/` and named `layout.yml`, for example `DemoBundle/Resources/config/oro/layout.yml`: +The theme definition should be placed at theme folder and named `theme.yml`, for example `DemoBundle/Resources/views/layouts/first_theme/theme.yml` +Deprecated method: placed at `Resources/config/oro/` and named `layout.yml`, for example `DemoBundle/Resources/config/oro/layout.yml` ```yaml oro_layout: diff --git a/src/Oro/Bundle/LayoutBundle/Resources/doc/theme_definition.md b/src/Oro/Bundle/LayoutBundle/Resources/doc/theme_definition.md index 74bce92d903..ab6ccd885a2 100644 --- a/src/Oro/Bundle/LayoutBundle/Resources/doc/theme_definition.md +++ b/src/Oro/Bundle/LayoutBundle/Resources/doc/theme_definition.md @@ -8,7 +8,8 @@ Basically, think of a **theme** as a skin for your application. Files, that the ## Configuration -The configuration file should be placed at `Resources/config/oro/` and named `layout.yml`. +The configuration file should be placed at theme folder and named `theme.yml`, for example `DemoBundle/Resources/views/layouts/first_theme/theme.yml` +Deprecated method: placed at `Resources/config/oro/` and named `layout.yml`, for example `DemoBundle/Resources/config/oro/layout.yml` ### Themes configuration reference @@ -27,7 +28,7 @@ You can find additional information if you execute the `app/console config:dump- **Example:** ```yaml -# src/Acme/Bundle/DemoBundle/Resources/config/oro/layout.yml +# src/Acme/Bundle/DemoBundle/Resources/views/layouts/base/theme.yml oro_layout: themes: @@ -37,6 +38,10 @@ oro_layout: label: ~ # this is a "hidden" theme groups: [ main ] +# src/Acme/Bundle/DemoBundle/Resources/views/layouts/oro/theme.yml + +oro_layout: + themes: oro: # Default layout theme for the Oro Platform label: Oro Theme @@ -44,6 +49,10 @@ oro_layout: parent: base groups: [ main ] +# src/Acme/Bundle/DemoBundle/Resources/views/layouts/oro-gold/theme.yml + +oro_layout: + themes: oro-gold: label: Nice ORO gold theme directory: OroGold diff --git a/src/Oro/Bundle/LayoutBundle/Tests/Unit/DependencyInjection/OroLayoutExtensionTest.php b/src/Oro/Bundle/LayoutBundle/Tests/Unit/DependencyInjection/OroLayoutExtensionTest.php index 43c6752449f..74655866e4b 100644 --- a/src/Oro/Bundle/LayoutBundle/Tests/Unit/DependencyInjection/OroLayoutExtensionTest.php +++ b/src/Oro/Bundle/LayoutBundle/Tests/Unit/DependencyInjection/OroLayoutExtensionTest.php @@ -204,9 +204,14 @@ public function testLoadWithBundleThemesConfig() $extension = new OroLayoutExtension(); $extension->load([], $container); - $expectedResult = ['base', 'oro-black']; - $result = $container->get(OroLayoutExtension::THEME_MANAGER_SERVICE_ID)->getThemeNames(); - $this->assertSame(sort($expectedResult), sort($result)); + $expectedThemeNames = ['base', 'oro-black']; + + $themes = $container->get(OroLayoutExtension::THEME_MANAGER_SERVICE_ID)->getAllThemes(); + $themeNames = $container->get(OroLayoutExtension::THEME_MANAGER_SERVICE_ID)->getThemeNames(); + + $this->assertSame(sort($expectedThemeNames), sort($themeNames)); + $this->assertSame('Oro Black theme', $themes['oro-black']->getLabel()); + $this->assertSame('Oro Black theme description', $themes['oro-black']->getDescription()); } public function testLoadingLayoutUpdates() diff --git a/src/Oro/Bundle/LayoutBundle/Tests/Unit/Stubs/Bundles/TestBundle/Resources/views/layouts/base/theme.yml b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Stubs/Bundles/TestBundle/Resources/views/layouts/base/theme.yml new file mode 100644 index 00000000000..61b85a00112 --- /dev/null +++ b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Stubs/Bundles/TestBundle/Resources/views/layouts/base/theme.yml @@ -0,0 +1,4 @@ +oro_layout: + themes: + oro-black: + description: Oro Black theme description From 94fcf7a79ee4675168debd0acb643c3da67616a9 Mon Sep 17 00:00:00 2001 From: Oleksandr Nechyporenko Date: Tue, 17 Nov 2015 17:00:33 +0200 Subject: [PATCH 034/471] BB-1582: Move theme info file to theme folder - remove unnecessary information from theme config --- .../views/layouts/embedded_default/theme.yml | 7 ++---- .../OroLayoutExtension.php | 25 ++++++++++++++++--- .../Resources/views/layouts/base/theme.yml | 4 --- .../views/layouts/oro-black/theme.yml | 1 + 4 files changed, 25 insertions(+), 12 deletions(-) delete mode 100644 src/Oro/Bundle/LayoutBundle/Tests/Unit/Stubs/Bundles/TestBundle/Resources/views/layouts/base/theme.yml create mode 100644 src/Oro/Bundle/LayoutBundle/Tests/Unit/Stubs/Bundles/TestBundle/Resources/views/layouts/oro-black/theme.yml diff --git a/src/Oro/Bundle/EmbeddedFormBundle/Resources/views/layouts/embedded_default/theme.yml b/src/Oro/Bundle/EmbeddedFormBundle/Resources/views/layouts/embedded_default/theme.yml index fc3c643e37b..0824e79e6a4 100644 --- a/src/Oro/Bundle/EmbeddedFormBundle/Resources/views/layouts/embedded_default/theme.yml +++ b/src/Oro/Bundle/EmbeddedFormBundle/Resources/views/layouts/embedded_default/theme.yml @@ -1,5 +1,2 @@ -oro_layout: - themes: - embedded_default: - label: Default theme for embedded forms - groups: [ embedded_forms ] +label: Default theme for embedded forms +groups: [ embedded_forms ] diff --git a/src/Oro/Bundle/LayoutBundle/DependencyInjection/OroLayoutExtension.php b/src/Oro/Bundle/LayoutBundle/DependencyInjection/OroLayoutExtension.php index 237b35efb10..d18863341df 100644 --- a/src/Oro/Bundle/LayoutBundle/DependencyInjection/OroLayoutExtension.php +++ b/src/Oro/Bundle/LayoutBundle/DependencyInjection/OroLayoutExtension.php @@ -7,6 +7,7 @@ use Symfony\Component\DependencyInjection\Loader; use Symfony\Component\HttpKernel\DependencyInjection\Extension; +use Oro\Component\Config\CumulativeResourceInfo; use Oro\Component\Config\Loader\CumulativeConfigLoader; use Oro\Component\Config\Loader\YamlCumulativeFileLoader; use Oro\Component\Config\Loader\FolderContentCumulativeLoader; @@ -27,7 +28,7 @@ public function load(array $configs, ContainerBuilder $container) [ new FolderingCumulativeFileLoader( '{folder}', - '\w+', + '[a-zA-Z][a-zA-Z0-9_\-:]*', new YamlCumulativeFileLoader('Resources/views/layouts/{folder}/theme.yml') ), new YamlCumulativeFileLoader('Resources/config/oro/layout.yml') @@ -37,7 +38,7 @@ public function load(array $configs, ContainerBuilder $container) $existThemePaths = []; foreach ($themesResources as $resource) { $existThemePaths[$resource->path] = true; - $configs[] = $resource->data['oro_layout']; + $configs[] = $this->getThemeConfig($resource); } $configuration = new Configuration(); @@ -118,7 +119,7 @@ public function load(array $configs, ContainerBuilder $container) * @param array $themes * @return array */ - protected function filterThemeLayoutUpdates($existThemePaths, array $themes) + protected function filterThemeLayoutUpdates(array $existThemePaths, array $themes) { foreach ($themes as $theme => $themePaths) { foreach ($themePaths as $pathIndex => $path) { @@ -135,4 +136,22 @@ protected function filterThemeLayoutUpdates($existThemePaths, array $themes) return $themes; } + + /** + * @param CumulativeResourceInfo $resource + * @return array + */ + protected function getThemeConfig(CumulativeResourceInfo $resource) + { + if ($resource->name === 'layout') { + return $resource->data['oro_layout']; + } else { + $themeName = basename(dirname($resource->path)); + return [ + 'themes' => [ + $themeName => $resource->data + ] + ]; + } + } } diff --git a/src/Oro/Bundle/LayoutBundle/Tests/Unit/Stubs/Bundles/TestBundle/Resources/views/layouts/base/theme.yml b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Stubs/Bundles/TestBundle/Resources/views/layouts/base/theme.yml deleted file mode 100644 index 61b85a00112..00000000000 --- a/src/Oro/Bundle/LayoutBundle/Tests/Unit/Stubs/Bundles/TestBundle/Resources/views/layouts/base/theme.yml +++ /dev/null @@ -1,4 +0,0 @@ -oro_layout: - themes: - oro-black: - description: Oro Black theme description diff --git a/src/Oro/Bundle/LayoutBundle/Tests/Unit/Stubs/Bundles/TestBundle/Resources/views/layouts/oro-black/theme.yml b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Stubs/Bundles/TestBundle/Resources/views/layouts/oro-black/theme.yml new file mode 100644 index 00000000000..8ea6f7f8f7c --- /dev/null +++ b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Stubs/Bundles/TestBundle/Resources/views/layouts/oro-black/theme.yml @@ -0,0 +1 @@ +description: Oro Black theme description From ce9b7f9b267e1806f8d3990f8ca05187928c51cc Mon Sep 17 00:00:00 2001 From: Oleksandr Nechyporenko Date: Tue, 17 Nov 2015 17:02:59 +0200 Subject: [PATCH 035/471] BB-1582: Move theme info file to theme folder - update docs --- .../LayoutBundle/Resources/doc/quick_start.md | 8 ++++- .../Resources/doc/theme_definition.md | 36 +++++++------------ 2 files changed, 19 insertions(+), 25 deletions(-) diff --git a/src/Oro/Bundle/LayoutBundle/Resources/doc/quick_start.md b/src/Oro/Bundle/LayoutBundle/Resources/doc/quick_start.md index 4eb08725708..852e6b39141 100644 --- a/src/Oro/Bundle/LayoutBundle/Resources/doc/quick_start.md +++ b/src/Oro/Bundle/LayoutBundle/Resources/doc/quick_start.md @@ -7,9 +7,15 @@ Create the layout theme ----------------------- The theme definition should be placed at theme folder and named `theme.yml`, for example `DemoBundle/Resources/views/layouts/first_theme/theme.yml` -Deprecated method: placed at `Resources/config/oro/` and named `layout.yml`, for example `DemoBundle/Resources/config/oro/layout.yml` +Deprecated method: placed `Resources/config/oro/` and named `layout.yml`, for example `DemoBundle/Resources/config/oro/layout.yml`: ```yaml +#DemoBundle/Resources/views/layouts/first_theme/theme.yml +label: Test Theme +icon: bundles/demo/images/favicon.ico +groups: [ main ] + +#DemoBundle/Resources/config/oro/layout.yml oro_layout: themes: first_theme: diff --git a/src/Oro/Bundle/LayoutBundle/Resources/doc/theme_definition.md b/src/Oro/Bundle/LayoutBundle/Resources/doc/theme_definition.md index ab6ccd885a2..38b88b484e0 100644 --- a/src/Oro/Bundle/LayoutBundle/Resources/doc/theme_definition.md +++ b/src/Oro/Bundle/LayoutBundle/Resources/doc/theme_definition.md @@ -29,34 +29,22 @@ You can find additional information if you execute the `app/console config:dump- ```yaml # src/Acme/Bundle/DemoBundle/Resources/views/layouts/base/theme.yml - -oro_layout: - themes: - base: - # The layout theme that is used to add the page content and common page elements - # for all themes in "main" group - label: ~ # this is a "hidden" theme - groups: [ main ] +# The layout theme that is used to add the page content and common page elements +# for all themes in "main" group +label: ~ # this is a "hidden" theme +groups: [ main ] # src/Acme/Bundle/DemoBundle/Resources/views/layouts/oro/theme.yml - -oro_layout: - themes: - oro: - # Default layout theme for the Oro Platform - label: Oro Theme - icon: bundles/oroui/themes/oro/images/favicon.ico - parent: base - groups: [ main ] +# Default layout theme for the Oro Platform +label: Oro Theme +icon: bundles/oroui/themes/oro/images/favicon.ico +parent: base +groups: [ main ] # src/Acme/Bundle/DemoBundle/Resources/views/layouts/oro-gold/theme.yml - -oro_layout: - themes: - oro-gold: - label: Nice ORO gold theme - directory: OroGold - parent: oro +label: Nice ORO gold theme +directory: OroGold +parent: oro ``` Where `base`, `oro` and `oro-gold` are unique theme identifiers. From 2572d8e21bb17a76edde21096e985e4bc5433c6b Mon Sep 17 00:00:00 2001 From: Oleksandr Nechyporenko Date: Tue, 17 Nov 2015 17:57:29 +0200 Subject: [PATCH 036/471] BB-1582: Move theme info file to theme folder - update docs --- src/Oro/Bundle/LayoutBundle/Resources/doc/theme_definition.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Oro/Bundle/LayoutBundle/Resources/doc/theme_definition.md b/src/Oro/Bundle/LayoutBundle/Resources/doc/theme_definition.md index 38b88b484e0..0d9f36d59e2 100644 --- a/src/Oro/Bundle/LayoutBundle/Resources/doc/theme_definition.md +++ b/src/Oro/Bundle/LayoutBundle/Resources/doc/theme_definition.md @@ -10,6 +10,7 @@ Basically, think of a **theme** as a skin for your application. Files, that the The configuration file should be placed at theme folder and named `theme.yml`, for example `DemoBundle/Resources/views/layouts/first_theme/theme.yml` Deprecated method: placed at `Resources/config/oro/` and named `layout.yml`, for example `DemoBundle/Resources/config/oro/layout.yml` +Theme folder(name) must match [a-zA-Z][a-zA-Z0-9_\-:]* expression. ### Themes configuration reference From 983d4addf9e38d51b9e64d0c377ad5304f839f87 Mon Sep 17 00:00:00 2001 From: Andrey Yatsenco Date: Tue, 17 Nov 2015 18:06:37 +0200 Subject: [PATCH 037/471] BB-1533: Create form template for new theme and apply - added setFormTheme method to all Layout building related classes - overrided symfony form renderer engines to provide posibility to change defaultThemes set during application run --- .../Compiler/OverrideServiceCompilerPass.php | 43 +++++++++++++++++ .../TemplatingRendererEngine.php | 23 ++++++++++ .../RendererEngine/TwigRendererEngine.php | 23 ++++++++++ .../Layout/TwigLayoutRenderer.php | 13 ++++-- .../Bundle/LayoutBundle/OroLayoutBundle.php | 2 + .../Resources/config/php_renderer.yml | 8 ++++ .../Resources/config/twig_renderer.yml | 6 +++ .../OverrideServiceCompilerPassTest.php | 46 +++++++++++++++++++ .../RendererEngine/RendererEngineTest.php | 34 ++++++++++++++ .../TemplatingRendererEngineTest.php | 19 ++++++++ .../RendererEngine/TwigRendererEngineTest.php | 16 +++++++ .../Unit/Layout/TwigLayoutRendererTest.php | 3 +- .../Layout/DeferredLayoutManipulator.php | 17 +++++++ .../DeferredLayoutManipulatorInterface.php | 9 ++++ .../FormRendererEngineInterface.php | 19 ++++++++ src/Oro/Component/Layout/Layout.php | 19 ++++++++ src/Oro/Component/Layout/LayoutBuilder.php | 12 +++++ .../Layout/LayoutBuilderInterface.php | 9 ++++ .../Layout/LayoutManipulatorInterface.php | 9 ++++ src/Oro/Component/Layout/LayoutRenderer.php | 17 ++++++- .../Layout/LayoutRendererInterface.php | 7 +++ src/Oro/Component/Layout/RawLayout.php | 38 +++++++++++++++ src/Oro/Component/Layout/RawLayoutBuilder.php | 10 ++++ .../Layout/RawLayoutBuilderInterface.php | 9 ++++ .../Layout/Templating/Helper/LayoutHelper.php | 29 ++++++++++-- .../Unit/DeferredLayoutManipulatorTest.php | 15 ++++++ .../Layout/Tests/Unit/LayoutBuilderTest.php | 25 +++++++++- .../Layout/Tests/Unit/LayoutRendererTest.php | 18 +++++++- .../Layout/Tests/Unit/LayoutTest.php | 12 +++++ .../Layout/Tests/Unit/RawLayoutTest.php | 24 ++++++++++ .../Templating/Helper/LayoutHelperTest.php | 19 +++++++- 31 files changed, 539 insertions(+), 14 deletions(-) create mode 100644 src/Oro/Bundle/LayoutBundle/DependencyInjection/Compiler/OverrideServiceCompilerPass.php create mode 100644 src/Oro/Bundle/LayoutBundle/Form/RendererEngine/TemplatingRendererEngine.php create mode 100644 src/Oro/Bundle/LayoutBundle/Form/RendererEngine/TwigRendererEngine.php create mode 100644 src/Oro/Bundle/LayoutBundle/Tests/Unit/DependencyInjection/Compiler/OverrideServiceCompilerPassTest.php create mode 100644 src/Oro/Bundle/LayoutBundle/Tests/Unit/Form/RendererEngine/RendererEngineTest.php create mode 100644 src/Oro/Bundle/LayoutBundle/Tests/Unit/Form/RendererEngine/TemplatingRendererEngineTest.php create mode 100644 src/Oro/Bundle/LayoutBundle/Tests/Unit/Form/RendererEngine/TwigRendererEngineTest.php create mode 100644 src/Oro/Component/Layout/Form/RendererEngine/FormRendererEngineInterface.php diff --git a/src/Oro/Bundle/LayoutBundle/DependencyInjection/Compiler/OverrideServiceCompilerPass.php b/src/Oro/Bundle/LayoutBundle/DependencyInjection/Compiler/OverrideServiceCompilerPass.php new file mode 100644 index 00000000000..de6b8b84d02 --- /dev/null +++ b/src/Oro/Bundle/LayoutBundle/DependencyInjection/Compiler/OverrideServiceCompilerPass.php @@ -0,0 +1,43 @@ +changeService( + $container, + 'twig.form.engine', + 'oro_layout.twig.form.engine' + ); + + $this->changeService( + $container, + 'templating.form.engine', + 'oro_layout.php.templating.form.engine' + ); + } + + /** + * @param ContainerBuilder $container + * @param string $serviceName + * @param string $newServiceName + */ + private function changeService(ContainerBuilder $container, $serviceName, $newServiceName) + { + $service = $container->getDefinition($serviceName); + $newService = $container->getDefinition($newServiceName); + + if ($service && $newService) { + $container->removeDefinition($serviceName); + $container->setDefinition($serviceName, $newService); + } + } +} diff --git a/src/Oro/Bundle/LayoutBundle/Form/RendererEngine/TemplatingRendererEngine.php b/src/Oro/Bundle/LayoutBundle/Form/RendererEngine/TemplatingRendererEngine.php new file mode 100644 index 00000000000..7a2a2c4b060 --- /dev/null +++ b/src/Oro/Bundle/LayoutBundle/Form/RendererEngine/TemplatingRendererEngine.php @@ -0,0 +1,23 @@ +defaultThemes = array_merge($this->defaultThemes, $themes); + } +} diff --git a/src/Oro/Bundle/LayoutBundle/Form/RendererEngine/TwigRendererEngine.php b/src/Oro/Bundle/LayoutBundle/Form/RendererEngine/TwigRendererEngine.php new file mode 100644 index 00000000000..a4332cfbbac --- /dev/null +++ b/src/Oro/Bundle/LayoutBundle/Form/RendererEngine/TwigRendererEngine.php @@ -0,0 +1,23 @@ +defaultThemes = array_merge($this->defaultThemes, $themes); + } +} diff --git a/src/Oro/Bundle/LayoutBundle/Layout/TwigLayoutRenderer.php b/src/Oro/Bundle/LayoutBundle/Layout/TwigLayoutRenderer.php index b1be8bb5fa3..f99765b11e4 100644 --- a/src/Oro/Bundle/LayoutBundle/Layout/TwigLayoutRenderer.php +++ b/src/Oro/Bundle/LayoutBundle/Layout/TwigLayoutRenderer.php @@ -5,16 +5,21 @@ use Symfony\Bridge\Twig\Form\TwigRendererInterface; use Oro\Component\Layout\LayoutRenderer; +use Oro\Component\Layout\Form\RendererEngine\FormRendererEngineInterface; class TwigLayoutRenderer extends LayoutRenderer { /** * @param TwigRendererInterface $innerRenderer - * @param \Twig_Environment $environment + * @param FormRendererEngineInterface $formRendererEngine + * @param \Twig_Environment $environment */ - public function __construct(TwigRendererInterface $innerRenderer, \Twig_Environment $environment) - { + public function __construct( + TwigRendererInterface $innerRenderer, + FormRendererEngineInterface $formRendererEngine, + \Twig_Environment $environment + ) { $innerRenderer->setEnvironment($environment); - parent::__construct($innerRenderer); + parent::__construct($innerRenderer, $formRendererEngine); } } diff --git a/src/Oro/Bundle/LayoutBundle/OroLayoutBundle.php b/src/Oro/Bundle/LayoutBundle/OroLayoutBundle.php index d0b290d2f93..5c2ba6ce62a 100644 --- a/src/Oro/Bundle/LayoutBundle/OroLayoutBundle.php +++ b/src/Oro/Bundle/LayoutBundle/OroLayoutBundle.php @@ -8,6 +8,7 @@ use Oro\Bundle\LayoutBundle\DependencyInjection\Compiler\ConfigurationPass; use Oro\Bundle\LayoutBundle\DependencyInjection\Compiler\ResourcePathProvidersPass; use Oro\Bundle\LayoutBundle\DependencyInjection\Compiler\ConfigExpressionCompilerPass; +use Oro\Bundle\LayoutBundle\DependencyInjection\Compiler\OverrideServiceCompilerPass; class OroLayoutBundle extends Bundle { @@ -21,5 +22,6 @@ public function build(ContainerBuilder $container) $container->addCompilerPass(new ConfigurationPass()); $container->addCompilerPass(new ConfigExpressionCompilerPass()); $container->addCompilerPass(new ResourcePathProvidersPass()); + $container->addCompilerPass(new OverrideServiceCompilerPass()); } } diff --git a/src/Oro/Bundle/LayoutBundle/Resources/config/php_renderer.yml b/src/Oro/Bundle/LayoutBundle/Resources/config/php_renderer.yml index 7e136deed51..1d33c50a9ff 100644 --- a/src/Oro/Bundle/LayoutBundle/Resources/config/php_renderer.yml +++ b/src/Oro/Bundle/LayoutBundle/Resources/config/php_renderer.yml @@ -23,11 +23,19 @@ services: class: %oro_layout.php.layout_renderer.class% arguments: - @oro_layout.php.renderer + - @templating.form.engine oro_layout.php.templating.helper: class: %oro_layout.php.templating.helper.class% arguments: - @oro_layout.php.renderer - @oro_layout.text.helper + - @templating.form.engine tags: - { name: templating.helper, alias: layout } + + oro_layout.php.templating.form.engine: + class: Oro\Bundle\LayoutBundle\Form\RendererEngine\TemplatingRendererEngine + arguments: + - @templating.engine.php + - %templating.helper.form.resources% diff --git a/src/Oro/Bundle/LayoutBundle/Resources/config/twig_renderer.yml b/src/Oro/Bundle/LayoutBundle/Resources/config/twig_renderer.yml index 159e0a73f0d..c88f0413861 100644 --- a/src/Oro/Bundle/LayoutBundle/Resources/config/twig_renderer.yml +++ b/src/Oro/Bundle/LayoutBundle/Resources/config/twig_renderer.yml @@ -31,4 +31,10 @@ services: class: %oro_layout.twig.layout_renderer.class% arguments: - @oro_layout.twig.renderer + - @twig.form.engine - @twig + + oro_layout.twig.form.engine: + class: Oro\Bundle\LayoutBundle\Form\RendererEngine\TwigRendererEngine + arguments: + - %twig.form.resources% diff --git a/src/Oro/Bundle/LayoutBundle/Tests/Unit/DependencyInjection/Compiler/OverrideServiceCompilerPassTest.php b/src/Oro/Bundle/LayoutBundle/Tests/Unit/DependencyInjection/Compiler/OverrideServiceCompilerPassTest.php new file mode 100644 index 00000000000..6aa5b5afc3e --- /dev/null +++ b/src/Oro/Bundle/LayoutBundle/Tests/Unit/DependencyInjection/Compiler/OverrideServiceCompilerPassTest.php @@ -0,0 +1,46 @@ +pass = new OverrideServiceCompilerPass(); + } + + protected function tearDown() + { + unset($this->pass); + } + + public function testProcess() + { + $container = new ContainerBuilder(); + $twigFormEngine = new Definition('\TwigFormEngineClass'); + $container->setDefinition('twig.form.engine', $twigFormEngine); + + $newTwigFormEngine = new Definition('\OroLayoutTwigFormEngineClass'); + $container->setDefinition('oro_layout.twig.form.engine', $newTwigFormEngine); + + $phpFormEngine = new Definition('\PhpFormEngineClass'); + $container->setDefinition('templating.form.engine', $phpFormEngine); + + $newPhpFormEngine = new Definition('\OroLayoutPhpFormEngineClass'); + $container->setDefinition('oro_layout.php.templating.form.engine', $newPhpFormEngine); + + $this->pass->process($container); + + $this->assertEquals($newTwigFormEngine, $container->getDefinition('twig.form.engine')); + $this->assertEquals($newPhpFormEngine, $container->getDefinition('templating.form.engine')); + } +} diff --git a/src/Oro/Bundle/LayoutBundle/Tests/Unit/Form/RendererEngine/RendererEngineTest.php b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Form/RendererEngine/RendererEngineTest.php new file mode 100644 index 00000000000..49c592d252a --- /dev/null +++ b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Form/RendererEngine/RendererEngineTest.php @@ -0,0 +1,34 @@ +createRendererEngine(); + + $reflectionClass = new \ReflectionClass(get_class($renderingEngine)); + $property = $reflectionClass->getProperty('defaultThemes'); + $property->setAccessible(true); + + $actual = $property->getValue($renderingEngine); + $this->assertNotContains('newThemePath', $actual); + + $renderingEngine->addDefaultThemes('newThemePath'); + $actual = $property->getValue($renderingEngine); + $this->assertContains('newThemePath', $actual); + + $renderingEngine->addDefaultThemes(['newThemePath2', 'newThemePath3']); + $actual = $property->getValue($renderingEngine); + $this->assertContains('newThemePath2', $actual); + $this->assertContains('newThemePath3', $actual); + } + + /** + * @return \Oro\Component\Layout\Form\RendererEngine\FormRendererEngineInterface + */ + abstract public function createRendererEngine(); +} diff --git a/src/Oro/Bundle/LayoutBundle/Tests/Unit/Form/RendererEngine/TemplatingRendererEngineTest.php b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Form/RendererEngine/TemplatingRendererEngineTest.php new file mode 100644 index 00000000000..1797e9de97d --- /dev/null +++ b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Form/RendererEngine/TemplatingRendererEngineTest.php @@ -0,0 +1,19 @@ +getMock('Symfony\Component\Templating\EngineInterface'); + + return new TemplatingRendererEngine($templatingEngine); + } +} diff --git a/src/Oro/Bundle/LayoutBundle/Tests/Unit/Form/RendererEngine/TwigRendererEngineTest.php b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Form/RendererEngine/TwigRendererEngineTest.php new file mode 100644 index 00000000000..5f47013f90b --- /dev/null +++ b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Form/RendererEngine/TwigRendererEngineTest.php @@ -0,0 +1,16 @@ +expects($this->once()) ->method('setEnvironment') ->with($this->identicalTo($environment)); + $formRenderer = $this->getMock('Oro\Component\Layout\Form\RendererEngine\FormRendererEngineInterface'); - new TwigLayoutRenderer($innerRenderer, $environment); + new TwigLayoutRenderer($innerRenderer, $formRenderer, $environment); } } diff --git a/src/Oro/Component/Layout/DeferredLayoutManipulator.php b/src/Oro/Component/Layout/DeferredLayoutManipulator.php index a6c5956477f..576cb40cb4c 100644 --- a/src/Oro/Component/Layout/DeferredLayoutManipulator.php +++ b/src/Oro/Component/Layout/DeferredLayoutManipulator.php @@ -51,6 +51,9 @@ class DeferredLayoutManipulator implements DeferredLayoutManipulatorInterface /** The action name for add the theme(s) to be used for rendering the layout item and its children */ const SET_BLOCK_THEME = 12; + /** The action name for add the form theme(s) to be used for rendering forms */ + const SET_FORM_THEME = 13; + /** @var LayoutRegistryInterface */ protected $registry; @@ -291,6 +294,20 @@ public function setBlockTheme($themes, $id = null) return $this; } + /** + * {@inheritdoc} + */ + public function setFormTheme($themes) + { + $this->actions[self::GROUP_ADD][++$this->lastIndex] = [ + self::SET_FORM_THEME, + __FUNCTION__, + [$themes] + ]; + + return $this; + } + /** * {@inheritdoc} */ diff --git a/src/Oro/Component/Layout/DeferredLayoutManipulatorInterface.php b/src/Oro/Component/Layout/DeferredLayoutManipulatorInterface.php index 03e102700dd..2f8867f4278 100644 --- a/src/Oro/Component/Layout/DeferredLayoutManipulatorInterface.php +++ b/src/Oro/Component/Layout/DeferredLayoutManipulatorInterface.php @@ -158,6 +158,15 @@ public function changeBlockType($id, $blockType, $optionsCallback = null); */ public function setBlockTheme($themes, $id = null); + /** + * Sets the theme(s) to be used for rendering forms + * + * @param string|string[] $themes The theme(s). For example 'MyBundle:Layout:my_theme.html.twig' + * + * @return self + */ + public function setFormTheme($themes); + /** * Reverts the manipulator to the initial state * diff --git a/src/Oro/Component/Layout/Form/RendererEngine/FormRendererEngineInterface.php b/src/Oro/Component/Layout/Form/RendererEngine/FormRendererEngineInterface.php new file mode 100644 index 00000000000..321facf430b --- /dev/null +++ b/src/Oro/Component/Layout/Form/RendererEngine/FormRendererEngineInterface.php @@ -0,0 +1,19 @@ +themes as $theme) { $renderer->setBlockTheme($theme[0], $theme[1]); } + $renderer->setFormTheme($this->formThemes); return $renderer->renderBlock($this->view); } @@ -81,4 +85,19 @@ public function setBlockTheme($themes, $blockId = null) return $this; } + + /** + * Sets the theme(s) to be used for rendering forms + * + * @param string|string[] $themes The theme(s). For example 'MyBundle:Layout:my_theme.html.twig' + * + * @return self + */ + public function setFormTheme($themes) + { + $themes = is_array($themes) ? $themes : [$themes]; + $this->formThemes = array_merge($this->formThemes, $themes); + + return $this; + } } diff --git a/src/Oro/Component/Layout/LayoutBuilder.php b/src/Oro/Component/Layout/LayoutBuilder.php index 00f7b1989e4..12a5ae6103c 100644 --- a/src/Oro/Component/Layout/LayoutBuilder.php +++ b/src/Oro/Component/Layout/LayoutBuilder.php @@ -166,6 +166,16 @@ public function setBlockTheme($themes, $id = null) return $this; } + /** + * {@inheritdoc} + */ + public function setFormTheme($themes) + { + $this->layoutManipulator->setFormTheme($themes); + + return $this; + } + /** * {@inheritdoc} */ @@ -195,6 +205,8 @@ public function getLayout(ContextInterface $context, $rootId = null) foreach ($blockThemes as $blockId => $themes) { $layout->setBlockTheme($themes, $blockId !== $rootBlockId ? $blockId : null); } + $formThemes = $rawLayout->getFormThemes(); + $layout->setFormTheme($formThemes); return $layout; } diff --git a/src/Oro/Component/Layout/LayoutBuilderInterface.php b/src/Oro/Component/Layout/LayoutBuilderInterface.php index d3f01eb01e4..5723ec1da62 100644 --- a/src/Oro/Component/Layout/LayoutBuilderInterface.php +++ b/src/Oro/Component/Layout/LayoutBuilderInterface.php @@ -159,6 +159,15 @@ public function changeBlockType($id, $blockType, $optionsCallback = null); */ public function setBlockTheme($themes, $id = null); + /** + * Sets the theme(s) to be used for rendering forms + * + * @param string|string[] $themes The theme(s). For example 'MyBundle:Layout:my_theme.html.twig' + * + * @return self + */ + public function setFormTheme($themes); + /** * Reverts the builder to the initial state * diff --git a/src/Oro/Component/Layout/LayoutManipulatorInterface.php b/src/Oro/Component/Layout/LayoutManipulatorInterface.php index 8f9928d3c3b..ff937a4163b 100644 --- a/src/Oro/Component/Layout/LayoutManipulatorInterface.php +++ b/src/Oro/Component/Layout/LayoutManipulatorInterface.php @@ -155,6 +155,15 @@ public function changeBlockType($id, $blockType, $optionsCallback = null); */ public function setBlockTheme($themes, $id = null); + /** + * Sets the theme(s) to be used for rendering forms + * + * @param string|string[] $themes The theme(s). For example 'MyBundle:Layout:my_theme.html.twig' + * + * @return self + */ + public function setFormTheme($themes); + /** * Reverts the manipulator to the initial state * diff --git a/src/Oro/Component/Layout/LayoutRenderer.php b/src/Oro/Component/Layout/LayoutRenderer.php index 09388501e0d..08610ad8e69 100644 --- a/src/Oro/Component/Layout/LayoutRenderer.php +++ b/src/Oro/Component/Layout/LayoutRenderer.php @@ -4,17 +4,24 @@ use Symfony\Component\Form\FormRendererInterface; +use Oro\Component\Layout\Form\RendererEngine\FormRendererEngineInterface; + class LayoutRenderer implements LayoutRendererInterface { /** @var FormRendererInterface */ protected $innerRenderer; + /** @var FormRendererEngineInterface */ + private $formRendererEngine; + /** * @param FormRendererInterface $innerRenderer + * @param FormRendererEngineInterface $formRendererEngine */ - public function __construct(FormRendererInterface $innerRenderer) + public function __construct(FormRendererInterface $innerRenderer, FormRendererEngineInterface $formRendererEngine) { $this->innerRenderer = $innerRenderer; + $this->formRendererEngine = $formRendererEngine; } /** @@ -32,4 +39,12 @@ public function setBlockTheme(BlockView $view, $themes) { $this->innerRenderer->setTheme($view, $themes); } + + /** + * {@inheritdoc} + */ + public function setFormTheme($themes) + { + $this->formRendererEngine->addDefaultThemes($themes); + } } diff --git a/src/Oro/Component/Layout/LayoutRendererInterface.php b/src/Oro/Component/Layout/LayoutRendererInterface.php index f60ccad0019..4d52933925f 100644 --- a/src/Oro/Component/Layout/LayoutRendererInterface.php +++ b/src/Oro/Component/Layout/LayoutRendererInterface.php @@ -20,4 +20,11 @@ public function renderBlock(BlockView $view); * @param string|string[] $themes The theme(s). For example 'MyBundle:Layout:my_theme.html.twig' */ public function setBlockTheme(BlockView $view, $themes); + + /** + * Sets the theme(s) to be used for rendering forms + * + * @param string|string[] $themes The theme(s). For example 'MyBundle:Layout:my_theme.html.twig' + */ + public function setFormTheme($themes); } diff --git a/src/Oro/Component/Layout/RawLayout.php b/src/Oro/Component/Layout/RawLayout.php index 398f7be2550..f2806ade0d9 100644 --- a/src/Oro/Component/Layout/RawLayout.php +++ b/src/Oro/Component/Layout/RawLayout.php @@ -58,6 +58,9 @@ class RawLayout /** @var array */ protected $blockThemes = []; + /** @var array */ + protected $formThemes = []; + public function __construct() { $this->hierarchy = new HierarchyCollection(); @@ -509,6 +512,41 @@ public function setBlockTheme($id, $themes) } } + /** + * Returns all registered themes to be used for rendering forms + * + * Example of returned data: + * [ + * 'MyBundle:Layout:div_form_layout.html.twig', + * 'AcmeBundle:Layout:div_form_layout.html.twig' + * ] + * + * @return array + */ + public function getFormThemes() + { + return $this->formThemes; + } + + /** + * Sets the theme(s) to be used for rendering the layout item and its children + * + * @param string|string[] $themes The theme(s). For example 'MyBundle:Layout:my_theme.html.twig' + * + * @return self + */ + public function setFormTheme($themes) + { + if (empty($themes)) { + throw new Exception\InvalidArgumentException('The theme must not be empty.'); + } + if (!is_string($themes) && !is_array($themes)) { + throw new Exception\UnexpectedTypeException($themes, 'string or array of strings', 'themes'); + } + + $this->formThemes = array_merge($this->formThemes, (array)$themes); + } + /** * Returns the layout items hierarchy from the given path * diff --git a/src/Oro/Component/Layout/RawLayoutBuilder.php b/src/Oro/Component/Layout/RawLayoutBuilder.php index d38e3cb57d7..de99a244f71 100644 --- a/src/Oro/Component/Layout/RawLayoutBuilder.php +++ b/src/Oro/Component/Layout/RawLayoutBuilder.php @@ -359,6 +359,16 @@ public function setBlockTheme($themes, $id = null) return $this; } + /** + * {@inheritdoc} + */ + public function setFormTheme($themes) + { + $this->rawLayout->setFormTheme($themes); + + return $this; + } + /** * {@inheritdoc} */ diff --git a/src/Oro/Component/Layout/RawLayoutBuilderInterface.php b/src/Oro/Component/Layout/RawLayoutBuilderInterface.php index 06b0728fd34..c1adf9b8b67 100644 --- a/src/Oro/Component/Layout/RawLayoutBuilderInterface.php +++ b/src/Oro/Component/Layout/RawLayoutBuilderInterface.php @@ -165,6 +165,15 @@ public function clear(); */ public function setBlockTheme($themes, $id = null); + /** + * Sets the theme(s) to be used for rendering forms + * + * @param string|string[] $themes The theme(s). For example 'MyBundle:Layout:my_theme.html.twig' + * + * @return self + */ + public function setFormTheme($themes); + /** * Checks whether at least one item exists in the layout * diff --git a/src/Oro/Component/Layout/Templating/Helper/LayoutHelper.php b/src/Oro/Component/Layout/Templating/Helper/LayoutHelper.php index f231d37d9e4..719b085a5a9 100644 --- a/src/Oro/Component/Layout/Templating/Helper/LayoutHelper.php +++ b/src/Oro/Component/Layout/Templating/Helper/LayoutHelper.php @@ -7,6 +7,7 @@ use Oro\Component\Layout\BlockView; use Oro\Component\Layout\Templating\TextHelper; +use Oro\Component\Layout\Form\RendererEngine\FormRendererEngineInterface; /** * LayoutHelper provides helpers to help display layout blocks @@ -19,14 +20,22 @@ class LayoutHelper extends Helper /** @var TextHelper */ private $textHelper; + /** @var \Oro\Component\Layout\Form\RendererEngine\FormRendererEngineInterface */ + private $formRendererEngine; + /** * @param FormRendererInterface $renderer - * @param TextHelper $textHelper + * @param TextHelper $textHelper + * @param FormRendererEngineInterface $formRendererEngine */ - public function __construct(FormRendererInterface $renderer, TextHelper $textHelper) - { - $this->renderer = $renderer; + public function __construct( + FormRendererInterface $renderer, + TextHelper $textHelper, + FormRendererEngineInterface $formRendererEngine + ) { + $this->renderer = $renderer; $this->textHelper = $textHelper; + $this->formRendererEngine = $formRendererEngine; } /** @@ -50,6 +59,18 @@ public function setBlockTheme(BlockView $view, $themes) $this->renderer->setTheme($view, $themes); } + /** + * Sets the theme(s) to be used for rendering forms + * + * The theme format is ":". + * + * @param string|string[] $themes The theme(s). For example 'MuBundle:Layout/php' + */ + public function setFormTheme($themes) + { + $this->formRendererEngine->addDefaultThemes($themes); + } + /** * Renders the HTML for a given view * diff --git a/src/Oro/Component/Layout/Tests/Unit/DeferredLayoutManipulatorTest.php b/src/Oro/Component/Layout/Tests/Unit/DeferredLayoutManipulatorTest.php index c8fc81da1b8..442d0807db8 100644 --- a/src/Oro/Component/Layout/Tests/Unit/DeferredLayoutManipulatorTest.php +++ b/src/Oro/Component/Layout/Tests/Unit/DeferredLayoutManipulatorTest.php @@ -1601,6 +1601,21 @@ public function testSetBlockTheme() ); } + public function testSetFormTheme() + { + $this->layoutManipulator + ->add('root', null, 'root') + ->setFormTheme(['MyBundle:Layout:form_theme1.html.twig', 'MyBundle:Layout:form_theme2.html.twig']); + + $this->getLayoutView(); + + $formThemes = $this->rawLayoutBuilder->getRawLayout()->getFormThemes(); + $this->assertSame( + ['MyBundle:Layout:form_theme1.html.twig', 'MyBundle:Layout:form_theme2.html.twig'], + $formThemes + ); + } + // @codingStandardsIgnoreStart /** * @expectedException \Oro\Component\Layout\Exception\DeferredUpdateFailureException diff --git a/src/Oro/Component/Layout/Tests/Unit/LayoutBuilderTest.php b/src/Oro/Component/Layout/Tests/Unit/LayoutBuilderTest.php index 49302a84afe..3464b9e0ae5 100644 --- a/src/Oro/Component/Layout/Tests/Unit/LayoutBuilderTest.php +++ b/src/Oro/Component/Layout/Tests/Unit/LayoutBuilderTest.php @@ -253,6 +253,13 @@ public function testGetLayout() ->method('setBlockTheme') ->with('TestTheme3', 'test_block'); + $this->layoutManipulator->expects($this->at(4)) + ->method('setFormTheme') + ->with('TestFormTheme1'); + $this->layoutManipulator->expects($this->at(5)) + ->method('setFormTheme') + ->with(['TestFormTheme2']); + $this->layoutManipulator->expects($this->once()) ->method('applyChanges') ->with($this->identicalTo($context), false); @@ -281,7 +288,6 @@ public function testGetLayout() ] ) ); - $layout->expects($this->at(0)) ->method('setBlockTheme') ->with(['RootTheme1', 'RootTheme2', 'RootTheme3'], $this->identicalTo(null)); @@ -291,11 +297,26 @@ public function testGetLayout() $layout->expects($this->exactly(2)) ->method('setBlockTheme'); + $rawLayout->expects($this->once()) + ->method('getFormThemes') + ->will( + $this->returnValue( + ['TestFormTheme1', 'TestFormTheme2'] + ) + ); + $layout->expects($this->at(2)) + ->method('setFormTheme') + ->with(['TestFormTheme1', 'TestFormTheme2']); + $layout->expects($this->once()) + ->method('setFormTheme'); + $this->layoutBuilder ->setBlockTheme('RootTheme1') ->setBlockTheme(['RootTheme2', 'RootTheme3']) ->setBlockTheme(['TestTheme1', 'TestTheme2'], 'test_block') - ->setBlockTheme('TestTheme3', 'test_block'); + ->setBlockTheme('TestTheme3', 'test_block') + ->setFormTheme('TestFormTheme1') + ->setFormTheme(['TestFormTheme2']); $result = $this->layoutBuilder->getLayout($context, $rootId); $this->assertSame($layout, $result); diff --git a/src/Oro/Component/Layout/Tests/Unit/LayoutRendererTest.php b/src/Oro/Component/Layout/Tests/Unit/LayoutRendererTest.php index de7b3ef377b..d0399a21047 100644 --- a/src/Oro/Component/Layout/Tests/Unit/LayoutRendererTest.php +++ b/src/Oro/Component/Layout/Tests/Unit/LayoutRendererTest.php @@ -4,6 +4,7 @@ use Oro\Component\Layout\BlockView; use Oro\Component\Layout\LayoutRenderer; +use Oro\Component\Layout\Form\RendererEngine\FormRendererEngineInterface; class LayoutRendererTest extends \PHPUnit_Framework_TestCase { @@ -13,10 +14,14 @@ class LayoutRendererTest extends \PHPUnit_Framework_TestCase /** @var LayoutRenderer */ protected $renderer; + /** @var FormRendererEngineInterface */ + protected $formRenderer; + protected function setUp() { $this->innerRenderer = $this->getMock('Symfony\Component\Form\FormRendererInterface'); - $this->renderer = new LayoutRenderer($this->innerRenderer); + $this->formRenderer = $this->getMock('Oro\Component\Layout\Form\RendererEngine\FormRendererEngineInterface'); + $this->renderer = new LayoutRenderer($this->innerRenderer, $this->formRenderer); } public function testRenderBlock() @@ -46,4 +51,15 @@ public function testSetBlockTheme() $this->renderer->setBlockTheme($view, $theme); } + + public function testSetFormTheme() + { + $theme = 'MyBundle::forms.html.twig'; + + $this->formRenderer->expects($this->once()) + ->method('addDefaultThemes') + ->with($theme); + + $this->renderer->setFormTheme($theme); + } } diff --git a/src/Oro/Component/Layout/Tests/Unit/LayoutTest.php b/src/Oro/Component/Layout/Tests/Unit/LayoutTest.php index 045daac1aa2..de1d07773db 100644 --- a/src/Oro/Component/Layout/Tests/Unit/LayoutTest.php +++ b/src/Oro/Component/Layout/Tests/Unit/LayoutTest.php @@ -123,4 +123,16 @@ public function testRenderWithBlockThemeForChild() $result = $layout->render(); $this->assertEquals($expected, $result); } + + public function testSetFormTheme() + { + $theme = 'MyBundle::forms.html.twig'; + $view = new BlockView(); + $this->renderer->expects($this->once()) + ->method('setFormTheme') + ->with([$theme]); + $layout = new Layout($view, $this->rendererRegistry); + $layout->setFormTheme($theme); + $layout->render(); + } } diff --git a/src/Oro/Component/Layout/Tests/Unit/RawLayoutTest.php b/src/Oro/Component/Layout/Tests/Unit/RawLayoutTest.php index 8391bee332e..bfc6994d5a7 100644 --- a/src/Oro/Component/Layout/Tests/Unit/RawLayoutTest.php +++ b/src/Oro/Component/Layout/Tests/Unit/RawLayoutTest.php @@ -1007,6 +1007,30 @@ public function testSetBlockThemeWithInvalidThemeType() $this->rawLayout->setBlockTheme('root', 123); } + public function testSetFormTheme() + { + // prepare test data + $this->rawLayout->add('root', null, 'root'); + + // do test + $this->rawLayout->setFormTheme( + ['MyBundle:Layout:theme1.html.twig', 'MyBundle:Layout:theme2.html.twig'] + ); + $this->rawLayout->setFormTheme( + 'MyBundle:Layout:theme3.html.twig' + ); + + $formThemes = $this->rawLayout->getFormThemes(); + $this->assertSame( + [ + 'MyBundle:Layout:theme1.html.twig', + 'MyBundle:Layout:theme2.html.twig', + 'MyBundle:Layout:theme3.html.twig' + ], + $formThemes + ); + } + public function testGetHierarchy() { // prepare test data diff --git a/src/Oro/Component/Layout/Tests/Unit/Templating/Helper/LayoutHelperTest.php b/src/Oro/Component/Layout/Tests/Unit/Templating/Helper/LayoutHelperTest.php index bb27f2b61a9..0082935a818 100644 --- a/src/Oro/Component/Layout/Tests/Unit/Templating/Helper/LayoutHelperTest.php +++ b/src/Oro/Component/Layout/Tests/Unit/Templating/Helper/LayoutHelperTest.php @@ -4,6 +4,7 @@ use Oro\Component\Layout\BlockView; use Oro\Component\Layout\Templating\Helper\LayoutHelper; +use Oro\Component\Layout\Form\RendererEngine\FormRendererEngineInterface; class LayoutHelperTest extends \PHPUnit_Framework_TestCase { @@ -16,6 +17,9 @@ class LayoutHelperTest extends \PHPUnit_Framework_TestCase /** @var LayoutHelper */ protected $helper; + /** @var \Oro\Component\Layout\Form\RendererEngine\FormRendererEngineInterface */ + protected $formRenderer; + protected function setUp() { $this->renderer = $this->getMock('Symfony\Component\Form\FormRendererInterface'); @@ -23,7 +27,9 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); - $this->helper = new LayoutHelper($this->renderer, $this->textHelper); + $this->formRenderer = $this->getMock('Oro\Component\Layout\Form\RendererEngine\FormRendererEngineInterface'); + + $this->helper = new LayoutHelper($this->renderer, $this->textHelper, $this->formRenderer); } public function testGetName() @@ -43,6 +49,17 @@ public function testSetBlockTheme() $this->helper->setBlockTheme($view, $theme); } + public function testSetFormTheme() + { + $theme = 'MyBundle:Layout\php'; + + $this->formRenderer->expects($this->once()) + ->method('addDefaultThemes') + ->with($theme); + + $this->helper->setFormTheme($theme); + } + public function testWidgetRendering() { $view = new BlockView(); From ffbf9435affa4699d2115e1009d70fbf0679ad7c Mon Sep 17 00:00:00 2001 From: Oleksandr Nechyporenko Date: Wed, 18 Nov 2015 12:47:57 +0200 Subject: [PATCH 038/471] BB-1596: Add ability to set additional data for theme - add data property and methods to Theme model --- .../DependencyInjection/Configuration.php | 4 +++ .../Layout/Extension/Theme/Model/Theme.php | 32 +++++++++++++++++++ .../Extension/Theme/Model/ThemeFactory.php | 3 ++ .../Theme/Model/ThemeFactoryTest.php | 4 ++- .../Unit/Extension/Theme/Model/ThemeTest.php | 13 ++++++++ 5 files changed, 55 insertions(+), 1 deletion(-) diff --git a/src/Oro/Bundle/LayoutBundle/DependencyInjection/Configuration.php b/src/Oro/Bundle/LayoutBundle/DependencyInjection/Configuration.php index 864057aab02..c3ada1db73b 100644 --- a/src/Oro/Bundle/LayoutBundle/DependencyInjection/Configuration.php +++ b/src/Oro/Bundle/LayoutBundle/DependencyInjection/Configuration.php @@ -145,6 +145,10 @@ protected function appendThemingNodes(ArrayNodeDefinition $parentNode) ->prototype('scalar')->end() ->cannotBeEmpty() ->end() + ->arrayNode('data') + ->prototype('array') + ->info('Layout additional data') + ->end() ->end() ->end(); diff --git a/src/Oro/Component/Layout/Extension/Theme/Model/Theme.php b/src/Oro/Component/Layout/Extension/Theme/Model/Theme.php index 30b15c84b74..c1dc6ebd1f0 100644 --- a/src/Oro/Component/Layout/Extension/Theme/Model/Theme.php +++ b/src/Oro/Component/Layout/Extension/Theme/Model/Theme.php @@ -31,6 +31,9 @@ class Theme /** @var string[] */ protected $groups = []; + /** @var mixed[] */ + protected $data = []; + /** * @param string $name * @param $parentTheme @@ -176,4 +179,33 @@ public function getGroups() { return $this->groups; } + + /** + * @param mixed[] $data + */ + public function setData(array $data) + { + $this->data = $data; + } + + /** + * @return mixed[] + */ + public function getAllData() + { + return $this->data; + } + + /** + * @param string $key + * @param mixed $default + * @return mixed|null + */ + public function getData($key, $default = null) + { + if (array_key_exists($key, $this->data)) { + return $this->data[$key]; + } + return $default; + } } diff --git a/src/Oro/Component/Layout/Extension/Theme/Model/ThemeFactory.php b/src/Oro/Component/Layout/Extension/Theme/Model/ThemeFactory.php index b4a54cfa3dd..9f83873efc7 100644 --- a/src/Oro/Component/Layout/Extension/Theme/Model/ThemeFactory.php +++ b/src/Oro/Component/Layout/Extension/Theme/Model/ThemeFactory.php @@ -37,6 +37,9 @@ public function create($themeName, array $themeDefinition) if (isset($themeDefinition['description'])) { $theme->setDescription($themeDefinition['description']); } + if (isset($themeDefinition['data'])) { + $theme->setData($themeDefinition['data']); + } return $theme; } diff --git a/src/Oro/Component/Layout/Tests/Unit/Extension/Theme/Model/ThemeFactoryTest.php b/src/Oro/Component/Layout/Tests/Unit/Extension/Theme/Model/ThemeFactoryTest.php index 2cf83fd0fcf..24e99a80a01 100644 --- a/src/Oro/Component/Layout/Tests/Unit/Extension/Theme/Model/ThemeFactoryTest.php +++ b/src/Oro/Component/Layout/Tests/Unit/Extension/Theme/Model/ThemeFactoryTest.php @@ -49,6 +49,7 @@ public function themeDefinitionDataProvider() $fullDefinition->setDirectory('OroBlack'); $fullDefinition->setGroups(['main', 'frontend']); $fullDefinition->setDescription('description'); + $fullDefinition->setData(['key' => 'value']); return [ 'minimal definition given' => [ @@ -66,7 +67,8 @@ public function themeDefinitionDataProvider() 'icon' => 'oro-black-icon.ico', 'logo' => 'oro-black-logo.png', 'directory' => 'OroBlack', - 'description' => 'description' + 'description' => 'description', + 'data' => ['key' => 'value'] ], '$expectedResult' => $fullDefinition, ] diff --git a/src/Oro/Component/Layout/Tests/Unit/Extension/Theme/Model/ThemeTest.php b/src/Oro/Component/Layout/Tests/Unit/Extension/Theme/Model/ThemeTest.php index a6ab3f9773e..308c5c06e29 100644 --- a/src/Oro/Component/Layout/Tests/Unit/Extension/Theme/Model/ThemeTest.php +++ b/src/Oro/Component/Layout/Tests/Unit/Extension/Theme/Model/ThemeTest.php @@ -84,4 +84,17 @@ public function testDescriptionMethods() $this->theme->setDescription('test'); $this->assertEquals('test', $this->theme->getDescription()); } + + public function testDataMethods() + { + $data = [ + 'key' => 'value', + ]; + + $this->assertEquals([], $this->theme->getAllData()); + $this->theme->setData($data); + $this->assertEquals($data, $this->theme->getAllData()); + $this->assertEquals($data['key'], $this->theme->getData('key')); + $this->assertEquals('default value', $this->theme->getData('unknown key', 'default value')); + } } From bd3141cb2dbc0d16b917bf57369ccad23054becb Mon Sep 17 00:00:00 2001 From: Oleksandr Nechyporenko Date: Wed, 18 Nov 2015 13:07:45 +0200 Subject: [PATCH 039/471] BB-1596: Add ability to set additional data for theme - add method to access data by key --- .../Layout/Extension/Theme/Model/Theme.php | 15 +++++++++++++-- .../Unit/Extension/Theme/Model/ThemeTest.php | 10 ++++++---- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/Oro/Component/Layout/Extension/Theme/Model/Theme.php b/src/Oro/Component/Layout/Extension/Theme/Model/Theme.php index c1dc6ebd1f0..9f20806638b 100644 --- a/src/Oro/Component/Layout/Extension/Theme/Model/Theme.php +++ b/src/Oro/Component/Layout/Extension/Theme/Model/Theme.php @@ -191,17 +191,28 @@ public function setData(array $data) /** * @return mixed[] */ - public function getAllData() + public function getData() { return $this->data; } + /** + * @param string $key + * @param mixed $value + * @return $this + */ + public function setDataByKey($key, $value) + { + $this->data[$key] = $value; + return $this; + } + /** * @param string $key * @param mixed $default * @return mixed|null */ - public function getData($key, $default = null) + public function getDataByKey($key, $default = null) { if (array_key_exists($key, $this->data)) { return $this->data[$key]; diff --git a/src/Oro/Component/Layout/Tests/Unit/Extension/Theme/Model/ThemeTest.php b/src/Oro/Component/Layout/Tests/Unit/Extension/Theme/Model/ThemeTest.php index 308c5c06e29..f64fcccdfd4 100644 --- a/src/Oro/Component/Layout/Tests/Unit/Extension/Theme/Model/ThemeTest.php +++ b/src/Oro/Component/Layout/Tests/Unit/Extension/Theme/Model/ThemeTest.php @@ -91,10 +91,12 @@ public function testDataMethods() 'key' => 'value', ]; - $this->assertEquals([], $this->theme->getAllData()); + $this->assertEquals([], $this->theme->getData()); $this->theme->setData($data); - $this->assertEquals($data, $this->theme->getAllData()); - $this->assertEquals($data['key'], $this->theme->getData('key')); - $this->assertEquals('default value', $this->theme->getData('unknown key', 'default value')); + $this->assertEquals($data, $this->theme->getData()); + $this->assertEquals($data['key'], $this->theme->getDataByKey('key')); + $this->assertEquals('default value', $this->theme->getDataByKey('unknown key', 'default value')); + $this->theme->setDataByKey('unknown key', 'unknown value'); + $this->assertEquals('unknown value', $this->theme->getDataByKey('unknown key', 'default value')); } } From fbdf0d6222278733f7fec0a1bc9ce7afd116ea13 Mon Sep 17 00:00:00 2001 From: Konstantin Stupak Date: Fri, 20 Nov 2015 16:39:59 +0200 Subject: [PATCH 040/471] Grammar fixes --- .../Resources/public/js/datagrid/action/delete-mass-action.js | 2 +- .../DataGridBundle/Resources/translations/jsmessages.en.yml | 2 +- .../DataGridBundle/Resources/translations/messages.en.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/action/delete-mass-action.js b/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/action/delete-mass-action.js index 6cca0a491b7..7759f5498eb 100644 --- a/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/action/delete-mass-action.js +++ b/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/action/delete-mass-action.js @@ -20,7 +20,7 @@ define([ /** @property {Object} */ defaultMessages: { confirm_title: 'Delete Confirmation', - confirm_content: 'Are you sure you want to do remove selected items?', + confirm_content: 'Are you sure you want to remove selected items?', confirm_ok: 'Yes, Delete', confirm_cancel: 'Cancel', success: 'Selected items were removed.', diff --git a/src/Oro/Bundle/DataGridBundle/Resources/translations/jsmessages.en.yml b/src/Oro/Bundle/DataGridBundle/Resources/translations/jsmessages.en.yml index 74eba96ad8b..ceae1410c71 100644 --- a/src/Oro/Bundle/DataGridBundle/Resources/translations/jsmessages.en.yml +++ b/src/Oro/Bundle/DataGridBundle/Resources/translations/jsmessages.en.yml @@ -1,7 +1,7 @@ "Item deleted": "Item deleted" "Are you sure you want to delete this item?": "Are you sure you want to delete this item?" "Delete Error": "Delete Error" -"Are you sure you want to do remove selected items?": "Are you sure you want to do remove selected items?" +"Are you sure you want to remove selected items?": "Are you sure you want to remove selected items?" "Are you sure you want to remove this item?": "Are you sure you want to remove this item?" "Selected items were removed.": "Selected items were removed." "Selected items were not removed.": "Selected items were removed." diff --git a/src/Oro/Bundle/DataGridBundle/Resources/translations/messages.en.yml b/src/Oro/Bundle/DataGridBundle/Resources/translations/messages.en.yml index ef7d2c28631..e8c016fe3b2 100644 --- a/src/Oro/Bundle/DataGridBundle/Resources/translations/messages.en.yml +++ b/src/Oro/Bundle/DataGridBundle/Resources/translations/messages.en.yml @@ -32,7 +32,7 @@ oro: delete: label: Delete confirm_title: Mass Delete Confirmation - confirm_content: Are you sure you want to do delete selected items? + confirm_content: Are you sure you want to delete selected items? success_message: "{0} No entities were removed|{1} One entity was removed|]1,Inf[ %count% entities were removed" datagrid.page_size.all: All export.csv: CSV From e21295187c45fa52c52985f84ce331430275fb8f Mon Sep 17 00:00:00 2001 From: rodolfobandeira Date: Sat, 21 Nov 2015 19:21:46 -0500 Subject: [PATCH 041/471] Add @remove_entity on doc reference --- .../workflow-entities/transition-actions.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/Oro/Bundle/WorkflowBundle/Resources/doc/reference/workflow/workflow-entities/transition-actions.md b/src/Oro/Bundle/WorkflowBundle/Resources/doc/reference/workflow/workflow-entities/transition-actions.md index 37d8837efcd..58b00c4995b 100644 --- a/src/Oro/Bundle/WorkflowBundle/Resources/doc/reference/workflow/workflow-entities/transition-actions.md +++ b/src/Oro/Bundle/WorkflowBundle/Resources/doc/reference/workflow/workflow-entities/transition-actions.md @@ -10,6 +10,7 @@ Table of Contents - [Unset Value](#unset-value) - [Create Object](#create-object) - [Create Entity](#create-entity) + - [Remove Entity](#remove-entity) - [Create Related Entity](#create-related-entity) - [Find Entity](#find-entity) - [Format Name](#format-name) @@ -231,6 +232,24 @@ OR ``` +Remove Entity +------------- + +**Class:** Oro\Bundle\WorkflowBundle\Model\Action\RemoveEntity + +**Alias:** remove_entity + +**Description:** Removes entity with specified class instance. + +**Parameters:** + - target - target that will contain entity instance; + +**Configuration Example** +``` +- @remove_entity: + target: $.data #remove the entity being processed +``` + Create Related Entity --------------------- From 7126889e650a57360210866ab2b4f9b8c5a1c8fa Mon Sep 17 00:00:00 2001 From: Ivan Shakuta Date: Mon, 23 Nov 2015 18:31:43 +0200 Subject: [PATCH 042/471] Add helper methods to grid configuration provide a way to manipulate grid configuration without knowing exact inner array structure - add/remove column, filter, sorter - add mass action - update label - join table - add enum filter - add twig column --- .../Datagrid/Common/DatagridConfiguration.php | 251 ++++++++++++++++++ .../Common/DatagridConfigurationTest.php | 207 +++++++++++++++ 2 files changed, 458 insertions(+) create mode 100644 src/Oro/Bundle/DataGridBundle/Tests/Unit/Datagrid/Common/DatagridConfigurationTest.php diff --git a/src/Oro/Bundle/DataGridBundle/Datagrid/Common/DatagridConfiguration.php b/src/Oro/Bundle/DataGridBundle/Datagrid/Common/DatagridConfiguration.php index 261ceb16a7d..2ce3314d429 100644 --- a/src/Oro/Bundle/DataGridBundle/Datagrid/Common/DatagridConfiguration.php +++ b/src/Oro/Bundle/DataGridBundle/Datagrid/Common/DatagridConfiguration.php @@ -2,8 +2,259 @@ namespace Oro\Bundle\DataGridBundle\Datagrid\Common; +use Doctrine\ORM\EntityRepository; + use Oro\Bundle\DataGridBundle\Common\Object; +use Oro\Bundle\EntityExtendBundle\Tools\ExtendHelper; class DatagridConfiguration extends Object { + const COLUMN_PATH = '[columns][%s]'; + const SORTER_PATH = '[sorters][columns][%s]'; + const FILTER_PATH = '[filters][columns][%s]'; + + /** + * @param string $name + * @param string $label + * + * @return DatagridConfiguration + */ + public function updateLabel($name, $label) + { + if (empty($name)) { + throw new \BadMethodCallException('DatagridConfiguration::updateLabel: name should not be empty'); + } + + $this->offsetSetByPath(sprintf(self::COLUMN_PATH.'[label]', $name), $label); + + return $this; + } + + /** + * @param string $name column name + * @param array $definition definition array as in datagrid.yml + * @param null|string $select select part for the column + * @param array $sorter sorter definition + * @param array $filter filter definition + * + * @return DatagridConfiguration + */ + public function addColumn($name, array $definition, $select = null, array $sorter = [], array $filter = []) + { + if (empty($name)) { + throw new \BadMethodCallException('DatagridConfiguration::addColumn: name should not be empty'); + } + + $this->offsetSetByPath( + sprintf(self::COLUMN_PATH, $name), + $definition + ); + + if (!is_null($select)) { + $this->addSelect($select); + } + + if (!empty($sorter)) { + $this->addSorter($name, $sorter); + } + + if (!empty($filter)) { + $this->addFilter($name, $filter); + } + + return $this; + } + + /** + * @param string $select + * + * @return DatagridConfiguration + */ + public function addSelect($select) + { + if (empty($select)) { + throw new \BadMethodCallException('DatagridConfiguration::addSelect: select should not be empty'); + } + + $this->offsetAddToArrayByPath( + '[source][query][select]', + [$select] + ); + + return $this; + } + + /** + * @param string $type + * @param array $definition + * + * @return DatagridConfiguration + */ + public function joinTable($type, array $definition) + { + $this + ->offsetAddToArrayByPath( + sprintf('[source][query][join][%s]', $type), + [$definition] + ); + + return $this; + } + + /** + * @param string $name + * @param array $definition + * + * @return DatagridConfiguration + */ + public function addFilter($name, array $definition) + { + $this->offsetSetByPath( + sprintf(self::FILTER_PATH, $name), + $definition + ); + + return $this; + } + + /** + * @param string $name + * @param array $definition + * + * @return DatagridConfiguration + */ + public function addSorter($name, array $definition) + { + $this->offsetSetByPath( + sprintf(self::SORTER_PATH, $name), + $definition + ); + + return $this; + } + + /** + * Remove column definition + * should remove sorters as well and optionally filters + * + * @param string $name column name from grid definition + * @param bool $removeFilter whether remove filter or not, true by default + * + * @return DatagridConfiguration + */ + public function removeColumn($name, $removeFilter = true) + { + $this->offsetUnsetByPath( + sprintf(self::COLUMN_PATH, $name) + ); + + $this->removeSorter($name); + if ($removeFilter) { + $this->removeFilter($name); + } + + return $this; + } + + /** + * @param string $name column name + */ + public function removeSorter($name) + { + $this->offsetUnsetByPath( + sprintf(self::SORTER_PATH, $name) + ); + } + + /** + * Remove filter definition + * + * @param string $name column name + * + * @return DatagridConfiguration + */ + public function removeFilter($name) + { + $this->offsetUnsetByPath( + sprintf(self::FILTER_PATH, $name) + ); + + return $this; + } + + /** + * @param string $columnName column name + * @param string $dataName property path of the field, e.g. entity.enum_field + * @param string $enumCode enum code + * @param bool $isMultiple allow to filter by several values + * + * @return DatagridConfiguration + */ + public function addEnumFilter($columnName, $dataName, $enumCode, $isMultiple = false) + { + $this->addFilter( + $columnName, + [ + 'type' => 'entity', + 'data_name' => $dataName, + 'options' => [ + 'field_options' => [ + 'class' => ExtendHelper::buildEnumValueClassName($enumCode), + 'property' => 'name', + 'query_builder' => function (EntityRepository $entityRepository) { + return $entityRepository->createQueryBuilder('c') + ->orderBy('c.name', 'ASC'); + }, + 'multiple' => $isMultiple, + ], + ], + ] + ); + + return $this; + } + + /** + * @param string $name + * @param string $label + * @param string $templatePath + * @param null|string $select select part for the column + * @param array $sorter sorter definition + * @param array $filter filter definitio + * + * @return DatagridConfiguration + */ + public function addTwigColumn($name, $label, $templatePath, $select = null, array $sorter = [], array $filter = []) + { + $this->addColumn( + $name, + [ + 'label' => $label, + 'type' => 'twig', + 'frontend_type' => 'html', + 'template' => $templatePath, + ], + $select, + $sorter, + $filter + ); + + return $this; + } + + /** + * @param string $name + * @param array $options + * + * @return DatagridConfiguration + */ + public function addMassAction($name, array $options) + { + $this->offsetSetByPath( + sprintf('[mass_actions][%s]', $name), + $options + ); + + return $this; + } } diff --git a/src/Oro/Bundle/DataGridBundle/Tests/Unit/Datagrid/Common/DatagridConfigurationTest.php b/src/Oro/Bundle/DataGridBundle/Tests/Unit/Datagrid/Common/DatagridConfigurationTest.php new file mode 100644 index 00000000000..5c253b6ae2c --- /dev/null +++ b/src/Oro/Bundle/DataGridBundle/Tests/Unit/Datagrid/Common/DatagridConfigurationTest.php @@ -0,0 +1,207 @@ +configuration = DatagridConfiguration::create([]); + } + + /** + * @dataProvider addColumnDataProvider + * + * @param array $expected + * @param string $name + * @param string $select + * @param array $definition + * @param array $sorter + * @param array $filter + */ + public function testAddColumn( + $expected, + $name, + $definition, + $select = null, + $sorter = [], + $filter = [] + ) { + $this->configuration->addColumn( + $name, + $definition, + $select, + $sorter, + $filter + ); + + $configArray = $this->configuration->toArray(); + $this->assertEquals($expected, $configArray); + } + + public function testExceptions() + { + $this->setExpectedException( + 'BadMethodCallException', + 'DatagridConfiguration::addColumn: name should not be empty' + ); + $this->configuration->addColumn(null, []); + + $this->setExpectedException( + 'BadMethodCallException', + 'DatagridConfiguration::updateLabel: name should not be empty' + ); + $this->configuration->updateLabel(null, []); + + $this->setExpectedException( + 'BadMethodCallException', + 'DatagridConfiguration::addSelect: select should not be empty' + ); + $this->configuration->addSelect(null); + } + + public function testUpdateLabel() + { + $this->configuration->updateLabel('testColumn', 'label1'); + + $configArray = $this->configuration->toArray(); + $this->assertEquals( + ['columns' => ['testColumn' => ['label' => 'label1']]], + $configArray + ); + + $this->configuration->updateLabel('testColumn1', null); + $configArray = $this->configuration->toArray(); + $this->assertEquals( + [ + 'columns' => [ + 'testColumn' => ['label' => 'label1'], + 'testColumn1' => ['label' => null], + ] + ], + $configArray + ); + + $this->configuration->updateLabel('testColumn', 'label2'); + $configArray = $this->configuration->toArray(); + $this->assertEquals( + [ + 'columns' => [ + 'testColumn' => ['label' => 'label2'], + 'testColumn1' => ['label' => null], + ] + ], + $configArray + ); + } + + public function testAddSelect() + { + $this->configuration->addSelect('testColumn'); + + $configArray = $this->configuration->toArray(); + $this->assertEquals( + [ + 'source' => [ + 'query' => ['select' => ['testColumn']], + ] + ], + $configArray + ); + } + + public function testJoinTable() + { + $this->configuration->joinTable('left', ['param' => 'value']); + + $configArray = $this->configuration->toArray(); + $this->assertEquals( + [ + 'source' => [ + 'query' => ['join' => ['left' => [['param' => 'value']]]], + ] + ], + $configArray + ); + } + + public function testRemoveColumn() + { + $this->configuration->addColumn('testColumn', ['param' => 123], null, ['param' => 123], ['param' => 123]); + + $configArray = $this->configuration->toArray(); + $this->assertTrue(isset($configArray['columns']['testColumn'])); + + $this->configuration->removeColumn('testColumn'); + $configArray = $this->configuration->toArray(); + + $this->assertEmpty($configArray['columns']); + $this->assertEmpty($configArray['sorters']['columns']); + $this->assertEmpty($configArray['filters']['columns']); + } + + /** + * @return array + */ + public function addColumnDataProvider() + { + return [ + 'all data supplied' => [ + 'expected' => [ + 'source' => [ + 'query' => ['select' => ['entity.testColumn1',]], + ], + 'columns' => ['testColumn1' => ['testParam1' => 'abc', 'testParam2' => 123,]], + 'sorters' => ['columns' => ['testColumn1' => ['data_name' => 'testColumn1']]], + 'filters' => ['columns' => ['testColumn1' => ['data_name' => 'testColumn1', 'type' => 'string']]], + ], + 'name' => 'testColumn1', + 'definition' => ['testParam1' => 'abc', 'testParam2' => 123,], + 'select' => 'entity.testColumn1', + 'sorter' => ['data_name' => 'testColumn1'], + 'filter' => ['data_name' => 'testColumn1', 'type' => 'string'], + ], + 'without sorter and filter' => [ + 'expected' => [ + 'source' => [ + 'query' => ['select' => ['entity.testColumn2',]], + ], + 'columns' => ['testColumn2' => ['testParam1' => 'abc', 'testParam2' => 123,]], + ], + 'name' => 'testColumn2', + 'definition' => ['testParam1' => 'abc', 'testParam2' => 123,], + 'select' => 'entity.testColumn2', + ], + 'without select part' => [ + 'expected' => [ + 'columns' => ['testColumn2' => ['testParam1' => 'abc', 'testParam2' => 123,]], + ], + 'name' => 'testColumn2', + 'definition' => ['testParam1' => 'abc', 'testParam2' => 123,], + ], + 'without sorter and select' => [ + 'expected' => [ + 'columns' => ['testColumn1' => ['testParam1' => 'abc', 'testParam2' => 123,]], + 'filters' => ['columns' => ['testColumn1' => ['data_name' => 'testColumn1', 'type' => 'string']]], + ], + 'name' => 'testColumn1', + 'definition' => ['testParam1' => 'abc', 'testParam2' => 123,], + 'select' => null, + 'sorter' => [], + 'filter' => ['data_name' => 'testColumn1', 'type' => 'string'], + ], + 'with empty definition' => [ + 'expected' => [ + 'columns' => ['testColumn1' => []], + ], + 'name' => 'testColumn1', + 'definition' => [], + ], + ]; + } +} From 9a82d98bfe1f7b51bd22b737d195b0f4a2e62a04 Mon Sep 17 00:00:00 2001 From: Ivan Shakuta Date: Mon, 23 Nov 2015 21:03:45 +0200 Subject: [PATCH 043/471] Fix typo - fix typo - remove unnecessary use statement --- .../EntityExtendBundle/Form/Type/DictionaryFilterType.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Oro/Bundle/EntityExtendBundle/Form/Type/DictionaryFilterType.php b/src/Oro/Bundle/EntityExtendBundle/Form/Type/DictionaryFilterType.php index fe73b686f96..fb8057e8786 100644 --- a/src/Oro/Bundle/EntityExtendBundle/Form/Type/DictionaryFilterType.php +++ b/src/Oro/Bundle/EntityExtendBundle/Form/Type/DictionaryFilterType.php @@ -10,7 +10,6 @@ use Oro\Bundle\EntityExtendBundle\Provider\EnumValueProvider; use Oro\Bundle\EntityExtendBundle\Entity\AbstractEnumValue; use Oro\Bundle\EntityExtendBundle\Tools\ExtendHelper; -use Oro\Bundle\EntityExtendBundle\Form\Type\AbstractMultiChoiceType; use Oro\Bundle\FilterBundle\Form\Type\Filter\ChoiceFilterType; @@ -59,7 +58,7 @@ public function setDefaultOptions(OptionsResolverInterface $resolver) if (empty($options['dictionary_code'])) { throw new InvalidOptionsException( - 'Either "class" or "dictionary_code" must option must be set.' + 'Either "class" or "dictionary_code" option must be set.' ); } From a08fd47e6930455016293fb8ab14ca11f784853c Mon Sep 17 00:00:00 2001 From: Oleksandr Nechyporenko Date: Tue, 24 Nov 2015 12:00:42 +0200 Subject: [PATCH 044/471] BB-1582: Move theme info file to theme folder - add assets support in theme data --- .../LayoutBundle/Assetic/LayoutResource.php | 82 +++++++------------ .../DependencyInjection/Configuration.php | 44 +++++++++- .../Layout/Block/Type/StylesheetsType.php | 75 ----------------- .../Resources/config/block_types.yml | 5 -- .../views/Layout/div_layout.html.twig | 5 +- 5 files changed, 72 insertions(+), 139 deletions(-) delete mode 100644 src/Oro/Bundle/LayoutBundle/Layout/Block/Type/StylesheetsType.php diff --git a/src/Oro/Bundle/LayoutBundle/Assetic/LayoutResource.php b/src/Oro/Bundle/LayoutBundle/Assetic/LayoutResource.php index fe55c711300..cbd76c389e1 100644 --- a/src/Oro/Bundle/LayoutBundle/Assetic/LayoutResource.php +++ b/src/Oro/Bundle/LayoutBundle/Assetic/LayoutResource.php @@ -4,90 +4,70 @@ use Assetic\Factory\Resource\ResourceInterface; -use Oro\Bundle\LayoutBundle\Layout\Block\Type\StylesheetsType; -use Oro\Component\Layout\Layout; -use Oro\Component\Layout\BlockView; -use Oro\Component\Layout\LayoutManager; -use Oro\Component\Layout\LayoutContext; +use Oro\Component\Layout\Extension\Theme\Model\Theme; use Oro\Component\Layout\Extension\Theme\Model\ThemeManager; -use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; class LayoutResource implements ResourceInterface { /** @var ThemeManager */ protected $themeManager; - /** @var LayoutManager $layoutManager */ - protected $layoutManager; - - public function __construct(ThemeManager $themeManager, LayoutManager $layoutManager) + /** + * @param ThemeManager $themeManager + */ + public function __construct(ThemeManager $themeManager) { $this->themeManager = $themeManager; - $this->layoutManager = $layoutManager; } + /** + * @inheritdoc + */ public function isFresh($timestamp) { return true; } + /** + * @inheritdoc + */ + public function __toString() + { + return 'layout'; + } + + /** + * @inheritdoc + */ public function getContent() { $formulae = []; - $themes = $this->themeManager->getThemeNames(); + $themes = $this->themeManager->getAllThemes(); foreach ($themes as $theme) { - $layout = $this->getLayout($theme); - if ($layout) { - $formulae += $this->collectViewStylesheets($theme, $layout->getView()); - } + $formulae += $this->collectThemeAssets($theme); } return $formulae; } /** - * @param string $theme - * @return Layout + * @param Theme $theme + * @return array */ - protected function getLayout($theme) + protected function collectThemeAssets(Theme $theme) { - $builder = $this->layoutManager->getLayoutBuilder(); - $builder->add('root', null, 'root'); - - $context = new LayoutContext(); - $context->set('theme', $theme); - - try { - $layout = $builder->getLayout($context); - } catch (NoSuchPropertyException $ex) { - $ex; - $layout = null; - } - - return $layout; - } - - protected function collectViewStylesheets($theme, BlockView $view, $formulae = []) - { - if ($view->vars['block_type'] === StylesheetsType::NAME) { - $name = 'layout_' . $theme . $view->vars['cache_key']; + $formulae = []; + $assets = $theme->getDataByKey('assets', []); + foreach ($assets as $assetKey => $asset) { + $name = 'layout_' . $theme->getName(). '_' . $assetKey; $formulae[$name] = [ - $view->vars['inputs'], - $view->vars['filters'], + $asset['inputs'], + $asset['filters'], [ - 'output' => $view->vars['output'], + 'output' => $asset['output'], 'name' => $name, ], ]; - } else { - foreach ($view as $childView) { - $formulae = $this->collectViewStylesheets($theme, $childView, $formulae); - } } return $formulae; } - - public function __toString() - { - return 'layout'; - } } diff --git a/src/Oro/Bundle/LayoutBundle/DependencyInjection/Configuration.php b/src/Oro/Bundle/LayoutBundle/DependencyInjection/Configuration.php index c3ada1db73b..bff642eb6b5 100644 --- a/src/Oro/Bundle/LayoutBundle/DependencyInjection/Configuration.php +++ b/src/Oro/Bundle/LayoutBundle/DependencyInjection/Configuration.php @@ -112,6 +112,10 @@ protected function appendThemingNodes(ArrayNodeDefinition $parentNode) $treeBuilder = new TreeBuilder(); $node = $treeBuilder->root('themes'); + $dataTreeBuilder = new TreeBuilder(); + $dataNode = $dataTreeBuilder->root('data'); + $dataNode->info('Layout additional data')->end(); + $node ->useAttributeAsKey('theme-identifier') ->normalizeKeys(false) @@ -145,13 +149,12 @@ protected function appendThemingNodes(ArrayNodeDefinition $parentNode) ->prototype('scalar')->end() ->cannotBeEmpty() ->end() - ->arrayNode('data') - ->prototype('array') - ->info('Layout additional data') - ->end() + ->append($dataNode) ->end() ->end(); + $this->appendDataNodes($dataNode); + $parentNode ->append($node) ->children() @@ -164,4 +167,37 @@ protected function appendThemingNodes(ArrayNodeDefinition $parentNode) ->end() ->end(); } + + /** + * @param ArrayNodeDefinition $dataNode + */ + protected function appendDataNodes($dataNode) + { + $treeBuilder = new TreeBuilder(); + $assetsNode = $treeBuilder->root('assets'); + + $assetsNode + ->useAttributeAsKey('asset-identifier') + ->normalizeKeys(false) + ->prototype('array') + ->children() + ->arrayNode('inputs') + ->info('Input assets list') + ->prototype('scalar')->end() + ->isRequired() + ->end() + ->arrayNode('filters') + ->info('Filters to manipulate input assets') + ->prototype('scalar')->end() + ->isRequired() + ->end() + ->scalarNode('output') + ->info('Output asset') + ->isRequired() + ->end() + ->end() + ->end(); + + $dataNode->append($assetsNode); + } } diff --git a/src/Oro/Bundle/LayoutBundle/Layout/Block/Type/StylesheetsType.php b/src/Oro/Bundle/LayoutBundle/Layout/Block/Type/StylesheetsType.php deleted file mode 100644 index 109b669b399..00000000000 --- a/src/Oro/Bundle/LayoutBundle/Layout/Block/Type/StylesheetsType.php +++ /dev/null @@ -1,75 +0,0 @@ -setRequired( - [ - 'inputs' - ] - ); - $resolver->setDefaults( - [ - 'filters' => [ - 'cssrewrite', - 'lessphp', - 'cssmin', - ], - 'output' => null, - ] - ); - - $resolver->setAllowedTypes('inputs', ['array', 'Oro\Component\Layout\OptionValueBag']); - $resolver->setAllowedTypes('filters', ['array', 'Oro\Component\Layout\OptionValueBag']); - $resolver->setAllowedTypes('output', ['null', 'string']); - } - - /** - * {@inheritdoc} - */ - public function finishView(BlockView $view, BlockInterface $block, array $options) - { - $theme = $block->getContext()->get('theme'); - - $inputs = $block->getOptions()['inputs']; - if ($inputs instanceof OptionValueBag) { - $inputs = $inputs->buildValue(new ArrayOptionValueBuilder()); - } - $view->vars['inputs'] = $inputs; - - $filters = $block->getOptions()['filters']; - if ($filters instanceof OptionValueBag) { - $filters = $filters->buildValue(new ArrayOptionValueBuilder()); - } - $view->vars['filters'] = $filters; - - $view->vars['output'] = $block->getOptions()['output']; - if (!$view->vars['output']) { - $view->vars['output'] = 'css/layout/'.$theme.'/'.$view->vars['cache_key'].'.css'; - } - } - - /** - * {@inheritdoc} - */ - public function getName() - { - return self::NAME; - } -} diff --git a/src/Oro/Bundle/LayoutBundle/Resources/config/block_types.yml b/src/Oro/Bundle/LayoutBundle/Resources/config/block_types.yml index cfe5fc0a04a..17a23227d80 100644 --- a/src/Oro/Bundle/LayoutBundle/Resources/config/block_types.yml +++ b/src/Oro/Bundle/LayoutBundle/Resources/config/block_types.yml @@ -29,11 +29,6 @@ services: tags: - { name: layout.block_type, alias: script } - oro_layout.block_type.stylesheets: - class: Oro\Bundle\LayoutBundle\Layout\Block\Type\StylesheetsType - tags: - - { name: layout.block_type, alias: stylesheets } - oro_layout.block_type.style: class: Oro\Bundle\LayoutBundle\Layout\Block\Type\StyleType tags: diff --git a/src/Oro/Bundle/LayoutBundle/Resources/views/Layout/div_layout.html.twig b/src/Oro/Bundle/LayoutBundle/Resources/views/Layout/div_layout.html.twig index eafb9e0daac..02c1d538678 100644 --- a/src/Oro/Bundle/LayoutBundle/Resources/views/Layout/div_layout.html.twig +++ b/src/Oro/Bundle/LayoutBundle/Resources/views/Layout/div_layout.html.twig @@ -57,12 +57,9 @@ {% endif %} {% endblock %} -{% block stylesheets_widget %} - -{% endblock %} - {% block style_widget %} {% if attr.href is defined and attr.href is not empty %} + {% set attr = attr|merge({href: asset(attr.href)}) %} {% else %} From fc3b20997cfc2b64a7e15d534d52e53e11dca866 Mon Sep 17 00:00:00 2001 From: Oleksandr Nechyporenko Date: Tue, 24 Nov 2015 13:27:11 +0200 Subject: [PATCH 045/471] BB-1582: Move theme info file to theme folder - fix tests --- .../Unit/Assetic/LayoutFormulaLoaderTest.php | 30 +++++ .../Tests/Unit/Assetic/LayoutResourceTest.php | 110 ++++++++++++++++++ .../Unit/ArrayOptionValueBuilderTest.php | 97 +++++++++++++++ .../Unit/DeferredLayoutManipulatorTest.php | 2 +- 4 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 src/Oro/Bundle/LayoutBundle/Tests/Unit/Assetic/LayoutFormulaLoaderTest.php create mode 100644 src/Oro/Bundle/LayoutBundle/Tests/Unit/Assetic/LayoutResourceTest.php create mode 100644 src/Oro/Component/Layout/Tests/Unit/ArrayOptionValueBuilderTest.php diff --git a/src/Oro/Bundle/LayoutBundle/Tests/Unit/Assetic/LayoutFormulaLoaderTest.php b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Assetic/LayoutFormulaLoaderTest.php new file mode 100644 index 00000000000..5c9b874b99f --- /dev/null +++ b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Assetic/LayoutFormulaLoaderTest.php @@ -0,0 +1,30 @@ +getMock('Assetic\Factory\Resource\ResourceInterface'); + + /** @var ResourceInterface|\PHPUnit_Framework_MockObject_MockObject $layoutResource */ + $layoutResource = $this->getMockBuilder('Oro\Bundle\LayoutBundle\Assetic\LayoutResource') + ->disableOriginalConstructor() + ->getMock(); + $layoutResource->expects($this->once()) + ->method('getContent') + ->willReturn($formulae); + + $this->assertEquals([], $loader->load($resource)); + $this->assertEquals($formulae, $loader->load($layoutResource)); + } +} diff --git a/src/Oro/Bundle/LayoutBundle/Tests/Unit/Assetic/LayoutResourceTest.php b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Assetic/LayoutResourceTest.php new file mode 100644 index 00000000000..a708405c068 --- /dev/null +++ b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Assetic/LayoutResourceTest.php @@ -0,0 +1,110 @@ +layoutResource = new LayoutResource($this->getThemeManager()); + } + + protected function tearDown() + { + unset($this->layoutResource, $this->themeManager); + } + + /** + * @return ThemeManager + */ + protected function getThemeManager() + { + if (!$this->themeManager) { + $this->themeManager = new ThemeManager(new ThemeFactory(), $this->getThemes()); + } + return $this->themeManager; + } + + /** + * @return array + */ + protected function getThemes() + { + $asset = [ + 'inputs' => ['styles.css'], + 'filters' => ['filters'], + 'output' => ['output.css'], + ]; + + return [ + 'without_assets' => [], + 'with_empty_assets' => [ + 'data' => ['assets' => []], + ], + 'with_one_asset' => [ + 'data' => [ + 'assets' => [ + 'first' => $asset, + ] + ], + ], + 'with_two_asset' => [ + 'data' => [ + 'assets' => [ + 'first' => $asset, + 'second' => $asset, + ] + ], + ], + ]; + } + + public function testIsFresh() + { + $this->assertTrue($this->layoutResource->isFresh(1)); + } + + public function testToString() + { + $this->assertEquals('layout', (string)$this->layoutResource); + } + + public function testGetContent() + { + $themes = $this->getThemes(); + $formulae = []; + foreach ($themes as $themeName => $theme) { + if (!isset($theme['data']) || !isset($theme['data']['assets']) || empty($theme['data']['assets'])) { + continue; + } + + $assets = $theme['data']['assets']; + foreach ($assets as $assetKey => $asset) { + $name = 'layout_' . $themeName . '_' . $assetKey; + $formulae[$name] = [ + $asset['inputs'], + $asset['filters'], + [ + 'output' => $asset['output'], + 'name' => $name, + ], + ]; + } + } + + $this->assertArrayHasKey('layout_with_one_asset_first', $formulae); + $this->assertArrayHasKey('layout_with_two_asset_first', $formulae); + $this->assertArrayHasKey('layout_with_two_asset_second', $formulae); + $this->assertEquals($formulae, $this->layoutResource->getContent()); + } +} diff --git a/src/Oro/Component/Layout/Tests/Unit/ArrayOptionValueBuilderTest.php b/src/Oro/Component/Layout/Tests/Unit/ArrayOptionValueBuilderTest.php new file mode 100644 index 00000000000..213f7381c9f --- /dev/null +++ b/src/Oro/Component/Layout/Tests/Unit/ArrayOptionValueBuilderTest.php @@ -0,0 +1,97 @@ +add(['val1']); + $builder->add(['val2']); + $builder->add(['val3', 'val4']); + $builder->remove(['val2']); + $builder->replace(['val3'], ['replaced_val3']); + $this->assertEquals(['val1', 'replaced_val3', 'val4'], $builder->get()); + } + + /** + * @expectedException \Oro\Component\Layout\Exception\UnexpectedTypeException + */ + public function testAddThrowsExceptionIfInvalidValue() + { + $builder = new ArrayOptionValueBuilder(); + $builder->add(123); + } + + /** + * @expectedException \Oro\Component\Layout\Exception\UnexpectedTypeException + */ + public function testRemoveThrowsExceptionIfInvalidValue() + { + $builder = new ArrayOptionValueBuilder(); + $builder->remove(123); + } + + /** + * @expectedException \Oro\Component\Layout\Exception\UnexpectedTypeException + */ + public function testReplaceThrowsExceptionIfInvalidOldValue() + { + $builder = new ArrayOptionValueBuilder(); + $builder->replace(123, ['new']); + } + + /** + * @expectedException \Oro\Component\Layout\Exception\UnexpectedTypeException + */ + public function testReplaceThrowsExceptionIfInvalidNewValue() + { + $builder = new ArrayOptionValueBuilder(); + $builder->replace(['old'], 123); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testReplaceThrowsExceptionIfCountNotEqual() + { + $builder = new ArrayOptionValueBuilder(); + $builder->replace(['old'], ['new1', 'new2']); + } + + public function testAddWithEmptyValue() + { + $builder = new ArrayOptionValueBuilder(); + $builder->add(['']); + $this->assertEquals([''], $builder->get()); + } + + public function testRemoveWithEmptyValue() + { + $builder = new ArrayOptionValueBuilder(); + $builder->add(['val1']); + $builder->remove(['']); + $this->assertEquals(['val1'], $builder->get()); + } + + public function testReplaceWithEmptyOldValue() + { + $builder = new ArrayOptionValueBuilder(); + $builder->add(['val1']); + $builder->replace([''], ['new']); + $this->assertEquals(['val1'], $builder->get()); + } + + public function testReplaceWithEmptyNewValue() + { + $builder = new ArrayOptionValueBuilder(); + $builder->add(['val1']); + $builder->add(['val2']); + $builder->replace(['val1'], ['']); + $this->assertEquals(['', 'val2'], $builder->get()); + } +} diff --git a/src/Oro/Component/Layout/Tests/Unit/DeferredLayoutManipulatorTest.php b/src/Oro/Component/Layout/Tests/Unit/DeferredLayoutManipulatorTest.php index c8fc81da1b8..5763ab705cf 100644 --- a/src/Oro/Component/Layout/Tests/Unit/DeferredLayoutManipulatorTest.php +++ b/src/Oro/Component/Layout/Tests/Unit/DeferredLayoutManipulatorTest.php @@ -1055,7 +1055,7 @@ public function testSubtractAndThenAppendOption() 'vars' => [ 'id' => 'logo', 'title' => '', - 'attr' => ['class' => 'test_class1 test_class2'] + 'attr' => ['class' => 'test_class1'] ] ] ] From 4de860748068b4c37363301d51458cf87eefb4cc Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Tue, 24 Nov 2015 12:31:50 +0100 Subject: [PATCH 046/471] CRM-4618: Impossible to define import strategy on the field configuration --- .../Resources/config/entity_config.yml | 14 +++++++++ .../Resources/translations/messages.en.yml | 1 + .../Import/AbstractImportStrategy.php | 29 +++++++++++++++++-- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/Oro/Bundle/ImportExportBundle/Resources/config/entity_config.yml b/src/Oro/Bundle/ImportExportBundle/Resources/config/entity_config.yml index 66e7c42d260..8656c776650 100644 --- a/src/Oro/Bundle/ImportExportBundle/Resources/config/entity_config.yml +++ b/src/Oro/Bundle/ImportExportBundle/Resources/config/entity_config.yml @@ -45,6 +45,20 @@ oro_entity_config: empty_value: false required: false block: import_export + identity_when_empty: + options: + value_type: boolean + allowed_type: [string, text, integer, smallint, bigint, boolean, date, float, decimal, money, percent, enum] + form: + type: choice + options: + label: oro.importexport.entity_config.identity_when_empty.label + choices: + true: 'Yes' + false: 'No' + empty_value: false + required: false + block: import_export # is field must be excluded from import/export excluded: options: diff --git a/src/Oro/Bundle/ImportExportBundle/Resources/translations/messages.en.yml b/src/Oro/Bundle/ImportExportBundle/Resources/translations/messages.en.yml index 81655cad9da..9981accd3dc 100644 --- a/src/Oro/Bundle/ImportExportBundle/Resources/translations/messages.en.yml +++ b/src/Oro/Bundle/ImportExportBundle/Resources/translations/messages.en.yml @@ -16,6 +16,7 @@ oro: header.label: "Column Name" header.tooltip: "If blank Label will be used" identity.label: "Use as Identity Field" + identity_when_empty.label: "Use as Identity Field even if empty" order.label: "Column Position" import: diff --git a/src/Oro/Bundle/ImportExportBundle/Strategy/Import/AbstractImportStrategy.php b/src/Oro/Bundle/ImportExportBundle/Strategy/Import/AbstractImportStrategy.php index c30b8e0bcce..3dd2d61988d 100644 --- a/src/Oro/Bundle/ImportExportBundle/Strategy/Import/AbstractImportStrategy.php +++ b/src/Oro/Bundle/ImportExportBundle/Strategy/Import/AbstractImportStrategy.php @@ -124,9 +124,7 @@ protected function findExistingEntity($entity, array $searchContext = []) if (!$existingEntity && (!$searchContext || $this->databaseHelper->getIdentifier(current($searchContext))) ) { - $identityValues = $searchContext; - $identityValues += $this->fieldHelper->getIdentityValues($entity); - $existingEntity = $this->findEntityByIdentityValues($entityName, $identityValues); + $existingEntity = $this->findExistingEntityByIdentityFields($entity, $searchContext); } if ($existingEntity && !$identifier) { @@ -138,6 +136,31 @@ protected function findExistingEntity($entity, array $searchContext = []) return $existingEntity; } + /** + * @param object $entity + * @param array $searchContext + * + * @return object|null + */ + protected function findExistingEntityByIdentityFields($entity, array $searchContext = []) + { + $entityName = ClassUtils::getClass($entity); + + $identityValues = $searchContext; + $identityValues += $this->fieldHelper->getIdentityValues($entity); + foreach ($identityValues as $fieldName => $value) { + if ($value !== null || + $this->fieldHelper->getConfigValue($entityName, $fieldName, 'identity_when_empty', true) + ) { + continue; + } + + unset($identityValues[$fieldName]); + } + + return $this->findEntityByIdentityValues($entityName, $identityValues); + } + /** * Try to find entity by identity fields if at least one is specified * From 20a8ef211257f5accc0fbc74f6057cc211f89f7e Mon Sep 17 00:00:00 2001 From: Oleksandr Nechyporenko Date: Tue, 24 Nov 2015 14:57:50 +0200 Subject: [PATCH 047/471] BB-1582: Move theme info file to theme folder - fix tests --- .../Resources/views/Layout/php/style_widget.html.php | 1 + src/Oro/Bundle/LayoutBundle/Tests/Functional/RendererTest.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Oro/Bundle/LayoutBundle/Resources/views/Layout/php/style_widget.html.php b/src/Oro/Bundle/LayoutBundle/Resources/views/Layout/php/style_widget.html.php index 618a97a77d1..5fd9ff82bc0 100644 --- a/src/Oro/Bundle/LayoutBundle/Resources/views/Layout/php/style_widget.html.php +++ b/src/Oro/Bundle/LayoutBundle/Resources/views/Layout/php/style_widget.html.php @@ -1,5 +1,6 @@ + getUrl($attr['href']); ?> block($block, 'block_attributes', array('attr' => $attr)) ?>/> - + From 9337c8a44907699f3a20a4c8ec5e220d5049e11d Mon Sep 17 00:00:00 2001 From: Mykhailo Kudelia Date: Fri, 27 Nov 2015 14:00:41 +0200 Subject: [PATCH 048/471] BAP-9418: Reports field filter with source or quarter type unexpected behaviour --- .../FilterBundle/Form/EventListener/DateFilterSubscriber.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Oro/Bundle/FilterBundle/Form/EventListener/DateFilterSubscriber.php b/src/Oro/Bundle/FilterBundle/Form/EventListener/DateFilterSubscriber.php index 94ba5a0c051..3822f505694 100644 --- a/src/Oro/Bundle/FilterBundle/Form/EventListener/DateFilterSubscriber.php +++ b/src/Oro/Bundle/FilterBundle/Form/EventListener/DateFilterSubscriber.php @@ -119,9 +119,7 @@ function ($data) { $this->mapValues($children, $data, $this->getDatePartAccessorClosure('Y')); $this->replaceValueFields($form->get('value'), array_flip(range(date('Y') - 50, date('Y') + 50))); break; - // in case we do not need any value to be mapped(converted), in other words - use as is case DateModifierInterface::PART_SOURCE: - return; case DateModifierInterface::PART_VALUE: default: $this->mapValues( From 3628d7e8c4d6ad13a85258f760c8f7b186ca6680 Mon Sep 17 00:00:00 2001 From: Oleksandr Nechyporenko Date: Fri, 27 Nov 2015 15:12:48 +0200 Subject: [PATCH 049/471] BB-1535: Create login page based on new layout - add html block type --- .../Layout/Block/Type/HtmlType.php | 26 +++++++++++++++++++ .../Resources/config/block_types.yml | 5 ++++ .../Resources/doc/what_is_layout.md | 1 + .../views/Layout/div_layout.html.twig | 4 +++ .../views/Layout/php/html_widget.html.php | 2 ++ .../Tests/Functional/RendererTest.php | 2 ++ .../Tests/Unit/BlockTypeTestCase.php | 3 ++- .../Unit/Layout/Block/Type/HtmlTypeTest.php | 24 +++++++++++++++++ 8 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 src/Oro/Bundle/LayoutBundle/Layout/Block/Type/HtmlType.php create mode 100644 src/Oro/Bundle/LayoutBundle/Resources/views/Layout/php/html_widget.html.php create mode 100644 src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Block/Type/HtmlTypeTest.php diff --git a/src/Oro/Bundle/LayoutBundle/Layout/Block/Type/HtmlType.php b/src/Oro/Bundle/LayoutBundle/Layout/Block/Type/HtmlType.php new file mode 100644 index 00000000000..37cfa544b2c --- /dev/null +++ b/src/Oro/Bundle/LayoutBundle/Layout/Block/Type/HtmlType.php @@ -0,0 +1,26 @@ +` | | `list_item` | [ListItemType.php](../../Layout/Block/Type/ListItemType.php) | `
  • `, this block type can be used if you want to control rendering of `li` tag and its attributes | | `text` | [TextType.php](../../Layout/Block/Type/TextType.php) | Text node | +| `html` | [HtmlType.php](../../Layout/Block/Type/HtmlType.php) | Html node | | `button` | [ButtonType.php](../../Layout/Block/Type/ButtonType.php) | ` +
    Renderer Test!
    • Hi World!
    • Hi World!
    • diff --git a/src/Oro/Bundle/LayoutBundle/Tests/Unit/BlockTypeTestCase.php b/src/Oro/Bundle/LayoutBundle/Tests/Unit/BlockTypeTestCase.php index ea17024cf6a..54ff7d76450 100644 --- a/src/Oro/Bundle/LayoutBundle/Tests/Unit/BlockTypeTestCase.php +++ b/src/Oro/Bundle/LayoutBundle/Tests/Unit/BlockTypeTestCase.php @@ -45,6 +45,7 @@ protected function initializeLayoutFactoryBuilder(LayoutFactoryBuilderInterface ->addType(new Type\ButtonGroupType()) ->addType(new Type\ListType()) ->addType(new Type\OrderedListType()) - ->addType(new Type\ListItemType()); + ->addType(new Type\ListItemType()) + ->addType(new Type\HtmlType()); } } diff --git a/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Block/Type/HtmlTypeTest.php b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Block/Type/HtmlTypeTest.php new file mode 100644 index 00000000000..692e0c52986 --- /dev/null +++ b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Block/Type/HtmlTypeTest.php @@ -0,0 +1,24 @@ +getBlockType(HtmlType::NAME); + + $this->assertSame(HtmlType::NAME, $type->getName()); + } + + public function testGetParent() + { + $type = $this->getBlockType(HtmlType::NAME); + + $this->assertSame(TextType::NAME, $type->getParent()); + } +} From 76aa9ed520720c48d518be5a2f6f5481e468bfb3 Mon Sep 17 00:00:00 2001 From: Oleksandr Nechyporenko Date: Fri, 27 Nov 2015 17:07:53 +0200 Subject: [PATCH 050/471] BB-1535: Create login page based on new layout - update base block type functionality --- .../views/Layout/div_layout.html.twig | 21 ++++++++++++++- .../Layout/php/block_attributes.html.php | 3 ++- .../views/Layout/php/block_widget.html.php | 5 +++- .../Tests/Functional/RendererTest.php | 17 +++++++++++- .../Component/Layout/Block/Type/BaseType.php | 27 ++++++++++++++++++- 5 files changed, 68 insertions(+), 5 deletions(-) diff --git a/src/Oro/Bundle/LayoutBundle/Resources/views/Layout/div_layout.html.twig b/src/Oro/Bundle/LayoutBundle/Resources/views/Layout/div_layout.html.twig index 74da72dbf74..13c27d964f9 100644 --- a/src/Oro/Bundle/LayoutBundle/Resources/views/Layout/div_layout.html.twig +++ b/src/Oro/Bundle/LayoutBundle/Resources/views/Layout/div_layout.html.twig @@ -1,4 +1,8 @@ {% block block_widget -%} + {% if tag %} + <{{ tag }}{{ block('block_attributes') }}> + + {% endif %} {%- endblock %} {% block block_label -%} @@ -6,7 +10,7 @@ {%- if label is empty -%} {% set label = id|humanize %} {%- endif -%} - {{ label|block_text(translation_domain) }} + {{ label|block_text(translation_domain) }} {%- endif %} {%- endblock %} @@ -16,9 +20,23 @@ {%- endblock %} {% block block_attributes -%} + {% if attr.class is defined %} + {% set attr = attr|merge({ + class: attr.class|replace({'{{ class_prefix }}': class_prefix}) + }) %} + {% endif %} {% for attrname, attrvalue in attr %} {% if attrname is same as('title') %}{{ attrname }}="{{ attrvalue|block_text(translation_domain) }}"{% else %}{{ attrname }}="{{ attrvalue }}"{% endif %}{% endfor %} {%- endblock %} +{% block block_label_attributes -%} + {% if label_attr.class is defined %} + {% set label_attr = label_attr|merge({ + class: label_attr.class|replace({'{{ class_prefix }}': class_prefix}) + }) %} + {% endif %} + {% for attrname, attrvalue in label_attr %} {% if attrname is same as('title') %}{{ attrname }}="{{ attrvalue|block_text(translation_domain) }}"{% else %}{{ attrname }}="{{ attrvalue }}"{% endif %}{% endfor %} +{%- endblock %} + {% block icon_block -%} {%- endblock %} @@ -49,6 +67,7 @@ {% block script_widget %} {% if attr.src is defined and attr.src is not empty %} + {% set attr = attr|merge({src: asset(attr.src)}) %} {% else %} diff --git a/src/Oro/Bundle/LayoutBundle/Resources/views/Layout/php/block_attributes.html.php b/src/Oro/Bundle/LayoutBundle/Resources/views/Layout/php/block_attributes.html.php index c7d9ca9af26..4057855695b 100644 --- a/src/Oro/Bundle/LayoutBundle/Resources/views/Layout/php/block_attributes.html.php +++ b/src/Oro/Bundle/LayoutBundle/Resources/views/Layout/php/block_attributes.html.php @@ -1,4 +1,5 @@ + $v): ?> -escape($k), $view->escape($k === 'title' ? $view['layout']->text($v, $translation_domain) : $v)) ?> + escape($k), $view->escape($k === 'title' ? $view['layout']->text($v, $translation_domain) : $v)) ?> diff --git a/src/Oro/Bundle/LayoutBundle/Resources/views/Layout/php/block_widget.html.php b/src/Oro/Bundle/LayoutBundle/Resources/views/Layout/php/block_widget.html.php index 9eaca89c8bf..9ef2b87daee 100644 --- a/src/Oro/Bundle/LayoutBundle/Resources/views/Layout/php/block_widget.html.php +++ b/src/Oro/Bundle/LayoutBundle/Resources/views/Layout/php/block_widget.html.php @@ -1,2 +1,5 @@ - + + < block($block, 'block_attributes') ?>> + > + diff --git a/src/Oro/Bundle/LayoutBundle/Tests/Functional/RendererTest.php b/src/Oro/Bundle/LayoutBundle/Tests/Functional/RendererTest.php index 09219448169..30564b64429 100644 --- a/src/Oro/Bundle/LayoutBundle/Tests/Functional/RendererTest.php +++ b/src/Oro/Bundle/LayoutBundle/Tests/Functional/RendererTest.php @@ -155,7 +155,7 @@ protected function getCoreBlocksTestLayout(ContextInterface $context) ] ) ->add('external_resource', 'head', 'external_resource', ['href' => 'test.css', 'rel' => 'stylesheet']) - ->add('content', 'root', 'body') + ->add('content', 'root', 'body', ['class_prefix' => 'content']) ->add('top_content', 'content', 'html', ['text' => '
      Renderer Test!
      ']) ->add('list', 'content', 'list') ->add( @@ -273,6 +273,20 @@ protected function getCoreBlocksTestLayout(ContextInterface $context) ['type' => 'input', 'action' => 'submit', 'name' => 'btn2', 'text' => 'Btn2'], 'button' ) + ->add( + 'block_tag', + 'content', + 'block', + [ + 'tag' => 'input', + 'attr' => [ + 'type' => 'button', + 'value' => 'Block button', + 'class' => '{{ class_prefix }}_input', + ] + ], + 'button' + ) // test manipulations of 'class' attribute ->appendOption('content', 'attr.class', ['@join' => [' ', 'class1', 'class2']]) ->replaceOption('content', 'attr.class', 'class1', ['@value' => ['$context.body_class']]) @@ -331,6 +345,7 @@ protected function getCoreBlocksTestLayoutResult($formLayout) +
      Renderer Test!
        diff --git a/src/Oro/Component/Layout/Block/Type/BaseType.php b/src/Oro/Component/Layout/Block/Type/BaseType.php index 95f5c984038..fa96e24f013 100644 --- a/src/Oro/Component/Layout/Block/Type/BaseType.php +++ b/src/Oro/Component/Layout/Block/Type/BaseType.php @@ -16,7 +16,14 @@ class BaseType extends AbstractType */ public function setDefaultOptions(OptionsResolverInterface $resolver) { - $resolver->setOptional(['vars', 'attr', 'label', 'label_attr', 'translation_domain']); + $resolver->setDefaults( + [ + 'tag' => null, + 'attr' => [], + 'label_attr' => [], + ] + ); + $resolver->setOptional(['vars', 'label', 'translation_domain', 'class_prefix']); $resolver->setAllowedTypes( [ 'vars' => 'array', @@ -39,6 +46,14 @@ public function buildView(BlockView $view, BlockInterface $block, array $options // add the view to itself vars to allow get it using 'block' variable in a rendered, for example TWIG $view->vars['block'] = $view; + $view->vars['tag'] = $options['tag']; + $view->vars['class_prefix'] = null; + if (isset($options['class_prefix'])) { + $view->vars['class_prefix'] = $options['class_prefix']; + } elseif($view->parent) { + $view->vars['class_prefix'] = $view->parent->vars['class_prefix']; + } + // replace attributes if specified ('attr' variable always exists in a view because it is added by FormView) if (isset($options['attr'])) { $view->vars['attr'] = $options['attr']; @@ -72,6 +87,16 @@ public function buildView(BlockView $view, BlockInterface $block, array $options $view->vars['cache_key'] = sprintf('_%s_%s', $id, $name); } + /** + * {@inheritdoc} + */ + public function finishView(BlockView $view, BlockInterface $block, array $options) + { + if (isset($view->vars['attr']['id']) && !isset($view->vars['label_attr']['for'])) { + $view->vars['label_attr']['for'] = $view->vars['attr']['id']; + } + } + /** * {@inheritdoc} */ From 554131a6fc00212fd31371b0bf46f0a29a23f9ee Mon Sep 17 00:00:00 2001 From: Oleksandr Nechyporenko Date: Fri, 27 Nov 2015 18:36:01 +0200 Subject: [PATCH 051/471] BB-1535: Create login page based on new layout - add input block type --- .../Layout/Block/Type/InputType.php | 65 ++++++++++++++ .../Resources/config/block_types.yml | 5 ++ .../Resources/doc/what_is_layout.md | 1 + .../views/Layout/div_layout.html.twig | 11 +++ .../Tests/Functional/RendererTest.php | 8 ++ .../Tests/Unit/BlockTypeTestCase.php | 3 +- .../Unit/Layout/Block/Type/InputTypeTest.php | 90 +++++++++++++++++++ .../Component/Layout/Block/Type/BaseType.php | 22 +++-- 8 files changed, 196 insertions(+), 9 deletions(-) create mode 100644 src/Oro/Bundle/LayoutBundle/Layout/Block/Type/InputType.php create mode 100644 src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Block/Type/InputTypeTest.php diff --git a/src/Oro/Bundle/LayoutBundle/Layout/Block/Type/InputType.php b/src/Oro/Bundle/LayoutBundle/Layout/Block/Type/InputType.php new file mode 100644 index 00000000000..e7806ca8193 --- /dev/null +++ b/src/Oro/Bundle/LayoutBundle/Layout/Block/Type/InputType.php @@ -0,0 +1,65 @@ +setDefaults( + [ + 'tag' => 'input', + 'type' => 'text', + 'id' => null, + 'name' => null, + 'value' => null, + 'placeholder' => null, + ] + ); + } + + /** + * {@inheritdoc} + */ + public function buildView(BlockView $view, BlockInterface $block, array $options) + { + $view->vars['type'] = $view->vars['attr']['type'] = $options['type']; + + if ($options['name']) { + $view->vars['attr']['name'] = $options['name']; + } + if ($options['id']) { + $view->vars['attr']['id'] = $options['id']; + } + if ($options['placeholder']) { + $view->vars['attr']['placeholder'] = $options['placeholder']; + } + if ($options['value']) { + $view->vars['attr']['value'] = $options['value']; + } + + if ($options['type'] === 'password') { + $view->vars['attr']['autocomplete'] = 'off'; + } + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return self::NAME; + } +} diff --git a/src/Oro/Bundle/LayoutBundle/Resources/config/block_types.yml b/src/Oro/Bundle/LayoutBundle/Resources/config/block_types.yml index 9af68398aa0..299c49aa3a9 100644 --- a/src/Oro/Bundle/LayoutBundle/Resources/config/block_types.yml +++ b/src/Oro/Bundle/LayoutBundle/Resources/config/block_types.yml @@ -34,6 +34,11 @@ services: tags: - { name: layout.block_type, alias: style } + oro_layout.block_type.input: + class: Oro\Bundle\LayoutBundle\Layout\Block\Type\InputType + tags: + - { name: layout.block_type, alias: input } + oro_layout.block_type.body: class: Oro\Bundle\LayoutBundle\Layout\Block\Type\BodyType tags: diff --git a/src/Oro/Bundle/LayoutBundle/Resources/doc/what_is_layout.md b/src/Oro/Bundle/LayoutBundle/Resources/doc/what_is_layout.md index d597db463e2..ca50b736d52 100644 --- a/src/Oro/Bundle/LayoutBundle/Resources/doc/what_is_layout.md +++ b/src/Oro/Bundle/LayoutBundle/Resources/doc/what_is_layout.md @@ -59,5 +59,6 @@ The **OroLayoutBundle** introduces a set of block types that allow to easily bui | `list_item` | [ListItemType.php](../../Layout/Block/Type/ListItemType.php) | `
      • `, this block type can be used if you want to control rendering of `li` tag and its attributes | | `text` | [TextType.php](../../Layout/Block/Type/TextType.php) | Text node | | `html` | [HtmlType.php](../../Layout/Block/Type/HtmlType.php) | Html node | +| `input` | [InputType.php](../../Layout/Block/Type/InputType.php) | Input node | | `button` | [ButtonType.php](../../Layout/Block/Type/ButtonType.php) | ` +
        Renderer Test!
          diff --git a/src/Oro/Bundle/LayoutBundle/Tests/Unit/BlockTypeTestCase.php b/src/Oro/Bundle/LayoutBundle/Tests/Unit/BlockTypeTestCase.php index 54ff7d76450..5c8942fcb6c 100644 --- a/src/Oro/Bundle/LayoutBundle/Tests/Unit/BlockTypeTestCase.php +++ b/src/Oro/Bundle/LayoutBundle/Tests/Unit/BlockTypeTestCase.php @@ -46,6 +46,7 @@ protected function initializeLayoutFactoryBuilder(LayoutFactoryBuilderInterface ->addType(new Type\ListType()) ->addType(new Type\OrderedListType()) ->addType(new Type\ListItemType()) - ->addType(new Type\HtmlType()); + ->addType(new Type\HtmlType()) + ->addType(new Type\InputType()); } } diff --git a/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Block/Type/InputTypeTest.php b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Block/Type/InputTypeTest.php new file mode 100644 index 00000000000..75d18b0c27f --- /dev/null +++ b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Block/Type/InputTypeTest.php @@ -0,0 +1,90 @@ +getBlockType(InputType::NAME); + + $this->assertSame(InputType::NAME, $type->getName()); + } + + public function testGetParent() + { + $type = $this->getBlockType(InputType::NAME); + + $this->assertSame(BaseType::NAME, $type->getParent()); + } + + public function testSetDefaultOptions() + { + $this->assertEquals( + [ + 'tag' => 'input', + 'type' => 'text', + 'id' => null, + 'name' => null, + 'value' => null, + 'placeholder' => null, + ], + $this->resolveOptions(InputType::NAME, []) + ); + + $options = [ + 'type' => 'password', + 'id' => 'passwordId', + 'name' => 'passwordName', + 'value' => '***', + 'placeholder' => 'Enter password', + ]; + $this->assertEquals(['tag' => 'input'] + $options, $this->resolveOptions(InputType::NAME, $options)); + } + + public function testBuildViewPassword() + { + $view = $this->getBlockView(InputType::NAME, ['type' => 'password']); + + $this->assertEquals('password', $view->vars['type']); + $this->assertEquals( + [ + 'type' => 'password', + 'autocomplete' => 'off', + ], + $view->vars['attr'] + ); + } + + public function testBuildViewWithoutOptions() + { + $view = $this->getBlockView(InputType::NAME); + + $this->assertEquals( + [ + 'type' => 'text', + ], + $view->vars['attr'] + ); + } + + public function testBuildView() + { + $options = [ + 'id' => 'usernameId', + 'name' => 'usernameName', + 'value' => 'username', + 'placeholder' => 'Enter username', + ]; + + $view = $this->getBlockView(InputType::NAME, $options); + + $this->assertEquals('text', $view->vars['type']); + $this->assertEquals(['type' => 'text'] + $options, $view->vars['attr']); + } +} diff --git a/src/Oro/Component/Layout/Block/Type/BaseType.php b/src/Oro/Component/Layout/Block/Type/BaseType.php index fa96e24f013..12795a9f947 100644 --- a/src/Oro/Component/Layout/Block/Type/BaseType.php +++ b/src/Oro/Component/Layout/Block/Type/BaseType.php @@ -16,19 +16,24 @@ class BaseType extends AbstractType */ public function setDefaultOptions(OptionsResolverInterface $resolver) { - $resolver->setDefaults( + $resolver->setOptional( [ - 'tag' => null, - 'attr' => [], - 'label_attr' => [], + 'vars', + 'tag', + 'attr', + 'label', + 'label_attr', + 'translation_domain', + 'class_prefix' ] ); - $resolver->setOptional(['vars', 'label', 'translation_domain', 'class_prefix']); $resolver->setAllowedTypes( [ 'vars' => 'array', + 'tag' => 'string', 'attr' => 'array', - 'label_attr' => 'array' + 'label_attr' => 'array', + 'class_prefix' => 'string' ] ); } @@ -46,11 +51,11 @@ public function buildView(BlockView $view, BlockInterface $block, array $options // add the view to itself vars to allow get it using 'block' variable in a rendered, for example TWIG $view->vars['block'] = $view; - $view->vars['tag'] = $options['tag']; + $view->vars['tag'] = isset($options['tag']) ? $options['tag'] : null; $view->vars['class_prefix'] = null; if (isset($options['class_prefix'])) { $view->vars['class_prefix'] = $options['class_prefix']; - } elseif($view->parent) { + } elseif ($view->parent) { $view->vars['class_prefix'] = $view->parent->vars['class_prefix']; } @@ -62,6 +67,7 @@ public function buildView(BlockView $view, BlockInterface $block, array $options // add label text and attributes if specified if (isset($options['label'])) { $view->vars['label'] = $options['label']; + $view->vars['label_attr'] = []; if (isset($options['label_attr'])) { $view->vars['label_attr'] = $options['label_attr']; } From eeb3eb65f16be9fab68d441c282b14316b0d2bf6 Mon Sep 17 00:00:00 2001 From: Andrey Yatsenco Date: Mon, 30 Nov 2015 18:21:10 +0200 Subject: [PATCH 052/471] BB-1534: Create abstract form context configurator in LayoutBundle - added $data.accountUserDataProvider for registration form - added $context.frontend_account_registration_form with registration form - added posibility to set form data from layout updates like, you can now create form like this form: blockType: form options: form_name: 'frontend_account_registration_form' form_data: { @value: $data.newAccountUser } form_start: blockType: form_start options: form_name: 'frontend_account_registration_form' form_method: 'POST' form_route_name: 'orob2b_account_frontend_account_user_register' form_end: blockType: form_end options: form_name: 'frontend_account_registration_form' --- .../Extension/ConfigExpressionExtension.php | 10 +-- .../Layout/Block/Type/FormStartType.php | 6 +- .../Layout/Block/Type/FormType.php | 9 +++ ...ndencyInjectionFormContextConfigurator.php | 66 +++++++++++++++++++ .../Layout/Form/AbstractFormAccessor.php | 33 ++++++++++ .../ConfigurableFormAccessorInterface.php | 13 ++++ .../Form/DependencyInjectionFormAccessor.php | 10 ++- .../Layout/AbstractBlockTypeExtension.php | 2 +- src/Oro/Component/Layout/BlockBuilder.php | 18 ++++- .../Layout/BlockBuilderInterface.php | 6 ++ src/Oro/Component/Layout/BlockFactory.php | 5 +- .../Layout/BlockTypeExtensionInterface.php | 4 +- src/Oro/Component/Layout/LayoutRegistry.php | 2 +- .../Layout/LayoutRegistryInterface.php | 4 +- 14 files changed, 171 insertions(+), 17 deletions(-) create mode 100644 src/Oro/Bundle/LayoutBundle/Layout/Extension/DependencyInjectionFormContextConfigurator.php create mode 100644 src/Oro/Bundle/LayoutBundle/Layout/Form/ConfigurableFormAccessorInterface.php diff --git a/src/Oro/Bundle/LayoutBundle/Layout/Block/Extension/ConfigExpressionExtension.php b/src/Oro/Bundle/LayoutBundle/Layout/Block/Extension/ConfigExpressionExtension.php index 094692958d5..58ccf45bb37 100644 --- a/src/Oro/Bundle/LayoutBundle/Layout/Block/Extension/ConfigExpressionExtension.php +++ b/src/Oro/Bundle/LayoutBundle/Layout/Block/Extension/ConfigExpressionExtension.php @@ -6,8 +6,7 @@ use Oro\Component\ConfigExpression\ExpressionInterface; use Oro\Component\Layout\AbstractBlockTypeExtension; use Oro\Component\Layout\Block\Type\BaseType; -use Oro\Component\Layout\BlockInterface; -use Oro\Component\Layout\BlockView; +use Oro\Component\Layout\BlockBuilderInterface; use Oro\Component\Layout\ContextInterface; use Oro\Component\Layout\DataAccessorInterface; use Oro\Component\Layout\OptionValueBag; @@ -40,13 +39,14 @@ public function __construct( /** * {@inheritdoc} */ - public function finishView(BlockView $view, BlockInterface $block, array $options) + public function buildBlock(BlockBuilderInterface $builder, array &$options) { - $context = $block->getContext(); + $context = $builder->getContext(); + $data = $builder->getDataAccessor(); $evaluate = $context->getOr('expressions_evaluate'); $encoding = $context->getOr('expressions_encoding'); if ($evaluate || $encoding !== null) { - $this->processExpressions($view->vars, $context, $block->getData(), $evaluate, $encoding); + $this->processExpressions($options, $context, $data, $evaluate, $encoding); } } diff --git a/src/Oro/Bundle/LayoutBundle/Layout/Block/Type/FormStartType.php b/src/Oro/Bundle/LayoutBundle/Layout/Block/Type/FormStartType.php index 696031816ab..c65dbd5e12f 100644 --- a/src/Oro/Bundle/LayoutBundle/Layout/Block/Type/FormStartType.php +++ b/src/Oro/Bundle/LayoutBundle/Layout/Block/Type/FormStartType.php @@ -37,8 +37,6 @@ public function buildView(BlockView $view, BlockInterface $block, array $options { $formAccessor = $this->getFormAccessor($block->getContext(), $options); - $view->vars['form'] = $formAccessor->getView(); - // form action if (isset($options['form_action'])) { $path = $options['form_action']; @@ -89,6 +87,10 @@ public function buildView(BlockView $view, BlockInterface $block, array $options */ public function finishView(BlockView $view, BlockInterface $block, array $options) { + $formAccessor = $this->getFormAccessor($block->getContext(), $options); + + $view->vars['form'] = $formAccessor->getView(); + // final check of the view vars and their modification (if required) // we have to do this in the finishView because only here we can be sure that // expressions have been evaluated (if $context.expressions_evaluate is true) diff --git a/src/Oro/Bundle/LayoutBundle/Layout/Block/Type/FormType.php b/src/Oro/Bundle/LayoutBundle/Layout/Block/Type/FormType.php index a0699278c87..de79b3a607a 100644 --- a/src/Oro/Bundle/LayoutBundle/Layout/Block/Type/FormType.php +++ b/src/Oro/Bundle/LayoutBundle/Layout/Block/Type/FormType.php @@ -11,6 +11,7 @@ use Oro\Component\Layout\BlockInterface; use Oro\Component\Layout\BlockBuilderInterface; +use Oro\Bundle\LayoutBundle\Layout\Form\ConfigurableFormAccessorInterface; use Oro\Bundle\LayoutBundle\Layout\Form\FormLayoutBuilderInterface; /** @@ -69,6 +70,9 @@ public function setDefaultOptions(OptionsResolverInterface $resolver) } ] ); + $resolver + ->setOptional(['form_data']); + $resolver->setAllowedTypes( [ 'preferred_fields' => 'array', @@ -95,7 +99,12 @@ public function buildBlock(BlockBuilderInterface $builder, array $options) */ public function buildView(BlockView $view, BlockInterface $block, array $options) { + $view->vars['form_data'] = isset($options['form_data']) ? $options['form_data'] : null; + $formAccessor = $this->getFormAccessor($block->getContext(), $options); + if ($formAccessor instanceof ConfigurableFormAccessorInterface) { + $formAccessor->setFormData($view->vars['form_data']); + } $view->vars['form'] = $formAccessor->getView(); } diff --git a/src/Oro/Bundle/LayoutBundle/Layout/Extension/DependencyInjectionFormContextConfigurator.php b/src/Oro/Bundle/LayoutBundle/Layout/Extension/DependencyInjectionFormContextConfigurator.php new file mode 100644 index 00000000000..a1ba6876bd4 --- /dev/null +++ b/src/Oro/Bundle/LayoutBundle/Layout/Extension/DependencyInjectionFormContextConfigurator.php @@ -0,0 +1,66 @@ +container = $container; + } + + /** + * {@inheritdoc} + */ + public function configureContext(ContextInterface $context) + { + if (!$this->formServiceId) { + throw new \Exception('formServiceId should be specified.'); + } + $formAccessor = new DependencyInjectionFormAccessor( + $this->container, + $this->formServiceId + ); + + $context->getResolver() + ->setDefaults([$this->contextOptionName => $formAccessor]); + } + + /** + * @param string $contextOptionName + */ + public function setContextOptionName($contextOptionName) + { + $this->contextOptionName = $contextOptionName; + } + + /** + * @param string $formServiceId + */ + public function setFormServiceId($formServiceId) + { + $this->formServiceId = $formServiceId; + } +} diff --git a/src/Oro/Bundle/LayoutBundle/Layout/Form/AbstractFormAccessor.php b/src/Oro/Bundle/LayoutBundle/Layout/Form/AbstractFormAccessor.php index 48bb7316865..fe8761d0711 100644 --- a/src/Oro/Bundle/LayoutBundle/Layout/Form/AbstractFormAccessor.php +++ b/src/Oro/Bundle/LayoutBundle/Layout/Form/AbstractFormAccessor.php @@ -50,6 +50,23 @@ public function getAction() return $this->action; } + /** + * @param FormAction $action + */ + public function setAction(FormAction $action) + { + $this->action = $action; + } + + /** + * @param string $route + * @param array $routeParams + */ + public function setActionRoute($route, array $routeParams = []) + { + $this->action = FormAction::createByRoute($route, $routeParams); + } + /** * {@inheritdoc} */ @@ -60,6 +77,14 @@ public function getMethod() return $this->method; } + /** + * @param string|null $method + */ + public function setMethod($method) + { + $this->method = $method; + } + /** * {@inheritdoc} */ @@ -70,6 +95,14 @@ public function getEnctype() return $this->enctype; } + /** + * @param string|null $enctype + */ + public function setEnctype($enctype) + { + $this->enctype = $enctype; + } + /** * {@inheritdoc} */ diff --git a/src/Oro/Bundle/LayoutBundle/Layout/Form/ConfigurableFormAccessorInterface.php b/src/Oro/Bundle/LayoutBundle/Layout/Form/ConfigurableFormAccessorInterface.php new file mode 100644 index 00000000000..2e0f3de1eb4 --- /dev/null +++ b/src/Oro/Bundle/LayoutBundle/Layout/Form/ConfigurableFormAccessorInterface.php @@ -0,0 +1,13 @@ +hash; } + + /** + * @param mixed $formData + */ + public function setFormData($formData) + { + $this->form->setData($formData); + } } diff --git a/src/Oro/Component/Layout/AbstractBlockTypeExtension.php b/src/Oro/Component/Layout/AbstractBlockTypeExtension.php index 363894a4edf..7aff9cb0e8c 100644 --- a/src/Oro/Component/Layout/AbstractBlockTypeExtension.php +++ b/src/Oro/Component/Layout/AbstractBlockTypeExtension.php @@ -9,7 +9,7 @@ abstract class AbstractBlockTypeExtension implements BlockTypeExtensionInterface /** * {@inheritdoc} */ - public function buildBlock(BlockBuilderInterface $builder, array $options) + public function buildBlock(BlockBuilderInterface $builder, array &$options) { } diff --git a/src/Oro/Component/Layout/BlockBuilder.php b/src/Oro/Component/Layout/BlockBuilder.php index 6ee743d894b..52d72a70d9d 100644 --- a/src/Oro/Component/Layout/BlockBuilder.php +++ b/src/Oro/Component/Layout/BlockBuilder.php @@ -19,22 +19,30 @@ final class BlockBuilder implements BlockBuilderInterface /** @var string */ private $id; + /** + * @var DataAccessorInterface + */ + private $dataAccessor; + /** * @param LayoutManipulatorInterface $layoutManipulator * @param RawLayout $rawLayout * @param BlockTypeHelperInterface $typeHelper * @param ContextInterface $context + * @param DataAccessorInterface $dataAccessor */ public function __construct( LayoutManipulatorInterface $layoutManipulator, RawLayout $rawLayout, BlockTypeHelperInterface $typeHelper, - ContextInterface $context + ContextInterface $context, + DataAccessorInterface $dataAccessor ) { $this->layoutManipulator = $layoutManipulator; $this->rawLayout = $rawLayout; $this->typeHelper = $typeHelper; $this->context = $context; + $this->dataAccessor = $dataAccessor; } /** @@ -90,4 +98,12 @@ public function getContext() { return $this->context; } + + /** + * {@inheritdoc} + */ + public function getDataAccessor() + { + return $this->dataAccessor; + } } diff --git a/src/Oro/Component/Layout/BlockBuilderInterface.php b/src/Oro/Component/Layout/BlockBuilderInterface.php index f359cd5502c..f87d9bfff71 100644 --- a/src/Oro/Component/Layout/BlockBuilderInterface.php +++ b/src/Oro/Component/Layout/BlockBuilderInterface.php @@ -38,4 +38,10 @@ public function getTypeHelper(); * @return ContextInterface */ public function getContext(); + + /** + * Returns the layout data + * @return DataAccessorInterface + */ + public function getDataAccessor(); } diff --git a/src/Oro/Component/Layout/BlockFactory.php b/src/Oro/Component/Layout/BlockFactory.php index 2450cbaf3be..26918b2adb1 100644 --- a/src/Oro/Component/Layout/BlockFactory.php +++ b/src/Oro/Component/Layout/BlockFactory.php @@ -87,7 +87,8 @@ protected function initializeState(RawLayout $rawLayout, ContextInterface $conte $this->layoutManipulator, $this->rawLayout, $this->typeHelper, - $this->context + $this->context, + $this->dataAccessor ); $this->block = new Block( $this->rawLayout, @@ -204,7 +205,6 @@ protected function buildBlock($id) // resolve options $resolvedOptions = $this->optionsResolver->resolveOptions($blockType, $options); - $this->rawLayout->setProperty($id, RawLayout::RESOLVED_OPTIONS, $resolvedOptions); // point the block builder state to the current block $this->blockBuilder->initialize($id); @@ -213,6 +213,7 @@ protected function buildBlock($id) $type->buildBlock($this->blockBuilder, $resolvedOptions); $this->registry->buildBlock($type->getName(), $this->blockBuilder, $resolvedOptions); } + $this->rawLayout->setProperty($id, RawLayout::RESOLVED_OPTIONS, $resolvedOptions); } /** diff --git a/src/Oro/Component/Layout/BlockTypeExtensionInterface.php b/src/Oro/Component/Layout/BlockTypeExtensionInterface.php index 662b1fe8584..808b8f8decd 100644 --- a/src/Oro/Component/Layout/BlockTypeExtensionInterface.php +++ b/src/Oro/Component/Layout/BlockTypeExtensionInterface.php @@ -10,14 +10,14 @@ interface BlockTypeExtensionInterface * Builds the block. * * This method is called after the extended type has built the block - * and can be used to further modify the block. + * and can be used to further modify the block and prepare block options. * * @see BlockTypeInterface::buildForm() * * @param BlockBuilderInterface $builder * @param array $options */ - public function buildBlock(BlockBuilderInterface $builder, array $options); + public function buildBlock(BlockBuilderInterface $builder, array &$options); /** * Builds the block view. diff --git a/src/Oro/Component/Layout/LayoutRegistry.php b/src/Oro/Component/Layout/LayoutRegistry.php index 82c8046551d..f9bff164fb1 100644 --- a/src/Oro/Component/Layout/LayoutRegistry.php +++ b/src/Oro/Component/Layout/LayoutRegistry.php @@ -124,7 +124,7 @@ public function setDefaultOptions($name, OptionsResolverInterface $resolver) /** * {@inheritdoc} */ - public function buildBlock($name, BlockBuilderInterface $builder, array $options) + public function buildBlock($name, BlockBuilderInterface $builder, array &$options) { $extensions = isset($this->typeExtensions[$name]) ? $this->typeExtensions[$name] diff --git a/src/Oro/Component/Layout/LayoutRegistryInterface.php b/src/Oro/Component/Layout/LayoutRegistryInterface.php index c9d8d1af118..f7bad529f26 100644 --- a/src/Oro/Component/Layout/LayoutRegistryInterface.php +++ b/src/Oro/Component/Layout/LayoutRegistryInterface.php @@ -60,7 +60,7 @@ public function setDefaultOptions($name, OptionsResolverInterface $resolver); * Builds the block. * * This method is called after the extended type has built the block - * and can be used to further modify the block. + * and can be used to further modify the block and prepare block options. * * @see BlockTypeInterface::buildForm() * @@ -68,7 +68,7 @@ public function setDefaultOptions($name, OptionsResolverInterface $resolver); * @param BlockBuilderInterface $builder The block builder * @param array $options The options */ - public function buildBlock($name, BlockBuilderInterface $builder, array $options); + public function buildBlock($name, BlockBuilderInterface $builder, array &$options); /** * Builds the block view. From fdb71ec23365ee761883fa53eb755b449738e5a1 Mon Sep 17 00:00:00 2001 From: Oleksandr Nechyporenko Date: Tue, 1 Dec 2015 12:16:13 +0200 Subject: [PATCH 053/471] BB-1532: Dump layout styles in css - fix CS issues --- src/Oro/Bundle/LayoutBundle/Assetic/LayoutResource.php | 6 ++++-- .../LayoutBundle/DependencyInjection/Configuration.php | 2 +- src/Oro/Bundle/LayoutBundle/Resources/config/services.yml | 1 - src/Oro/Component/Layout/ArrayOptionValueBuilder.php | 2 +- src/Oro/Component/Layout/Extension/Theme/Model/Theme.php | 6 +++--- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Oro/Bundle/LayoutBundle/Assetic/LayoutResource.php b/src/Oro/Bundle/LayoutBundle/Assetic/LayoutResource.php index cbd76c389e1..9360282249a 100644 --- a/src/Oro/Bundle/LayoutBundle/Assetic/LayoutResource.php +++ b/src/Oro/Bundle/LayoutBundle/Assetic/LayoutResource.php @@ -9,6 +9,8 @@ class LayoutResource implements ResourceInterface { + const RESOURCE_ALIAS = 'layout'; + /** @var ThemeManager */ protected $themeManager; @@ -33,7 +35,7 @@ public function isFresh($timestamp) */ public function __toString() { - return 'layout'; + return self::RESOURCE_ALIAS; } /** @@ -58,7 +60,7 @@ protected function collectThemeAssets(Theme $theme) $formulae = []; $assets = $theme->getDataByKey('assets', []); foreach ($assets as $assetKey => $asset) { - $name = 'layout_' . $theme->getName(). '_' . $assetKey; + $name = self::RESOURCE_ALIAS . '_' . $theme->getName(). '_' . $assetKey; $formulae[$name] = [ $asset['inputs'], $asset['filters'], diff --git a/src/Oro/Bundle/LayoutBundle/DependencyInjection/Configuration.php b/src/Oro/Bundle/LayoutBundle/DependencyInjection/Configuration.php index bff642eb6b5..10f754c7077 100644 --- a/src/Oro/Bundle/LayoutBundle/DependencyInjection/Configuration.php +++ b/src/Oro/Bundle/LayoutBundle/DependencyInjection/Configuration.php @@ -114,7 +114,7 @@ protected function appendThemingNodes(ArrayNodeDefinition $parentNode) $dataTreeBuilder = new TreeBuilder(); $dataNode = $dataTreeBuilder->root('data'); - $dataNode->info('Layout additional data')->end(); + $dataNode->info('Layout theme additional data')->end(); $node ->useAttributeAsKey('theme-identifier') diff --git a/src/Oro/Bundle/LayoutBundle/Resources/config/services.yml b/src/Oro/Bundle/LayoutBundle/Resources/config/services.yml index dad44a3ee7e..f630b91b536 100644 --- a/src/Oro/Bundle/LayoutBundle/Resources/config/services.yml +++ b/src/Oro/Bundle/LayoutBundle/Resources/config/services.yml @@ -147,6 +147,5 @@ services: class: %oro_layout.assetic.layout_resource.class% arguments: - @oro_layout.theme_manager - - @oro_layout.layout_manager tags: - { name: assetic.formula_resource, loader: layout } diff --git a/src/Oro/Component/Layout/ArrayOptionValueBuilder.php b/src/Oro/Component/Layout/ArrayOptionValueBuilder.php index 749e116b3cf..5e327d94578 100644 --- a/src/Oro/Component/Layout/ArrayOptionValueBuilder.php +++ b/src/Oro/Component/Layout/ArrayOptionValueBuilder.php @@ -7,7 +7,7 @@ class ArrayOptionValueBuilder implements OptionValueBuilderInterface { - /** @var mixed[] */ + /** @var array */ protected $values = []; /** diff --git a/src/Oro/Component/Layout/Extension/Theme/Model/Theme.php b/src/Oro/Component/Layout/Extension/Theme/Model/Theme.php index 9f20806638b..26f59417037 100644 --- a/src/Oro/Component/Layout/Extension/Theme/Model/Theme.php +++ b/src/Oro/Component/Layout/Extension/Theme/Model/Theme.php @@ -31,7 +31,7 @@ class Theme /** @var string[] */ protected $groups = []; - /** @var mixed[] */ + /** @var array */ protected $data = []; /** @@ -181,7 +181,7 @@ public function getGroups() } /** - * @param mixed[] $data + * @param array $data */ public function setData(array $data) { @@ -189,7 +189,7 @@ public function setData(array $data) } /** - * @return mixed[] + * @return array */ public function getData() { From 5b079249df03f5b88e602f0218a98d92fa210e24 Mon Sep 17 00:00:00 2001 From: Oleksandr Nechyporenko Date: Tue, 1 Dec 2015 17:41:23 +0200 Subject: [PATCH 054/471] BB-1533: Create form template for new theme and apply - fix CS issues --- .../Compiler/OverrideServiceCompilerPass.php | 8 +++----- .../LayoutBundle/Resources/config/php_renderer.yml | 5 +++-- .../LayoutBundle/Resources/config/twig_renderer.yml | 3 ++- .../Compiler/OverrideServiceCompilerPassTest.php | 2 +- .../Unit/Form/RendererEngine/RendererEngineTest.php | 2 +- .../Tests/Unit/Layout/TwigLayoutRendererTest.php | 6 ++++++ .../Layout/Templating/Helper/LayoutHelper.php | 2 +- .../Tests/Unit/Templating/Helper/LayoutHelperTest.php | 11 +++++++---- 8 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/Oro/Bundle/LayoutBundle/DependencyInjection/Compiler/OverrideServiceCompilerPass.php b/src/Oro/Bundle/LayoutBundle/DependencyInjection/Compiler/OverrideServiceCompilerPass.php index de6b8b84d02..b300c718ed0 100644 --- a/src/Oro/Bundle/LayoutBundle/DependencyInjection/Compiler/OverrideServiceCompilerPass.php +++ b/src/Oro/Bundle/LayoutBundle/DependencyInjection/Compiler/OverrideServiceCompilerPass.php @@ -21,7 +21,7 @@ public function process(ContainerBuilder $container) $this->changeService( $container, 'templating.form.engine', - 'oro_layout.php.templating.form.engine' + 'oro_layout.templating.form.engine' ); } @@ -35,9 +35,7 @@ private function changeService(ContainerBuilder $container, $serviceName, $newSe $service = $container->getDefinition($serviceName); $newService = $container->getDefinition($newServiceName); - if ($service && $newService) { - $container->removeDefinition($serviceName); - $container->setDefinition($serviceName, $newService); - } + $container->removeDefinition($serviceName); + $container->setDefinition($serviceName, $newService); } } diff --git a/src/Oro/Bundle/LayoutBundle/Resources/config/php_renderer.yml b/src/Oro/Bundle/LayoutBundle/Resources/config/php_renderer.yml index 1d33c50a9ff..57e81da2178 100644 --- a/src/Oro/Bundle/LayoutBundle/Resources/config/php_renderer.yml +++ b/src/Oro/Bundle/LayoutBundle/Resources/config/php_renderer.yml @@ -3,6 +3,7 @@ parameters: oro_layout.php.renderer.engine.class: Symfony\Component\Form\Extension\Templating\TemplatingRendererEngine oro_layout.php.layout_renderer.class: Oro\Component\Layout\LayoutRenderer oro_layout.php.templating.helper.class: Oro\Component\Layout\Templating\Helper\LayoutHelper + oro_layout.templating.form.engine.class: Oro\Bundle\LayoutBundle\Form\RendererEngine\TemplatingRendererEngine services: oro_layout.php.renderer: @@ -34,8 +35,8 @@ services: tags: - { name: templating.helper, alias: layout } - oro_layout.php.templating.form.engine: - class: Oro\Bundle\LayoutBundle\Form\RendererEngine\TemplatingRendererEngine + oro_layout.templating.form.engine: + class: %oro_layout.templating.form.engine.class% arguments: - @templating.engine.php - %templating.helper.form.resources% diff --git a/src/Oro/Bundle/LayoutBundle/Resources/config/twig_renderer.yml b/src/Oro/Bundle/LayoutBundle/Resources/config/twig_renderer.yml index c88f0413861..864aa8ecbeb 100644 --- a/src/Oro/Bundle/LayoutBundle/Resources/config/twig_renderer.yml +++ b/src/Oro/Bundle/LayoutBundle/Resources/config/twig_renderer.yml @@ -3,6 +3,7 @@ parameters: oro_layout.twig.renderer.class: Symfony\Bridge\Twig\Form\TwigRenderer oro_layout.twig.renderer.engine.class: Symfony\Bridge\Twig\Form\TwigRendererEngine oro_layout.twig.layout_renderer.class: Oro\Bundle\LayoutBundle\Layout\TwigLayoutRenderer + oro_layout.twig.form.engine.class: Oro\Bundle\LayoutBundle\Form\RendererEngine\TwigRendererEngine services: oro_layout.twig.extension.layout: @@ -35,6 +36,6 @@ services: - @twig oro_layout.twig.form.engine: - class: Oro\Bundle\LayoutBundle\Form\RendererEngine\TwigRendererEngine + class: %oro_layout.twig.form.engine.class% arguments: - %twig.form.resources% diff --git a/src/Oro/Bundle/LayoutBundle/Tests/Unit/DependencyInjection/Compiler/OverrideServiceCompilerPassTest.php b/src/Oro/Bundle/LayoutBundle/Tests/Unit/DependencyInjection/Compiler/OverrideServiceCompilerPassTest.php index 6aa5b5afc3e..0215cbb019a 100644 --- a/src/Oro/Bundle/LayoutBundle/Tests/Unit/DependencyInjection/Compiler/OverrideServiceCompilerPassTest.php +++ b/src/Oro/Bundle/LayoutBundle/Tests/Unit/DependencyInjection/Compiler/OverrideServiceCompilerPassTest.php @@ -36,7 +36,7 @@ public function testProcess() $container->setDefinition('templating.form.engine', $phpFormEngine); $newPhpFormEngine = new Definition('\OroLayoutPhpFormEngineClass'); - $container->setDefinition('oro_layout.php.templating.form.engine', $newPhpFormEngine); + $container->setDefinition('oro_layout.templating.form.engine', $newPhpFormEngine); $this->pass->process($container); diff --git a/src/Oro/Bundle/LayoutBundle/Tests/Unit/Form/RendererEngine/RendererEngineTest.php b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Form/RendererEngine/RendererEngineTest.php index 49c592d252a..346999bfa99 100644 --- a/src/Oro/Bundle/LayoutBundle/Tests/Unit/Form/RendererEngine/RendererEngineTest.php +++ b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Form/RendererEngine/RendererEngineTest.php @@ -28,7 +28,7 @@ public function testAddDefaultThemes() } /** - * @return \Oro\Component\Layout\Form\RendererEngine\FormRendererEngineInterface + * @return FormRendererEngineInterface */ abstract public function createRendererEngine(); } diff --git a/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/TwigLayoutRendererTest.php b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/TwigLayoutRendererTest.php index 5cd6685f071..703c6f9734a 100644 --- a/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/TwigLayoutRendererTest.php +++ b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/TwigLayoutRendererTest.php @@ -2,18 +2,24 @@ namespace Oro\Bundle\LayoutBundle\Tests\Unit\Layout; +use Symfony\Bridge\Twig\Form\TwigRendererInterface; + use Oro\Bundle\LayoutBundle\Layout\TwigLayoutRenderer; +use Oro\Component\Layout\Form\RendererEngine\FormRendererEngineInterface; class TwigLayoutRendererTest extends \PHPUnit_Framework_TestCase { public function testEnvironmentSet() { + /** @var TwigRendererInterface|\PHPUnit_Framework_MockObject_MockObject $innerRenderer */ $innerRenderer = $this->getMock('Symfony\Bridge\Twig\Form\TwigRendererInterface'); + /** @var \Twig_Environment $environment */ $environment = $this->getMockBuilder('\Twig_Environment')->getMock(); $innerRenderer->expects($this->once()) ->method('setEnvironment') ->with($this->identicalTo($environment)); + /** @var FormRendererEngineInterface $formRenderer */ $formRenderer = $this->getMock('Oro\Component\Layout\Form\RendererEngine\FormRendererEngineInterface'); new TwigLayoutRenderer($innerRenderer, $formRenderer, $environment); diff --git a/src/Oro/Component/Layout/Templating/Helper/LayoutHelper.php b/src/Oro/Component/Layout/Templating/Helper/LayoutHelper.php index 719b085a5a9..c48f896ad53 100644 --- a/src/Oro/Component/Layout/Templating/Helper/LayoutHelper.php +++ b/src/Oro/Component/Layout/Templating/Helper/LayoutHelper.php @@ -20,7 +20,7 @@ class LayoutHelper extends Helper /** @var TextHelper */ private $textHelper; - /** @var \Oro\Component\Layout\Form\RendererEngine\FormRendererEngineInterface */ + /** @var FormRendererEngineInterface */ private $formRendererEngine; /** diff --git a/src/Oro/Component/Layout/Tests/Unit/Templating/Helper/LayoutHelperTest.php b/src/Oro/Component/Layout/Tests/Unit/Templating/Helper/LayoutHelperTest.php index 0082935a818..87813e29ced 100644 --- a/src/Oro/Component/Layout/Tests/Unit/Templating/Helper/LayoutHelperTest.php +++ b/src/Oro/Component/Layout/Tests/Unit/Templating/Helper/LayoutHelperTest.php @@ -2,22 +2,25 @@ namespace Oro\Component\Layout\Tests\Unit\Templating\Helper; +use Symfony\Component\Form\FormRendererInterface; + use Oro\Component\Layout\BlockView; -use Oro\Component\Layout\Templating\Helper\LayoutHelper; use Oro\Component\Layout\Form\RendererEngine\FormRendererEngineInterface; +use Oro\Component\Layout\Templating\Helper\LayoutHelper; +use Oro\Component\Layout\Templating\TextHelper; class LayoutHelperTest extends \PHPUnit_Framework_TestCase { - /** @var \PHPUnit_Framework_MockObject_MockObject */ + /** @var FormRendererInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $renderer; - /** @var \PHPUnit_Framework_MockObject_MockObject */ + /** @var \PHPUnit_Framework_MockObject_MockObject|TextHelper */ protected $textHelper; /** @var LayoutHelper */ protected $helper; - /** @var \Oro\Component\Layout\Form\RendererEngine\FormRendererEngineInterface */ + /** @var FormRendererEngineInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $formRenderer; protected function setUp() From 5f831541c13496096ac681abb7a6d2b447162c1f Mon Sep 17 00:00:00 2001 From: Sergey Zhuravel Date: Wed, 2 Dec 2015 01:15:48 +0200 Subject: [PATCH 055/471] BB-1385: Add listener to add both backend and frontend audits to backend grid - cr fixes --- .../DataAuditBundle/Entity/AbstractAudit.php | 17 +++-- .../Entity/AbstractAuditField.php | 7 -- .../Bundle/DataAuditBundle/Entity/Audit.php | 16 ----- .../DataAuditBundle/Entity/AuditField.php | 5 +- .../EventListener/AuditGridListener.php | 8 +-- .../Schema/OroDataAuditBundleInstaller.php | 3 - .../Migrations/Schema/v1_6/AddColumn.php | 3 - .../Migrations/Schema/v1_6/SetNotNullable.php | 6 -- .../Migrations/Schema/v1_6/SetValue.php | 8 --- .../Resources/config/datagrid.yml | 69 +++++++++---------- .../Resources/config/placeholders.yml | 4 +- .../CompilerPass/ConfigurationPass.php | 5 +- 12 files changed, 58 insertions(+), 93 deletions(-) diff --git a/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAudit.php b/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAudit.php index db53da55a05..4510a86c27e 100644 --- a/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAudit.php +++ b/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAudit.php @@ -12,7 +12,6 @@ use Oro\Bundle\UserBundle\Entity\AbstractUser; /** - * @ORM\MappedSuperclass() * @ORM\Entity() * @ORM\Table(name="oro_audit", indexes={ * @ORM\Index(name="idx_oro_audit_logged_at", columns={"logged_at"}), @@ -20,7 +19,7 @@ * }) * @ORM\InheritanceType("SINGLE_TABLE") * @ORM\DiscriminatorColumn(name="type", type="string") - * @ORM\DiscriminatorMap({"audit" = "Audit"}) + * @ORM\DiscriminatorMap({"audit" = "Oro\Bundle\DataAuditBundle\Entity\Audit"}) */ abstract class AbstractAudit extends AbstractLogEntry { @@ -48,7 +47,11 @@ abstract class AbstractAudit extends AbstractLogEntry /** * @var AbstractAuditField[]|Collection * - * @ORM\OneToMany(targetEntity="AbstractAuditField", mappedBy="audit", cascade={"persist"}) + * @ORM\OneToMany( + * targetEntity="Oro\Bundle\DataAuditBundle\Entity\AuditField", + * mappedBy="audit", + * cascade={"persist"} + * ) */ protected $fields; @@ -88,7 +91,13 @@ abstract public function getUser(); * @param mixed $oldValue * @return AbstractAuditField */ - abstract protected function getAuditFieldInstance(AbstractAudit $audit, $field, $dataType, $newValue, $oldValue); + /** + * {@inheritdoc} + */ + protected function getAuditFieldInstance(AbstractAudit $audit, $field, $dataType, $newValue, $oldValue) + { + return new AuditField($audit, $field, $dataType, $newValue, $oldValue); + } /** * Constructor diff --git a/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAuditField.php b/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAuditField.php index aa2e8bafab3..9dd1e449c45 100644 --- a/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAuditField.php +++ b/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAuditField.php @@ -10,13 +10,6 @@ /** * @ORM\MappedSuperclass() - * @ORM\Entity() - * @ORM\Table(name="oro_audit_field", indexes={ - * @ORM\Index(name="idx_oro_audit_field_type", columns={"type"}) - * }) - * @ORM\InheritanceType("SINGLE_TABLE") - * @ORM\DiscriminatorColumn(name="type", type="string") - * @ORM\DiscriminatorMap({"audit_field" = "AuditField"}) */ abstract class AbstractAuditField { diff --git a/src/Oro/Bundle/DataAuditBundle/Entity/Audit.php b/src/Oro/Bundle/DataAuditBundle/Entity/Audit.php index 5f6405caf57..3a7bbc13a25 100644 --- a/src/Oro/Bundle/DataAuditBundle/Entity/Audit.php +++ b/src/Oro/Bundle/DataAuditBundle/Entity/Audit.php @@ -2,7 +2,6 @@ namespace Oro\Bundle\DataAuditBundle\Entity; -use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use JMS\Serializer\Annotation\Type; @@ -67,13 +66,6 @@ class Audit extends AbstractAudit */ protected $version; - /** - * @var AuditField[]|Collection - * - * @ORM\OneToMany(targetEntity="AuditField", mappedBy="audit", cascade={"persist"}) - */ - protected $fields; - /** * @var string $username * @@ -91,14 +83,6 @@ class Audit extends AbstractAudit */ protected $user; - /** - * {@inheritdoc} - */ - protected function getAuditFieldInstance(AbstractAudit $audit, $field, $dataType, $newValue, $oldValue) - { - return new AuditField($audit, $field, $dataType, $newValue, $oldValue); - } - /** * {@inheritdoc} */ diff --git a/src/Oro/Bundle/DataAuditBundle/Entity/AuditField.php b/src/Oro/Bundle/DataAuditBundle/Entity/AuditField.php index b96ecdbe5e7..02fc5b05b8a 100644 --- a/src/Oro/Bundle/DataAuditBundle/Entity/AuditField.php +++ b/src/Oro/Bundle/DataAuditBundle/Entity/AuditField.php @@ -8,7 +8,8 @@ use Oro\Bundle\EntityConfigBundle\Metadata\Annotation\Config; /** - * @ORM\Entity + * @ORM\Entity() + * @ORM\Table(name="oro_audit_field") * @Config(mode="hidden") */ class AuditField extends ExtendAuditField @@ -16,7 +17,7 @@ class AuditField extends ExtendAuditField /** * @var Audit * - * @ORM\ManyToOne(targetEntity="Audit", inversedBy="fields", cascade={"persist"}) + * @ORM\ManyToOne(targetEntity="AbstractAudit", inversedBy="fields", cascade={"persist"}) * @ORM\JoinColumn(name="audit_id", referencedColumnName="id", nullable=false, onDelete="CASCADE") */ protected $audit; diff --git a/src/Oro/Bundle/DataAuditBundle/EventListener/AuditGridListener.php b/src/Oro/Bundle/DataAuditBundle/EventListener/AuditGridListener.php index 9e64668f3a2..9369178778d 100644 --- a/src/Oro/Bundle/DataAuditBundle/EventListener/AuditGridListener.php +++ b/src/Oro/Bundle/DataAuditBundle/EventListener/AuditGridListener.php @@ -16,7 +16,7 @@ class AuditGridListener protected $em; /** @var null|array */ - protected $objectClassChoices = null; + protected $objectClassChoices; /** * @param EntityManager $em @@ -34,11 +34,11 @@ public function __construct(EntityManager $em) public function getObjectClassOptions() { if (is_null($this->objectClassChoices)) { - $options = array(); + $options = []; $result = $this->em->createQueryBuilder() - ->add('select', 'a.objectClass') - ->add('from', 'Oro\Bundle\DataAuditBundle\Entity\Audit a') + ->select('a.objectClass') + ->from('Oro\Bundle\DataAuditBundle\Entity\AbstractAudit', 'a') ->distinct('a.objectClass') ->getQuery() ->getArrayResult(); diff --git a/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/OroDataAuditBundleInstaller.php b/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/OroDataAuditBundleInstaller.php index d813ce77f81..f80c60f5788 100644 --- a/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/OroDataAuditBundleInstaller.php +++ b/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/OroDataAuditBundleInstaller.php @@ -114,9 +114,6 @@ private function createAuditField(Schema $schema) 'comment' => '(DC2Type:json_array)', ]); $auditFieldTable->setPrimaryKey(['id']); - $auditFieldTable->addColumn('type', 'string', ['length' => 255]); - - $auditFieldTable->addIndex(['type'], 'idx_oro_audit_field_type'); $auditFieldTable->addIndex(['audit_id'], 'IDX_9A31A824BD29F359', []); $auditFieldTable->addForeignKeyConstraint( diff --git a/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/AddColumn.php b/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/AddColumn.php index ce855ca4ce0..5f5ed5f4a8e 100644 --- a/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/AddColumn.php +++ b/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/AddColumn.php @@ -23,8 +23,5 @@ public function up(Schema $schema, QueryBag $queries) { $auditTable = $schema->getTable('oro_audit'); $auditTable->addColumn('type', 'string', ['length' => 255, 'notnull' => false]); - - $auditFieldTable = $schema->getTable('oro_audit_field'); - $auditFieldTable->addColumn('type', 'string', ['length' => 255, 'notnull' => false]); } } diff --git a/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/SetNotNullable.php b/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/SetNotNullable.php index 5f3c143a4ac..365b6a485bd 100644 --- a/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/SetNotNullable.php +++ b/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/SetNotNullable.php @@ -27,11 +27,5 @@ public function up(Schema $schema, QueryBag $queries) ->setType(Type::getType(Type::STRING)) ->setOptions(['length' => 255, 'notnull' => true]); $auditTable->addIndex(['type'], 'idx_oro_audit_type'); - - $auditFieldTable = $schema->getTable('oro_audit_field'); - $auditFieldTable->getColumn('type') - ->setType(Type::getType(Type::STRING)) - ->setOptions(['length' => 255, 'notnull' => true]); - $auditFieldTable->addIndex(['type'], 'idx_oro_audit_field_type'); } } diff --git a/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/SetValue.php b/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/SetValue.php index cda73823e17..642674c05c0 100644 --- a/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/SetValue.php +++ b/src/Oro/Bundle/DataAuditBundle/Migrations/Schema/v1_6/SetValue.php @@ -30,13 +30,5 @@ public function up(Schema $schema, QueryBag $queries) ['type' => Type::STRING] ) ); - - $queries->addPreQuery( - new ParametrizedSqlMigrationQuery( - 'UPDATE oro_audit_field SET type = :type', - ['type' => 'audit_field'], - ['type' => Type::STRING] - ) - ); } } diff --git a/src/Oro/Bundle/DataAuditBundle/Resources/config/datagrid.yml b/src/Oro/Bundle/DataAuditBundle/Resources/config/datagrid.yml index d49bde388fd..b0730a6904e 100644 --- a/src/Oro/Bundle/DataAuditBundle/Resources/config/datagrid.yml +++ b/src/Oro/Bundle/DataAuditBundle/Resources/config/datagrid.yml @@ -7,37 +7,34 @@ datagrid: type: orm query: select: - - a - - a.id - - a.action - - a.version - - a.objectClass - - a.objectName - - a.objectId - - a.loggedAt - - > - CONCAT( - CONCAT( - CONCAT(u.firstName, ' '), - CONCAT(u.lastName, ' ') - ), - CONCAT(' - ', u.email) - ) as author - - o.name as organization + audit: a + id: a.id + section: a.action + version: a.version + objectClass: a.objectClass + objectName: a.objectName + objectId: a.objectId + loggedAt: a.loggedAt + author: CONCAT(u.firstName, ' ', u.lastName, ' - ', u.email) as author + organization: o.name as organization from: - - { table: OroDataAuditBundle:Audit, alias: a } + - { table: OroDataAuditBundle:AbstractAudit, alias: a } join: left: - user: - join: a.user - alias: u organization: join: a.organization alias: o fields: join: a.fields alias: f - + audit: + join: OroDataAuditBundle:Audit + alias: ua + conditionType: WITH + condition: ua.id = a.id + user: + join: ua.user + alias: u where: and: - o.id = @oro_security.security_facade->getOrganizationId @@ -130,27 +127,25 @@ datagrid: type: orm query: select: - - a - - a.id - - a.loggedAt - - > - CONCAT( - CONCAT( - CONCAT(u.firstName, ' '), - CONCAT(u.lastName, ' ') - ), - CONCAT(' - ', u.email) - ) as author + audit: a + id: a.id + loggedAt: a.loggedAt + author: CONCAT(u.firstName, ' ', u.lastName, ' - ', u.email) as author from: - - { table: OroDataAuditBundle:Audit, alias: a } + - { table: OroDataAuditBundle:AbstractAudit, alias: a } join: left: - user: - join: a.user - alias: u fields: join: a.fields alias: f + audit: + join: OroDataAuditBundle:Audit + alias: ua + conditionType: WITH + condition: ua.id = a.id + user: + join: ua.user + alias: u where: and: - a.objectClass = :objectClass AND a.objectId = :objectId diff --git a/src/Oro/Bundle/DataAuditBundle/Resources/config/placeholders.yml b/src/Oro/Bundle/DataAuditBundle/Resources/config/placeholders.yml index b208a31289a..6df62f1c029 100644 --- a/src/Oro/Bundle/DataAuditBundle/Resources/config/placeholders.yml +++ b/src/Oro/Bundle/DataAuditBundle/Resources/config/placeholders.yml @@ -12,7 +12,9 @@ placeholders: items: change_history_link: template: OroDataAuditBundle::change_history_link.html.twig - applicable: @oro_dataaudit.placeholder.filter->isEntityAuditable($entity$, $audit_show_change_history$) + applicable: + - @oro_dataaudit.placeholder.filter->isEntityAuditable($entity$, $audit_show_change_history$) + - @oro_user.placeholder.filter->isUserApplicable() acl: oro_dataaudit_history template_audit_condition_type_select: template: OroDataAuditBundle:js:audit-condition-type-select.html.twig diff --git a/src/Oro/Bundle/DataGridBundle/DependencyInjection/CompilerPass/ConfigurationPass.php b/src/Oro/Bundle/DataGridBundle/DependencyInjection/CompilerPass/ConfigurationPass.php index 04860ed3947..1c422f34d46 100644 --- a/src/Oro/Bundle/DataGridBundle/DependencyInjection/CompilerPass/ConfigurationPass.php +++ b/src/Oro/Bundle/DataGridBundle/DependencyInjection/CompilerPass/ConfigurationPass.php @@ -6,6 +6,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; +use Oro\Bundle\UIBundle\Tools\ArrayUtils; use Oro\Component\Config\Loader\CumulativeConfigLoader; use Oro\Component\Config\Loader\YamlCumulativeFileLoader; @@ -48,7 +49,7 @@ protected function registerConfigFiles(ContainerBuilder $container) $resources = $configLoader->load($container); foreach ($resources as $resource) { if (isset($resource->data[self::ROOT_PARAMETER]) && is_array($resource->data[self::ROOT_PARAMETER])) { - $config = array_merge_recursive($config, $resource->data[self::ROOT_PARAMETER]); + $config = ArrayUtils::arrayMergeRecursiveDistinct($config, $resource->data[self::ROOT_PARAMETER]); } } @@ -65,7 +66,7 @@ protected function registerConfigFiles(ContainerBuilder $container) protected function registerConfigProviders(ContainerBuilder $container) { if ($container->hasDefinition(self::CHAIN_PROVIDER_SERVICE_ID)) { - $providers = array(); + $providers = []; foreach ($container->findTaggedServiceIds(self::PROVIDER_TAG_NAME) as $id => $attributes) { $priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0; $providers[$priority][] = new Reference($id); From e2c7e7e8a15cf61982603b979ea0ce269b776ef6 Mon Sep 17 00:00:00 2001 From: Sergey Zhuravel Date: Wed, 2 Dec 2015 01:21:04 +0200 Subject: [PATCH 056/471] BB-1385: Add listener to add both backend and frontend audits to backend grid - cr fixes --- src/Oro/Bundle/DataAuditBundle/Entity/AbstractAudit.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAudit.php b/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAudit.php index 4510a86c27e..215ad0ae863 100644 --- a/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAudit.php +++ b/src/Oro/Bundle/DataAuditBundle/Entity/AbstractAudit.php @@ -91,9 +91,6 @@ abstract public function getUser(); * @param mixed $oldValue * @return AbstractAuditField */ - /** - * {@inheritdoc} - */ protected function getAuditFieldInstance(AbstractAudit $audit, $field, $dataType, $newValue, $oldValue) { return new AuditField($audit, $field, $dataType, $newValue, $oldValue); From 73e4234698415e75e1dd69a493ac09319443e007 Mon Sep 17 00:00:00 2001 From: Oleksandr Nechyporenko Date: Wed, 2 Dec 2015 15:36:49 +0200 Subject: [PATCH 057/471] BB-1534: Create abstract form context configurator in LayoutBundle - fix issues --- .../Extension/DependencyInjectionFormContextConfigurator.php | 1 - .../Layout/Form/DependencyInjectionFormAccessor.php | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Oro/Bundle/LayoutBundle/Layout/Extension/DependencyInjectionFormContextConfigurator.php b/src/Oro/Bundle/LayoutBundle/Layout/Extension/DependencyInjectionFormContextConfigurator.php index a1ba6876bd4..b0c01aefe35 100644 --- a/src/Oro/Bundle/LayoutBundle/Layout/Extension/DependencyInjectionFormContextConfigurator.php +++ b/src/Oro/Bundle/LayoutBundle/Layout/Extension/DependencyInjectionFormContextConfigurator.php @@ -3,7 +3,6 @@ namespace Oro\Bundle\LayoutBundle\Layout\Extension; use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\Routing\RouterInterface; use Oro\Component\Layout\ContextConfiguratorInterface; use Oro\Component\Layout\ContextInterface; diff --git a/src/Oro/Bundle/LayoutBundle/Layout/Form/DependencyInjectionFormAccessor.php b/src/Oro/Bundle/LayoutBundle/Layout/Form/DependencyInjectionFormAccessor.php index 29589f32252..6d834d1711e 100644 --- a/src/Oro/Bundle/LayoutBundle/Layout/Form/DependencyInjectionFormAccessor.php +++ b/src/Oro/Bundle/LayoutBundle/Layout/Form/DependencyInjectionFormAccessor.php @@ -67,6 +67,6 @@ public function toString() */ public function setFormData($formData) { - $this->form->setData($formData); + $this->getForm()->setData($formData); } } From 68ee20259d689fdb09bd0283b2a7f425bf06bb58 Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Thu, 3 Dec 2015 10:28:51 +0100 Subject: [PATCH 058/471] BAP-9429: Missing column functions for Date columns in Report builder --- .../ReportBundle/Resources/config/query_designer.yml | 7 +++++++ .../Resources/translations/messages.en.yml | 10 ++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/Oro/Bundle/ReportBundle/Resources/config/query_designer.yml b/src/Oro/Bundle/ReportBundle/Resources/config/query_designer.yml index fab369f3daa..ebbde9b0357 100644 --- a/src/Oro/Bundle/ReportBundle/Resources/config/query_designer.yml +++ b/src/Oro/Bundle/ReportBundle/Resources/config/query_designer.yml @@ -19,3 +19,10 @@ query_designer: - { name: Min, expr: MIN($column) } - { name: Max, expr: MAX($column) } query_type: [report] + date: + applicable: [{type: date}, {type: datetime}] + functions: + - { name: Count, expr: COUNT($column), return_type: integer } + - { name: Min, expr: MIN($column) } + - { name: Max, expr: MAX($column) } + query_type: [report] diff --git a/src/Oro/Bundle/ReportBundle/Resources/translations/messages.en.yml b/src/Oro/Bundle/ReportBundle/Resources/translations/messages.en.yml index 3dbc7513c2a..077c6487251 100644 --- a/src/Oro/Bundle/ReportBundle/Resources/translations/messages.en.yml +++ b/src/Oro/Bundle/ReportBundle/Resources/translations/messages.en.yml @@ -25,6 +25,16 @@ oro: Max: name: Max hint: Maximum value + date: + Count: + name: Count + hint: Number of items + Min: + name: Min + hint: Minimum value + Max: + name: Max + hint: Maximum value report: menu: From e0e3a998a578ba6bcc15fd8037a3b74dc684453c Mon Sep 17 00:00:00 2001 From: Andrey Yatsenco Date: Thu, 3 Dec 2015 13:07:17 +0200 Subject: [PATCH 059/471] BB-1536: Create register page based on new layout - added handling of registartion form --- .../Layout/Form/DependencyInjectionFormAccessor.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Oro/Bundle/LayoutBundle/Layout/Form/DependencyInjectionFormAccessor.php b/src/Oro/Bundle/LayoutBundle/Layout/Form/DependencyInjectionFormAccessor.php index 29589f32252..871fbc2be8c 100644 --- a/src/Oro/Bundle/LayoutBundle/Layout/Form/DependencyInjectionFormAccessor.php +++ b/src/Oro/Bundle/LayoutBundle/Layout/Form/DependencyInjectionFormAccessor.php @@ -67,6 +67,8 @@ public function toString() */ public function setFormData($formData) { - $this->form->setData($formData); + if (!$this->form->isSubmitted()) { + $this->form->setData($formData); + } } } From d7faeaba2c151163e129dcc657b489f45f4a09b8 Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Thu, 3 Dec 2015 13:56:27 +0100 Subject: [PATCH 060/471] BAP-9430: Report builder filters does not contain aggregation columns - prepare backend for filters using aggregated functions --- .../QueryDesigner/AbstractQueryConverter.php | 48 +++++++++++++------ .../GroupingOrmQueryConverter.php | 18 ++++++- 2 files changed, 49 insertions(+), 17 deletions(-) diff --git a/src/Oro/Bundle/QueryDesignerBundle/QueryDesigner/AbstractQueryConverter.php b/src/Oro/Bundle/QueryDesignerBundle/QueryDesigner/AbstractQueryConverter.php index 5b879885abf..198f592ef15 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/QueryDesigner/AbstractQueryConverter.php +++ b/src/Oro/Bundle/QueryDesignerBundle/QueryDesigner/AbstractQueryConverter.php @@ -440,6 +440,32 @@ protected function prepareColumnAliases() } } + /** + * @param array $column + * + * @return array Where array has elements: string|FunctionInterface|null, string|null + */ + protected function createColumnFuncion(array $column) + { + if (!empty($column['func'])) { + $function = $this->functionProvider->getFunction( + $column['func']['name'], + $column['func']['group_name'], + $column['func']['group_type'] + ); + $functionExpr = $function['expr']; + if (isset($function['return_type'])) { + $functionReturnType = $function['return_type']; + } else { + $functionReturnType = null; + } + + return [$functionExpr, $functionReturnType]; + } + + return [null, null]; + } + /** * Performs conversion of SELECT statement */ @@ -448,21 +474,7 @@ protected function addSelectStatement() foreach ($this->definition['columns'] as $column) { $columnName = $column['name']; $fieldName = $this->getFieldName($columnName); - $functionExpr = null; - $functionReturnType = null; - if (!empty($column['func'])) { - $function = $this->functionProvider->getFunction( - $column['func']['name'], - $column['func']['group_name'], - $column['func']['group_type'] - ); - $functionExpr = $function['expr']; - if (isset($function['return_type'])) { - $functionReturnType = $function['return_type']; - } else { - $functionReturnType = null; - } - } + list($functionExpr, $functionReturnType) = $this->createColumnFuncion($column); $isDistinct = !empty($column['distinct']); $tableAlias = $this->getTableAliasForColumn($columnName); if (isset($column['label'])) { @@ -651,6 +663,12 @@ protected function processFilter($filter) $fieldName = $this->getFieldName($columnName); $columnAliasKey = $this->buildColumnAliasKey($columnName); $tableAlias = $this->getTableAliasForColumn($columnName); + $column = ['name' => $fieldName]; + if (isset($filter['func'])) { + $column['func'] = $filter['func']; + } + list($functionExpr) = $this->createColumnFuncion($column); + $this->addWhereCondition( $this->getEntityClassName($columnName), $tableAlias, diff --git a/src/Oro/Bundle/QueryDesignerBundle/QueryDesigner/GroupingOrmQueryConverter.php b/src/Oro/Bundle/QueryDesignerBundle/QueryDesigner/GroupingOrmQueryConverter.php index 9f7a948e2d7..4d5e64167e6 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/QueryDesigner/GroupingOrmQueryConverter.php +++ b/src/Oro/Bundle/QueryDesignerBundle/QueryDesigner/GroupingOrmQueryConverter.php @@ -91,10 +91,24 @@ protected function addWhereCondition( $columnExpr, $columnAlias, $filterName, - array $filterData + array $filterData, + $functionExpr = null ) { $filter = [ - 'column' => $this->getFilterByExpr($entityClassName, $tableAlias, $fieldName, $columnExpr), + 'column' => $this->getFilterByExpr( + $entityClassName, + $tableAlias, + $fieldName, + $functionExpr + ? $this->prepareFunctionExpression( + $functionExpr, + $tableAlias, + $fieldName, + $columnExpr, + $columnAlias + ) + : $columnExpr + ), 'filter' => $filterName, 'filterData' => $filterData ]; From 18a91a65e58e751fe9f4a03ea6f9d0980c9fdadf Mon Sep 17 00:00:00 2001 From: Oleksandr Nechyporenko Date: Thu, 3 Dec 2015 17:44:38 +0200 Subject: [PATCH 061/471] BB-1661: Fix tests - fix failed tests --- .../Extension/ConfigExpressionExtension.php | 4 +- .../ConfigExpressionExtensionTest.php | 127 ++++++++++-------- .../Layout/Tests/Unit/BlockBuilderTest.php | 14 +- .../Tests/Unit/LayoutFactoryBuilderTest.php | 22 ++- .../Layout/Tests/Unit/LayoutTestCase.php | 6 + 5 files changed, 110 insertions(+), 63 deletions(-) diff --git a/src/Oro/Bundle/LayoutBundle/Layout/Block/Extension/ConfigExpressionExtension.php b/src/Oro/Bundle/LayoutBundle/Layout/Block/Extension/ConfigExpressionExtension.php index 58ccf45bb37..cc87bef29c2 100644 --- a/src/Oro/Bundle/LayoutBundle/Layout/Block/Extension/ConfigExpressionExtension.php +++ b/src/Oro/Bundle/LayoutBundle/Layout/Block/Extension/ConfigExpressionExtension.php @@ -41,11 +41,11 @@ public function __construct( */ public function buildBlock(BlockBuilderInterface $builder, array &$options) { - $context = $builder->getContext(); - $data = $builder->getDataAccessor(); + $context = $builder->getContext(); $evaluate = $context->getOr('expressions_evaluate'); $encoding = $context->getOr('expressions_encoding'); if ($evaluate || $encoding !== null) { + $data = $builder->getDataAccessor(); $this->processExpressions($options, $context, $data, $evaluate, $encoding); } } diff --git a/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Block/Extension/ConfigExpressionExtensionTest.php b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Block/Extension/ConfigExpressionExtensionTest.php index f254d71cd41..29ce992450d 100644 --- a/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Block/Extension/ConfigExpressionExtensionTest.php +++ b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Block/Extension/ConfigExpressionExtensionTest.php @@ -4,20 +4,22 @@ use Symfony\Component\PropertyAccess\PropertyPath; +use Oro\Component\ConfigExpression\AssemblerInterface; use Oro\Component\ConfigExpression\Condition; use Oro\Component\ConfigExpression\ContextAccessor; use Oro\Component\ConfigExpression\Func; use Oro\Component\Layout\Block\Type\BaseType; -use Oro\Component\Layout\BlockView; +use Oro\Component\Layout\BlockBuilderInterface; use Oro\Component\Layout\LayoutContext; use Oro\Component\Layout\OptionValueBag; use Oro\Bundle\LayoutBundle\Layout\Block\Extension\ConfigExpressionExtension; +use Oro\Bundle\LayoutBundle\Layout\Encoder\ConfigExpressionEncoderRegistry; use Oro\Bundle\LayoutBundle\Layout\Encoder\JsonConfigExpressionEncoder; class ConfigExpressionExtensionTest extends \PHPUnit_Framework_TestCase { - /** @var \PHPUnit_Framework_MockObject_MockObject */ + /** @var AssemblerInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $expressionAssembler; /** @var ConfigExpressionExtension */ @@ -27,6 +29,7 @@ protected function setUp() { $this->expressionAssembler = $this->getMock('Oro\Component\ConfigExpression\AssemblerInterface'); + /** @var ConfigExpressionEncoderRegistry|\PHPUnit_Framework_MockObject_MockObject $encoderRegistry */ $encoderRegistry = $this ->getMockBuilder('Oro\Bundle\LayoutBundle\Layout\Encoder\ConfigExpressionEncoderRegistry') ->disableOriginalConstructor() @@ -50,19 +53,20 @@ public function testGetExtendedType() /** * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - public function testFinishViewEvaluatesAllExpressions() + public function testBuildBlockEvaluatesAllExpressions() { $context = new LayoutContext(); $context->set('css_class', 'test_class'); $data = $this->getMock('Oro\Component\Layout\DataAccessorInterface'); - $block = $this->getMock('Oro\Component\Layout\BlockInterface'); + + /** @var BlockBuilderInterface|\PHPUnit_Framework_MockObject_MockObject $block */ + $block = $this->getMock('Oro\Component\Layout\BlockBuilderInterface'); $block->expects($this->once()) ->method('getContext') ->will($this->returnValue($context)); $block->expects($this->once()) - ->method('getData') + ->method('getDataAccessor') ->will($this->returnValue($data)); - $view = new BlockView(); $expr = $this->getMock('Oro\Component\ConfigExpression\ExpressionInterface'); $expr->expects($this->once()) @@ -78,16 +82,17 @@ public function testFinishViewEvaluatesAllExpressions() $expectedClassAttr = new OptionValueBag(); $expectedClassAttr->add('test_class'); - $view->vars['expr_object'] = $expr; - $view->vars['expr_array'] = ['@true' => null]; - $view->vars['not_expr_array'] = ['\@true' => null]; - $view->vars['scalar'] = 123; - $view->vars['attr']['enabled'] = ['@true' => null]; - $view->vars['attr']['data-scalar'] = 'foo'; - $view->vars['attr']['data-expr'] = ['@true' => null]; - $view->vars['attr']['class'] = $classAttr; - $view->vars['label_attr']['enabled'] = ['@true' => null]; - $view->vars['array_with_expr'] = ['item1' => 'val1', 'item2' => ['@true' => null]]; + $options = []; + $options['expr_object'] = $expr; + $options['expr_array'] = ['@true' => null]; + $options['not_expr_array'] = ['\@true' => null]; + $options['scalar'] = 123; + $options['attr']['enabled'] = ['@true' => null]; + $options['attr']['data-scalar'] = 'foo'; + $options['attr']['data-expr'] = ['@true' => null]; + $options['attr']['class'] = $classAttr; + $options['label_attr']['enabled'] = ['@true' => null]; + $options['array_with_expr'] = ['item1' => 'val1', 'item2' => ['@true' => null]]; $this->expressionAssembler->expects($this->exactly(6)) ->method('assemble') @@ -101,70 +106,71 @@ public function testFinishViewEvaluatesAllExpressions() ); $context['expressions_evaluate'] = true; - $this->extension->finishView($view, $block, []); + $this->extension->buildBlock($block, $options); $this->assertSame( true, - $view->vars['expr_object'], + $options['expr_object'], 'Failed asserting that an expression is evaluated' ); $this->assertSame( true, - $view->vars['expr_array'], + $options['expr_array'], 'Failed asserting that an expression is assembled and evaluated' ); $this->assertSame( ['@true' => null], - $view->vars['not_expr_array'], + $options['not_expr_array'], 'Failed asserting that a backslash at the begin of the array key is removed' ); $this->assertSame( 123, - $view->vars['scalar'], + $options['scalar'], 'Failed asserting that a scalar value is not changed' ); $this->assertSame( true, - $view->vars['attr']['enabled'], + $options['attr']['enabled'], 'Failed asserting that an expression in "attr" is assembled and evaluated' ); $this->assertSame( 'foo', - $view->vars['attr']['data-scalar'], + $options['attr']['data-scalar'], 'Failed asserting that "attr.data-scalar" exists' ); $this->assertSame( true, - $view->vars['attr']['data-expr'], + $options['attr']['data-expr'], 'Failed asserting that "attr.data-expr" is assembled and evaluated' ); $this->assertEquals( $expectedClassAttr, - $view->vars['attr']['class'], + $options['attr']['class'], 'Failed asserting that "attr.class" is assembled and evaluated' ); $this->assertSame( true, - $view->vars['label_attr']['enabled'], + $options['label_attr']['enabled'], 'Failed asserting that an expression in "label_attr" is assembled and evaluated' ); $this->assertSame( ['item1' => 'val1', 'item2' => true], - $view->vars['array_with_expr'], + $options['array_with_expr'], 'Failed asserting that an expression is assembled and evaluated in nested array' ); } - public function testFinishViewDoNothingIfEvaluationOfExpressionsDisabledAndEncodingIsNotSet() + public function testBuildBlockDoNothingIfEvaluationOfExpressionsDisabledAndEncodingIsNotSet() { $context = new LayoutContext(); - $block = $this->getMock('Oro\Component\Layout\BlockInterface'); + + /** @var BlockBuilderInterface|\PHPUnit_Framework_MockObject_MockObject $block */ + $block = $this->getMock('Oro\Component\Layout\BlockBuilderInterface'); $block->expects($this->once()) ->method('getContext') ->will($this->returnValue($context)); $block->expects($this->never()) - ->method('getData'); - $view = new BlockView(); + ->method('getDataAccessor'); $expr = $this->getMock('Oro\Component\ConfigExpression\ExpressionInterface'); $expr->expects($this->never()) @@ -172,36 +178,38 @@ public function testFinishViewDoNothingIfEvaluationOfExpressionsDisabledAndEncod $expr->expects($this->never()) ->method('toArray'); - $view->vars['expr_object'] = $expr; - $view->vars['expr_array'] = ['@true' => null]; - $view->vars['not_expr_array'] = ['\@true' => null]; - $view->vars['scalar'] = 123; - $view->vars['attr']['enabled'] = ['@true' => null]; - $view->vars['label_attr']['enabled'] = ['@true' => null]; + $options = []; + $options['expr_object'] = $expr; + $options['expr_array'] = ['@true' => null]; + $options['not_expr_array'] = ['\@true' => null]; + $options['scalar'] = 123; + $options['attr']['enabled'] = ['@true' => null]; + $options['label_attr']['enabled'] = ['@true' => null]; $this->expressionAssembler->expects($this->never()) ->method('assemble'); - $initialVars = $view->vars; + $initialVars = $options; $context['expressions_evaluate'] = false; - $this->extension->finishView($view, $block, []); + $this->extension->buildBlock($block, $options); - $this->assertSame($initialVars, $view->vars); + $this->assertSame($initialVars, $options); } - public function testFinishViewEncodesAllExpressions() + public function testBuildBlockEncodesAllExpressions() { $context = new LayoutContext(); $data = $this->getMock('Oro\Component\Layout\DataAccessorInterface'); - $block = $this->getMock('Oro\Component\Layout\BlockInterface'); + + /** @var BlockBuilderInterface|\PHPUnit_Framework_MockObject_MockObject $block */ + $block = $this->getMock('Oro\Component\Layout\BlockBuilderInterface'); $block->expects($this->once()) ->method('getContext') ->will($this->returnValue($context)); $block->expects($this->once()) - ->method('getData') + ->method('getDataAccessor') ->will($this->returnValue($data)); - $view = new BlockView(); $expr = $this->getMock('Oro\Component\ConfigExpression\ExpressionInterface'); $expr->expects($this->once()) @@ -215,13 +223,14 @@ public function testFinishViewEncodesAllExpressions() $expectedClassAttr = new OptionValueBag(); $expectedClassAttr->add('{"@value":{"parameters":["$context.css_class"]}}'); - $view->vars['expr_object'] = $expr; - $view->vars['expr_array'] = ['@true' => null]; - $view->vars['not_expr_array'] = ['\@true' => null]; - $view->vars['scalar'] = 123; - $view->vars['attr']['enabled'] = ['@true' => null]; - $view->vars['attr']['class'] = $classAttr; - $view->vars['label_attr']['enabled'] = ['@true' => null]; + $options = []; + $options['expr_object'] = $expr; + $options['expr_array'] = ['@true' => null]; + $options['not_expr_array'] = ['\@true' => null]; + $options['scalar'] = 123; + $options['attr']['enabled'] = ['@true' => null]; + $options['attr']['class'] = $classAttr; + $options['label_attr']['enabled'] = ['@true' => null]; $this->expressionAssembler->expects($this->exactly(4)) ->method('assemble') @@ -236,41 +245,41 @@ public function testFinishViewEncodesAllExpressions() $context['expressions_evaluate'] = false; $context['expressions_encoding'] = 'json'; - $this->extension->finishView($view, $block, []); + $this->extension->buildBlock($block, $options); $this->assertSame( '{"@true":null}', - $view->vars['expr_object'], + $options['expr_object'], 'Failed asserting that an expression is encoded' ); $this->assertSame( '{"@true":null}', - $view->vars['expr_array'], + $options['expr_array'], 'Failed asserting that an expression is assembled and encoded' ); $this->assertSame( ['@true' => null], - $view->vars['not_expr_array'], + $options['not_expr_array'], 'Failed asserting that a backslash at the begin of the array key is removed' ); $this->assertSame( 123, - $view->vars['scalar'], + $options['scalar'], 'Failed asserting that a scalar value is not changed' ); $this->assertSame( '{"@true":null}', - $view->vars['attr']['enabled'], + $options['attr']['enabled'], 'Failed asserting that an expression in "attr" is assembled and encoded' ); $this->assertEquals( $expectedClassAttr, - $view->vars['attr']['class'], + $options['attr']['class'], 'Failed asserting that "attr.class" is assembled and encoded' ); $this->assertSame( '{"@true":null}', - $view->vars['label_attr']['enabled'], + $options['label_attr']['enabled'], 'Failed asserting that an expression in "label_attr" is assembled and encoded' ); } diff --git a/src/Oro/Component/Layout/Tests/Unit/BlockBuilderTest.php b/src/Oro/Component/Layout/Tests/Unit/BlockBuilderTest.php index 4ee7ce0a53a..8c3b6842578 100644 --- a/src/Oro/Component/Layout/Tests/Unit/BlockBuilderTest.php +++ b/src/Oro/Component/Layout/Tests/Unit/BlockBuilderTest.php @@ -4,8 +4,10 @@ use Oro\Component\Layout\BlockBuilder; use Oro\Component\Layout\BlockTypeHelperInterface; +use Oro\Component\Layout\DataAccessor; use Oro\Component\Layout\LayoutContext; use Oro\Component\Layout\LayoutManipulatorInterface; +use Oro\Component\Layout\LayoutRegistryInterface; use Oro\Component\Layout\RawLayout; class BlockBuilderTest extends \PHPUnit_Framework_TestCase @@ -25,17 +27,27 @@ class BlockBuilderTest extends \PHPUnit_Framework_TestCase /** @var BlockBuilder */ protected $blockBuilder; + /** @var DataAccessor */ + protected $dataAccessor; + + /** @var LayoutRegistryInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $registry; + protected function setUp() { $this->rawLayout = new RawLayout(); $this->typeHelper = $this->getMock('Oro\Component\Layout\BlockTypeHelperInterface'); $this->context = new LayoutContext(); $this->layoutManipulator = $this->getMock('Oro\Component\Layout\LayoutManipulatorInterface'); + $this->registry = $this->getMock('Oro\Component\Layout\LayoutRegistryInterface'); + $this->dataAccessor = new DataAccessor($this->registry, $this->context); + $this->blockBuilder = new BlockBuilder( $this->layoutManipulator, $this->rawLayout, $this->typeHelper, - $this->context + $this->context, + $this->dataAccessor ); } diff --git a/src/Oro/Component/Layout/Tests/Unit/LayoutFactoryBuilderTest.php b/src/Oro/Component/Layout/Tests/Unit/LayoutFactoryBuilderTest.php index 801a6f94cb6..39fbf7847d6 100644 --- a/src/Oro/Component/Layout/Tests/Unit/LayoutFactoryBuilderTest.php +++ b/src/Oro/Component/Layout/Tests/Unit/LayoutFactoryBuilderTest.php @@ -2,7 +2,15 @@ namespace Oro\Component\Layout\Tests\Unit; +use Oro\Component\Layout\BlockBuilderInterface; +use Oro\Component\Layout\BlockTypeExtensionInterface; +use Oro\Component\Layout\BlockTypeInterface; +use Oro\Component\Layout\Extension\ExtensionInterface; use Oro\Component\Layout\LayoutFactoryBuilder; +use Oro\Component\Layout\LayoutItemInterface; +use Oro\Component\Layout\LayoutRendererInterface; +use Oro\Component\Layout\LayoutUpdateInterface; +use Oro\Component\Layout\DeferredLayoutManipulatorInterface; class LayoutFactoryBuilderTest extends \PHPUnit_Framework_TestCase { @@ -22,7 +30,9 @@ public function testGetEmptyLayoutFactory() public function testGetLayoutFactoryWithImplicitSetOfDefaultRenderer() { + /** @var LayoutRendererInterface $renderer1 */ $renderer1 = $this->getMock('Oro\Component\Layout\LayoutRendererInterface'); + /** @var LayoutRendererInterface $renderer2 */ $renderer2 = $this->getMock('Oro\Component\Layout\LayoutRendererInterface'); $this->layoutFactoryBuilder ->addRenderer('renderer1', $renderer1) @@ -46,7 +56,9 @@ public function testGetLayoutFactoryWithImplicitSetOfDefaultRenderer() public function testGetLayoutFactoryWithExplicitSetOfDefaultRenderer() { + /** @var LayoutRendererInterface $renderer1 */ $renderer1 = $this->getMock('Oro\Component\Layout\LayoutRendererInterface'); + /** @var LayoutRendererInterface $renderer2 */ $renderer2 = $this->getMock('Oro\Component\Layout\LayoutRendererInterface'); $this->layoutFactoryBuilder ->addRenderer('renderer1', $renderer1) @@ -74,6 +86,7 @@ public function testAddExtension() $name = 'test'; $type = $this->getMock('Oro\Component\Layout\BlockTypeInterface'); + /** @var ExtensionInterface|\PHPUnit_Framework_MockObject_MockObject $extension */ $extension = $this->getMock('Oro\Component\Layout\Extension\ExtensionInterface'); $layoutFactory = $this->layoutFactoryBuilder ->addExtension($extension) @@ -97,6 +110,7 @@ public function testAddExtension() public function testAddType() { $name = 'test'; + /** @var BlockTypeInterface|\PHPUnit_Framework_MockObject_MockObject $type */ $type = $this->getMock('Oro\Component\Layout\BlockTypeInterface'); $type->expects($this->once()) ->method('getName') @@ -115,10 +129,12 @@ public function testAddType() public function testAddTypeExtension() { $name = 'test'; + /** @var BlockTypeExtensionInterface|\PHPUnit_Framework_MockObject_MockObject $typeExtension */ $typeExtension = $this->getMock('Oro\Component\Layout\BlockTypeExtensionInterface'); $typeExtension->expects($this->once()) ->method('getExtendedType') ->will($this->returnValue($name)); + /** @var BlockBuilderInterface $blockBuilder */ $blockBuilder = $this->getMock('Oro\Component\Layout\BlockBuilderInterface'); $layoutFactory = $this->layoutFactoryBuilder @@ -129,14 +145,18 @@ public function testAddTypeExtension() ->method('buildBlock') ->with($this->identicalTo($blockBuilder), []); - $layoutFactory->getRegistry()->buildBlock($name, $blockBuilder, []); + $options = []; + $layoutFactory->getRegistry()->buildBlock($name, $blockBuilder, $options); } public function testAddLayoutUpdate() { $id = 'test'; + /** @var LayoutUpdateInterface|\PHPUnit_Framework_MockObject_MockObject $layoutUpdate */ $layoutUpdate = $this->getMock('Oro\Component\Layout\LayoutUpdateInterface'); + /** @var DeferredLayoutManipulatorInterface $layoutManipulator */ $layoutManipulator = $this->getMock('Oro\Component\Layout\DeferredLayoutManipulatorInterface'); + /** @var LayoutItemInterface|\PHPUnit_Framework_MockObject_MockObject $layoutItem */ $layoutItem = $this->getMock('Oro\Component\Layout\LayoutItemInterface'); $layoutItem->expects($this->any())->method('getId')->willReturn($id); diff --git a/src/Oro/Component/Layout/Tests/Unit/LayoutTestCase.php b/src/Oro/Component/Layout/Tests/Unit/LayoutTestCase.php index 522c5e4a803..cca934ff430 100644 --- a/src/Oro/Component/Layout/Tests/Unit/LayoutTestCase.php +++ b/src/Oro/Component/Layout/Tests/Unit/LayoutTestCase.php @@ -45,6 +45,12 @@ protected function completeView(array &$view) if (!isset($view['children'])) { $view['children'] = []; } + if (!isset($view['vars']['tag'])) { + $view['vars']['tag'] = null; + } + if (!isset($view['vars']['class_prefix'])) { + $view['vars']['class_prefix'] = null; + } array_walk($view['children'], [$this, 'completeView']); } From c4f520f5437b2451e12e5d4f39f6549497f5048d Mon Sep 17 00:00:00 2001 From: Oleksandr Nechyporenko Date: Thu, 3 Dec 2015 17:55:17 +0200 Subject: [PATCH 062/471] BB-1491: Login and Registration Page Layouts - fix whitespace trim after master merge --- UPGRADE-1.9.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/UPGRADE-1.9.md b/UPGRADE-1.9.md index 4f1d18b20f8..94b7b022345 100644 --- a/UPGRADE-1.9.md +++ b/UPGRADE-1.9.md @@ -32,13 +32,13 @@ UPGRADE FROM 1.8 to 1.9 ####DataGridBundle - Services with tag `oro_datagrid.extension.formatter.property` was marked as private -- JS collection models format changed to maintain compatibility with Backbone collections: now it is always list of models, and additional parameters are passed through the options +- JS collection models format changed to maintain compatibility with Backbone collections: now it is always list of models, and additional parameters are passed through the options ####EmailBundle - Method `setFolder` of `Oro\Bundle\EmailBundle\Entity\EmailUser` marked as deprecated. Use the method `addFolder` instead. - `oro_email.emailtemplate.variable_provider.entity` service was marked as private - `oro_email.emailtemplate.variable_provider.system` service was marked as private -- `oro_email.emailtemplate.variable_provider.user` service was marked as private +- `oro_email.emailtemplate.variable_provider.user` service was marked as private ####EmbeddedFormBundle - Bundle now contains configuration of security firewall `embedded_form` @@ -112,14 +112,14 @@ UPGRADE FROM 1.8 to 1.9 ####SecurityBundle - `Oro\Bundle\SecurityBundle\Owner\OwnerTreeInterface` is changed. New method `buildTree` added (due to performance issues). It should be called once after all `addDeepEntity` calls. See [OwnerTreeProvider](./src/Oro/Bundle/SecurityBundle/Owner/OwnerTreeProvider.php) method `fillTree`. Implementation example [OwnerTree](./src/Oro/Bundle/SecurityBundle/Owner/OwnerTree.php). -- Bundle now contains part of Symfony security configuration (ACL configuration and access decision manager strategy) +- Bundle now contains part of Symfony security configuration (ACL configuration and access decision manager strategy) - `Oro\Bundle\SecurityBundle\Http\Firewall\ContextListener` added to the class cache and constructor have container as performance improvement ####SidebarBundle - `Oro\Bundle\SidebarBundle\EventListener\RequestHandler` added to the class cache as performance improvement ####SoapBundle -- Bundle now contains configuration of security firewall `wsse_secured` +- Bundle now contains configuration of security firewall `wsse_secured` - `Oro\Bundle\SoapBundle\EventListener\LocaleListener` added to the class cache and constructor have container as performance improvement ####TrackingBundle @@ -144,8 +144,8 @@ UPGRADE FROM 1.8 to 1.9 - Constructor of `Oro\Bundle\WorkflowBundle\Model\ProcessFactory` changed. New argument: `ConditionFactory $conditionFactory` - Added new process definition option `pre_conditions` - Class `Oro\Bundle\WorkflowBundle\Model\WorkflowManager` now has method `massTransit` to perform several transitions in one transaction, can be used to improve workflow performance -- Services with tag `oro_workflow.condition` was marked as private -- Services with tag `oro_workflow.action` was marked as private +- Services with tag `oro_workflow.condition` was marked as private +- Services with tag `oro_workflow.action` was marked as private - Route `oro_workflow_api_rest_process_activate` marked as deprecated. Use the route `oro_api_process_activate` instead. - Route `oro_workflow_api_rest_process_deactivate` marked as deprecated. Use the route `oro_api_process_deactivate` instead. - Route `oro_workflow_api_rest_workflowdefinition_get` marked as deprecated. Use the route `oro_api_workflow_definition_get` instead. From cccfe3d75d4a06520662704ff7b352261ecb19e0 Mon Sep 17 00:00:00 2001 From: Oleksandr Nechyporenko Date: Thu, 3 Dec 2015 17:56:10 +0200 Subject: [PATCH 063/471] BB-1491: Login and Registration Page Layouts - fix whitespace trim after master merge --- UPGRADE-1.9.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UPGRADE-1.9.md b/UPGRADE-1.9.md index 94b7b022345..fbf5fe7478d 100644 --- a/UPGRADE-1.9.md +++ b/UPGRADE-1.9.md @@ -33,7 +33,7 @@ UPGRADE FROM 1.8 to 1.9 ####DataGridBundle - Services with tag `oro_datagrid.extension.formatter.property` was marked as private - JS collection models format changed to maintain compatibility with Backbone collections: now it is always list of models, and additional parameters are passed through the options - + ####EmailBundle - Method `setFolder` of `Oro\Bundle\EmailBundle\Entity\EmailUser` marked as deprecated. Use the method `addFolder` instead. - `oro_email.emailtemplate.variable_provider.entity` service was marked as private From c1271586ec34dbb40d8456d13a33da0cb90bd86e Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Fri, 4 Dec 2015 11:02:39 +0100 Subject: [PATCH 064/471] BAP-9430: Report builder filters does not contain aggregation columns - basic functionality --- .../GroupingOrmFilterDatasourceAdapter.php | 8 +- .../QueryDesigner/AbstractQueryConverter.php | 3 +- .../Resources/config/requirejs.yml | 1 + .../public/js/aggregated-field-condition.js | 111 ++++++++++++++++++ .../Resources/translations/messages.en.yml | 1 + ...GroupingOrmFilterDatasourceAdapterTest.php | 40 +++++++ .../Resources/config/requirejs.yml | 1 + .../aggregated-field-condition-extension.js | 58 +++++++++ .../Resources/views/macros.html.twig | 29 ++++- 9 files changed, 248 insertions(+), 4 deletions(-) create mode 100644 src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js create mode 100644 src/Oro/Bundle/SegmentBundle/Resources/public/js/app/components/aggregated-field-condition-extension.js diff --git a/src/Oro/Bundle/QueryDesignerBundle/Grid/Extension/GroupingOrmFilterDatasourceAdapter.php b/src/Oro/Bundle/QueryDesignerBundle/Grid/Extension/GroupingOrmFilterDatasourceAdapter.php index d80687f54f6..43f9c4e5efd 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Grid/Extension/GroupingOrmFilterDatasourceAdapter.php +++ b/src/Oro/Bundle/QueryDesignerBundle/Grid/Extension/GroupingOrmFilterDatasourceAdapter.php @@ -50,7 +50,13 @@ public function __construct(QueryBuilder $qb) public function addRestriction($restriction, $condition, $isComputed = false) { if ($isComputed) { - throw new \LogicException('The HAVING restrictions is not supported yet.'); + if ($condition === FilterUtility::CONDITION_OR) { + $this->qb->orHaving($restriction); + } else { + $this->qb->andHaving($restriction); + } + + return; } if ($this->currentExpr === null) { diff --git a/src/Oro/Bundle/QueryDesignerBundle/QueryDesigner/AbstractQueryConverter.php b/src/Oro/Bundle/QueryDesignerBundle/QueryDesigner/AbstractQueryConverter.php index 198f592ef15..539cb46536f 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/QueryDesigner/AbstractQueryConverter.php +++ b/src/Oro/Bundle/QueryDesignerBundle/QueryDesigner/AbstractQueryConverter.php @@ -676,7 +676,8 @@ protected function processFilter($filter) $this->buildColumnExpression($columnName, $tableAlias, $fieldName), $this->getColumnAlias($columnAliasKey), $filter['criterion']['filter'], - $filter['criterion']['data'] + $filter['criterion']['data'], + $functionExpr ); } diff --git a/src/Oro/Bundle/QueryDesignerBundle/Resources/config/requirejs.yml b/src/Oro/Bundle/QueryDesignerBundle/Resources/config/requirejs.yml index a9517905e07..9c997dc7b5c 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Resources/config/requirejs.yml +++ b/src/Oro/Bundle/QueryDesignerBundle/Resources/config/requirejs.yml @@ -2,6 +2,7 @@ config: paths: 'oroquerydesigner/js/condition-builder': 'bundles/oroquerydesigner/js/condition-builder.js' 'oroquerydesigner/js/field-condition': 'bundles/oroquerydesigner/js/field-condition.js' + 'oroquerydesigner/js/aggregated-field-condition': 'bundles/oroquerydesigner/js/aggregated-field-condition.js' 'oroquerydesigner/js/function-choice': 'bundles/oroquerydesigner/js/function-choice.js' 'oroquerydesigner/js/items-manager/grouping-model': 'bundles/oroquerydesigner/js/items-manager/grouping-model.js' 'oroquerydesigner/js/items-manager/column-model': 'bundles/oroquerydesigner/js/items-manager/column-model.js' diff --git a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js new file mode 100644 index 00000000000..ab467a2ec3b --- /dev/null +++ b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js @@ -0,0 +1,111 @@ +define([ + 'jquery', + 'underscore', + 'oroquerydesigner/js/field-condition' +], function ($, _) { + $.widget('oroauditquerydesigner.aggregatedFieldCondition', $.oroquerydesigner.fieldCondition, { + options: { + columnsCollection: null + }, + + _create: function() { + var data = this.element.data('value'); + + this.$fieldChoice = $('').addClass(this.options.fieldChoiceClass); + this.$filterContainer = $('').addClass(this.options.filterContainerClass); + this.element.append(this.$fieldChoice, this.$filterContainer); + + this.$fieldChoice.fieldChoice(this.options.fieldChoice); + + this._updateFieldChoice(); + if (data && data.columnName) { + this.selectField(data.columnName); + this._renderFilter(data.columnName); + } + + this._on(this.$fieldChoice, { + changed: function(e, fieldId) { + $(':focus').blur(); + // reset current value on field change + this.element.data('value', {}); + this._renderFilter(fieldId); + e.stopPropagation(); + } + }); + + this._on(this.$filterContainer, { + change: function() { + if (this.filter) { + this.filter.applyValue(); + } + } + }); + }, + + _updateFieldChoice: function() { + var self = this; + var fieldChoice = this.$fieldChoice.fieldChoice().data('oroentity-fieldChoice'); + var originalSelect2Data = fieldChoice._select2Data; + + fieldChoice._select2Data = function(path) { + originalSelect2Data.apply(this, arguments); + + return self._getAggregatedSelectData(); + }; + }, + + _getAggregatedSelectData: function() { + return _.map( + this._getAggregatedColumns(), + function (model) { + return { + id: model.get('name'), + text: model.get('label') + }; + } + ); + }, + + _getAggregatedColumns: function() { + return _.filter( + this.options.columnsCollection.models, + _.compose(_.negate(_.isEmpty), _.property('func'), _.property('attributes')) + ); + }, + + _onUpdate: function() { + var value; + var columnName = this.element.find('input.select').select2('val'); + var columnFunc = this._getColumnFunc(columnName); + + if (this.filter && !this.filter.isEmptyValue() && !_.isEmpty(columnFunc)) { + value = { + columnName: columnName, + criterion: this._getFilterCriterion(), + func: columnFunc + }; + } else { + value = {}; + } + + this.element.data('value', value); + this.element.trigger('changed'); + }, + + _getColumnFunc: function(columnName) { + var column = this.options.columnsCollection.findWhere({name: columnName}); + if (_.isEmpty(column)) { + return; + } + + return column.get('func'); + }, + + _getFilterCriterion: function() { + var criterion = this._superApply(arguments); + $.extend(true, criterion, {'data': {'params': {'filter_by_having': true}}}); + + return criterion; + } + }); +}); diff --git a/src/Oro/Bundle/QueryDesignerBundle/Resources/translations/messages.en.yml b/src/Oro/Bundle/QueryDesignerBundle/Resources/translations/messages.en.yml index 1197fd0a151..37c3fc4ced7 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Resources/translations/messages.en.yml +++ b/src/Oro/Bundle/QueryDesignerBundle/Resources/translations/messages.en.yml @@ -34,6 +34,7 @@ oro: criteria: drag_hint: drag to select field_condition: Field Condition + aggregated_field_condition: Aggregation column conditions_group: Conditions Group choose_entity_field: Choose a field... validation: diff --git a/src/Oro/Bundle/QueryDesignerBundle/Tests/Unit/Grid/Extension/GroupingOrmFilterDatasourceAdapterTest.php b/src/Oro/Bundle/QueryDesignerBundle/Tests/Unit/Grid/Extension/GroupingOrmFilterDatasourceAdapterTest.php index dd510acb10d..651983a0520 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Tests/Unit/Grid/Extension/GroupingOrmFilterDatasourceAdapterTest.php +++ b/src/Oro/Bundle/QueryDesignerBundle/Tests/Unit/Grid/Extension/GroupingOrmFilterDatasourceAdapterTest.php @@ -45,6 +45,25 @@ public function testOneRestriction() ); } + public function testOneComputedRestriction() + { + $qb = new QueryBuilder($this->getTestEntityManager()); + $qb->select(['u.status, COUNT(u.id)']) + ->from('Oro\Bundle\QueryDesignerBundle\Tests\Unit\Fixtures\Models\CMS\CmsUser', 'u') + ->groupBy('u.status'); + $ds = new GroupingOrmFilterDatasourceAdapter($qb); + + $ds->addRestriction($qb->expr()->eq('COUNT(u.id)', '1'), FilterUtility::CONDITION_AND, true); + $ds->applyRestrictions(); + + $this->assertEquals( + 'SELECT u.status, COUNT(u.id) FROM Oro\Bundle\QueryDesignerBundle\Tests\Unit\Fixtures\Models\CMS\CmsUser u ' + . 'GROUP BY u.status ' + . 'HAVING COUNT(u.id) = 1', + $qb->getDQL() + ); + } + public function testSeveralRestrictions() { $qb = new QueryBuilder($this->getTestEntityManager()); @@ -66,6 +85,27 @@ public function testSeveralRestrictions() ); } + public function testSeveralComputedRestrictions() + { + $qb = new QueryBuilder($this->getTestEntityManager()); + $qb->select(['u.status, COUNT(u.id)']) + ->from('Oro\Bundle\QueryDesignerBundle\Tests\Unit\Fixtures\Models\CMS\CmsUser', 'u') + ->groupBy('u.status'); + $ds = new GroupingOrmFilterDatasourceAdapter($qb); + + $ds->addRestriction($qb->expr()->eq('COUNT(u.id)', '1'), FilterUtility::CONDITION_AND, true); + $ds->addRestriction($qb->expr()->eq('MIN(u.id)', '2'), FilterUtility::CONDITION_OR, true); + $ds->addRestriction($qb->expr()->eq('MAX(u.id)', '3'), FilterUtility::CONDITION_AND, true); + $ds->applyRestrictions(); + + $this->assertEquals( + 'SELECT u.status, COUNT(u.id) FROM Oro\Bundle\QueryDesignerBundle\Tests\Unit\Fixtures\Models\CMS\CmsUser u ' + . 'GROUP BY u.status ' + . 'HAVING (COUNT(u.id) = 1 OR MIN(u.id) = 2) AND MAX(u.id) = 3', + $qb->getDQL() + ); + } + public function testEmptyGroup() { $qb = new QueryBuilder($this->getTestEntityManager()); diff --git a/src/Oro/Bundle/SegmentBundle/Resources/config/requirejs.yml b/src/Oro/Bundle/SegmentBundle/Resources/config/requirejs.yml index b3f24523396..81115c25ea1 100644 --- a/src/Oro/Bundle/SegmentBundle/Resources/config/requirejs.yml +++ b/src/Oro/Bundle/SegmentBundle/Resources/config/requirejs.yml @@ -2,3 +2,4 @@ config: paths: 'orosegment/js/app/components/refresh-button': 'bundles/orosegment/js/app/components/refresh-button.js' 'orosegment/js/app/components/segment-component': 'bundles/orosegment/js/app/components/segment-component.js' + 'orosegment/js/app/components/aggregated-field-condition-extension': 'bundles/orosegment/js/app/components/aggregated-field-condition-extension.js' diff --git a/src/Oro/Bundle/SegmentBundle/Resources/public/js/app/components/aggregated-field-condition-extension.js b/src/Oro/Bundle/SegmentBundle/Resources/public/js/app/components/aggregated-field-condition-extension.js new file mode 100644 index 00000000000..6d9d37d93fb --- /dev/null +++ b/src/Oro/Bundle/SegmentBundle/Resources/public/js/app/components/aggregated-field-condition-extension.js @@ -0,0 +1,58 @@ +define([ + 'jquery', + 'underscore' +], function($, _) { + 'use strict'; + + return { + load: function(segment) { + segment.defaults = $.extend(true, segment.defaults, { + defaults: { + aggregatedFieldsLoader: { + loadingMaskParent: '', + router: null, + routingParams: {}, + fieldsData: [], + confirmMessage: '', + loadEvent: 'aggregatedFieldsLoaded' + } + } + }); + + segment.configureFilters = _.wrap(segment.configureFilters, function(original) { + var $criteria = $(this.options.filters.criteriaList); + + var aggregatedFieldCondition = $criteria.find('[data-criteria=aggregated-condition-item]'); + if (!_.isEmpty(aggregatedFieldCondition)) { + var $itemContainer = $(this.options.column.itemContainer); + var columnsCollection = $itemContainer.data('oroui-itemsManagerTable').options.collection; + + $.extend(true, aggregatedFieldCondition.data('options'), { + fieldChoice: this.options.fieldChoiceOptions, + filters: this.options.metadata.filters, + hierarchy: this.options.metadata.hierarchy, + columnsCollection: columnsCollection + }); + } + + original.apply(this, _.rest(arguments)); + }); + + segment.initFieldsLoader = _.wrap(segment.initFieldsLoader, function(original) { + this.$aggregatedFieldsLoader = original.call(this, this.options.aggregatedFieldsLoader); + + return original.apply(this, _.rest(arguments)); + }); + + segment.initEntityChangeEvents = _.wrap(segment.initEntityChangeEvents, function(original) { + this.trigger( + this.options.aggregatedFieldsLoader.loadEvent, + this.$aggregatedFieldsLoader.val(), + this.$aggregatedFieldsLoader.fieldsLoader('getFieldsData') + ); + + return original.apply(this, _.rest(arguments)); + }); + } + }; +}); diff --git a/src/Oro/Bundle/SegmentBundle/Resources/views/macros.html.twig b/src/Oro/Bundle/SegmentBundle/Resources/views/macros.html.twig index 6eff3740499..8e23e674fad 100644 --- a/src/Oro/Bundle/SegmentBundle/Resources/views/macros.html.twig +++ b/src/Oro/Bundle/SegmentBundle/Resources/views/macros.html.twig @@ -7,6 +7,14 @@ fieldsLoaderSelector: '[data-ftid=' ~ params.entity_choice_id ~ 'oro_api_querydesigner_fields_entity]' } } %} + {% set aggregatedFieldConditionOptions = { + fieldChoice: { + select2: { + placeholder: 'oro.query_designer.condition_builder.choose_entity_field'|trans + }, + fieldsLoaderSelector: '#' ~ params.entity_choice_id ~ 'oro_api_querydesigner_aggregated_fields', + } + } %} {% set segmentConditionOptions = { segmentChoice: { select2: { @@ -25,7 +33,7 @@ criteriaListSelector: '#' ~ params.criteria_list_id, entityChoiceSelector: '[data-ftid=' ~ params.entity_choice_id ~ ']', onFieldsUpdate: { - toggleCriteria: ['condition-item', 'condition-segment', 'conditions-group'] + toggleCriteria: ['condition-item', 'condition-segment', 'conditions-group', 'aggregated-condition-item'] } } %} {% set conditionBuilderOptions = update_segment_condition_builder_options(conditionBuilderOptions) %} @@ -41,6 +49,12 @@ data-options="{{ fieldConditionOptions|json_encode }}"> {{ 'oro.query_designer.condition_builder.criteria.field_condition'|trans }} +
        • + {{ 'oro.query_designer.condition_builder.criteria.aggregated_field_condition'|trans }} +
        • Date: Fri, 4 Dec 2015 12:15:48 +0100 Subject: [PATCH 065/471] BAP-9430: Report builder filters does not contain aggregation columns - include aggregation column filter via extension --- .../EventListener/SegmentSubscriber.php | 69 +++++++++++++++++++ .../Resources/config/placeholders.yml | 9 +++ .../Resources/config/services.yml | 6 ++ .../aggregated_field_condition.html.twig | 15 ++++ .../Event/WidgetOptionsLoadEvent.php | 15 +++- .../Resources/views/macros.html.twig | 31 +-------- .../SegmentBundle/Twig/SegmentExtension.php | 5 +- .../Placeholder/PlaceholderFilter.php | 11 +++ .../Config/Resolver/SystemAwareResolver.php | 49 ++++++++++--- 9 files changed, 170 insertions(+), 40 deletions(-) create mode 100644 src/Oro/Bundle/ReportBundle/EventListener/SegmentSubscriber.php create mode 100644 src/Oro/Bundle/ReportBundle/Resources/config/placeholders.yml create mode 100644 src/Oro/Bundle/ReportBundle/Resources/views/Segment/aggregated_field_condition.html.twig diff --git a/src/Oro/Bundle/ReportBundle/EventListener/SegmentSubscriber.php b/src/Oro/Bundle/ReportBundle/EventListener/SegmentSubscriber.php new file mode 100644 index 00000000000..ab727b29a60 --- /dev/null +++ b/src/Oro/Bundle/ReportBundle/EventListener/SegmentSubscriber.php @@ -0,0 +1,69 @@ + 'loadAggregatedFieldsWidgetOptions', + ConditionBuilderOptionsLoadEvent::EVENT_NAME => 'loadAggregatedFieldsBuilderOptions', + ]; + } + + /** + * @param WidgetOptionsLoadEvent $event + */ + public function loadAggregatedFieldsWidgetOptions(WidgetOptionsLoadEvent $event) + { + if ($event->getWidgetType() !== 'oro_report') { + return; + } + + $widgetOptions = $event->getWidgetOptions(); + $fieldsLoader = $widgetOptions['fieldsLoader']; + + $event->setWidgetOptions(array_merge_recursive( + $widgetOptions, + [ + 'aggregatedFieldsLoader' => [ + 'entityChoice' => $fieldsLoader['entityChoice'], + 'loadingMaskParent' => $fieldsLoader['loadingMaskParent'], + 'router' => 'oro_api_querydesigner_aggregated_fields', + 'routingParams' => [], + 'fieldsData' => $fieldsLoader['fieldsData'], + 'confirmMessage' => $fieldsLoader['confirmMessage'], + ], + 'extensions' => [ + 'orosegment/js/app/components/aggregated-field-condition-extension', + ], + ] + )); + } + + /** + * @param ConditionBuilderOptionsLoadEvent $event + */ + public function loadAggregatedFieldsBuilderOptions(ConditionBuilderOptionsLoadEvent $event) + { + $event->setOptions(array_merge_recursive( + $event->getOptions(), + [ + 'onFieldsUpdate' => [ + 'toggleCriteria' => [ + 'aggregated-condition-item', + ], + ], + ] + )); + } +} diff --git a/src/Oro/Bundle/ReportBundle/Resources/config/placeholders.yml b/src/Oro/Bundle/ReportBundle/Resources/config/placeholders.yml new file mode 100644 index 00000000000..f5863f42487 --- /dev/null +++ b/src/Oro/Bundle/ReportBundle/Resources/config/placeholders.yml @@ -0,0 +1,9 @@ +placeholders: + segment_criteria_list: + items: + aggregated_field_condition: ~ + +items: + aggregated_field_condition: + template: OroReportBundle:Segment:aggregated_field_condition.html.twig + applicable: @oro_ui.placeholder.filter->isSame($params.id$, oro_report-condition-builder) diff --git a/src/Oro/Bundle/ReportBundle/Resources/config/services.yml b/src/Oro/Bundle/ReportBundle/Resources/config/services.yml index e034476d3f4..0ea72443c82 100644 --- a/src/Oro/Bundle/ReportBundle/Resources/config/services.yml +++ b/src/Oro/Bundle/ReportBundle/Resources/config/services.yml @@ -4,6 +4,7 @@ parameters: oro_report.listener.navigation_listener.class: Oro\Bundle\ReportBundle\EventListener\NavigationListener oro_report.report_manager.class: Oro\Bundle\ReportBundle\Entity\Manager\ReportManager oro_report.grid.base_configuration_builder.class: Oro\Bundle\ReportBundle\Grid\BaseReportConfigurationBuilder + oro_report.listener.segment_subscriber.class: Oro\Bundle\ReportBundle\EventListener\SegmentSubscriber oro_report.grid.datagrid_configuration_builder.class: Oro\Bundle\ReportBundle\Grid\ReportDatagridConfigurationBuilder services: @@ -42,3 +43,8 @@ services: oro_report.grid.datagrid_configuration_builder: class: %oro_report.grid.datagrid_configuration_builder.class% parent: oro_report.grid.base_configuration_builder + + oro_report.listener.segment_subscriber: + class: %oro_report.listener.segment_subscriber.class% + tags: + - { name: kernel.event_subscriber } diff --git a/src/Oro/Bundle/ReportBundle/Resources/views/Segment/aggregated_field_condition.html.twig b/src/Oro/Bundle/ReportBundle/Resources/views/Segment/aggregated_field_condition.html.twig new file mode 100644 index 00000000000..540efb11502 --- /dev/null +++ b/src/Oro/Bundle/ReportBundle/Resources/views/Segment/aggregated_field_condition.html.twig @@ -0,0 +1,15 @@ +{% set aggregatedFieldConditionOptions = { + fieldChoice: { + select2: { + placeholder: 'oro.query_designer.condition_builder.choose_entity_field'|trans + }, + fieldsLoaderSelector: '#' ~ params.entity_choice_id ~ 'oro_api_querydesigner_aggregated_fields', + } +} %} + +
        • + {{ 'oro.query_designer.condition_builder.criteria.aggregated_field_condition'|trans }} +
        • diff --git a/src/Oro/Bundle/SegmentBundle/Event/WidgetOptionsLoadEvent.php b/src/Oro/Bundle/SegmentBundle/Event/WidgetOptionsLoadEvent.php index 511b33e6e5f..ea9cbfda306 100644 --- a/src/Oro/Bundle/SegmentBundle/Event/WidgetOptionsLoadEvent.php +++ b/src/Oro/Bundle/SegmentBundle/Event/WidgetOptionsLoadEvent.php @@ -11,12 +11,17 @@ class WidgetOptionsLoadEvent extends Event /** @var array */ protected $widgetOptions; + /** @var string|null */ + protected $type; + /** * @param array $widgetOptions + * @param string|null $type */ - public function __construct(array $widgetOptions) + public function __construct(array $widgetOptions, $type = null) { $this->widgetOptions = $widgetOptions; + $this->type = $type; } /** @@ -34,4 +39,12 @@ public function setWidgetOptions(array $widgetOptions) { $this->widgetOptions = $widgetOptions; } + + /** + * @return string|null + */ + public function getWidgetType() + { + return $this->type; + } } diff --git a/src/Oro/Bundle/SegmentBundle/Resources/views/macros.html.twig b/src/Oro/Bundle/SegmentBundle/Resources/views/macros.html.twig index 8e23e674fad..3dd8ec2a38b 100644 --- a/src/Oro/Bundle/SegmentBundle/Resources/views/macros.html.twig +++ b/src/Oro/Bundle/SegmentBundle/Resources/views/macros.html.twig @@ -7,14 +7,6 @@ fieldsLoaderSelector: '[data-ftid=' ~ params.entity_choice_id ~ 'oro_api_querydesigner_fields_entity]' } } %} - {% set aggregatedFieldConditionOptions = { - fieldChoice: { - select2: { - placeholder: 'oro.query_designer.condition_builder.choose_entity_field'|trans - }, - fieldsLoaderSelector: '#' ~ params.entity_choice_id ~ 'oro_api_querydesigner_aggregated_fields', - } - } %} {% set segmentConditionOptions = { segmentChoice: { select2: { @@ -33,7 +25,7 @@ criteriaListSelector: '#' ~ params.criteria_list_id, entityChoiceSelector: '[data-ftid=' ~ params.entity_choice_id ~ ']', onFieldsUpdate: { - toggleCriteria: ['condition-item', 'condition-segment', 'conditions-group', 'aggregated-condition-item'] + toggleCriteria: ['condition-item', 'condition-segment', 'conditions-group'] } } %} {% set conditionBuilderOptions = update_segment_condition_builder_options(conditionBuilderOptions) %} @@ -49,12 +41,6 @@ data-options="{{ fieldConditionOptions|json_encode }}"> {{ 'oro.query_designer.condition_builder.criteria.field_condition'|trans }} -
        • - {{ 'oro.query_designer.condition_builder.criteria.aggregated_field_condition'|trans }} -
        • diff --git a/src/Oro/Bundle/SegmentBundle/Twig/SegmentExtension.php b/src/Oro/Bundle/SegmentBundle/Twig/SegmentExtension.php index ed15ca4c5e5..37ed62dfa28 100644 --- a/src/Oro/Bundle/SegmentBundle/Twig/SegmentExtension.php +++ b/src/Oro/Bundle/SegmentBundle/Twig/SegmentExtension.php @@ -41,16 +41,17 @@ public function getFunctions() /** * @param array $widgetOptions + * @param string|null $type * * @return array */ - public function updateSegmentWidgetOptions(array $widgetOptions) + public function updateSegmentWidgetOptions(array $widgetOptions, $type = null) { if (!$this->dispatcher->hasListeners(WidgetOptionsLoadEvent::EVENT_NAME)) { return $widgetOptions; } - $event = new WidgetOptionsLoadEvent($widgetOptions); + $event = new WidgetOptionsLoadEvent($widgetOptions, $type); $this->dispatcher->dispatch(WidgetOptionsLoadEvent::EVENT_NAME, $event); return $event->getWidgetOptions(); diff --git a/src/Oro/Bundle/UIBundle/Placeholder/PlaceholderFilter.php b/src/Oro/Bundle/UIBundle/Placeholder/PlaceholderFilter.php index 203d1ca6c14..b897b6814ca 100644 --- a/src/Oro/Bundle/UIBundle/Placeholder/PlaceholderFilter.php +++ b/src/Oro/Bundle/UIBundle/Placeholder/PlaceholderFilter.php @@ -4,6 +4,17 @@ class PlaceholderFilter { + /** + * @param mixed $param1 + * @param mixed $param2 + * + * @return bool + */ + public function isSame($param1, $param2) + { + return $param1 === $param2; + } + /** * Checks the object is an instance of a given class. * diff --git a/src/Oro/Component/Config/Resolver/SystemAwareResolver.php b/src/Oro/Component/Config/Resolver/SystemAwareResolver.php index e7aed5297de..3c7972561fa 100644 --- a/src/Oro/Component/Config/Resolver/SystemAwareResolver.php +++ b/src/Oro/Component/Config/Resolver/SystemAwareResolver.php @@ -4,22 +4,24 @@ use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\PropertyAccess\PropertyAccessorInterface; + +use Oro\Component\PropertyAccess\PropertyAccessor; class SystemAwareResolver implements ResolverInterface, ContainerAwareInterface { const PARENT_NODE = 'parent_node'; const NODE_KEY = 'node_key'; - /** - * @var ContainerInterface - */ + /** @var ContainerInterface */ protected $container; - /** - * @var array - */ + /** @var array */ protected $context; + /** @var PropertyAccessorInterface|null */ + protected $propertyAccessor; + /** * @param ContainerInterface $container */ @@ -121,9 +123,14 @@ protected function getMethodCallParameters($declaration) if ($this->startsWith($item, '$') && $this->endsWith($item, '$')) { $name = substr($item, 1, -1); - $item = (isset($this->context[$name]) || array_key_exists($name, $this->context)) - ? $this->context[$name] - : null; + $dot = strpos($name, '.'); + $objectName = $dot ? substr($name, 0, $dot) : $name; + $item = $this->getContextValue($objectName); + + if ($dot) { + $propertyPath = substr($name, $dot + 1); + $item = $this->getPropertyAccessor()->getValue($item, $propertyPath); + } } elseif ($this->startsWith($item, '%') && $this->endsWith($item, '%')) { $name = substr($item, 1, -1); $item = $this->getParameter($name); @@ -135,6 +142,18 @@ protected function getMethodCallParameters($declaration) return $result; } + /** + * @param string $name + * + * @return mixed + */ + protected function getContextValue($name) + { + return (isset($this->context[$name]) || array_key_exists($name, $this->context)) + ? $this->context[$name] + : null; + } + /** * @param string $name * @return mixed @@ -281,4 +300,16 @@ protected function resolveService($val) return $val; } + + /** + * @return PropertyAccessorInterface + */ + protected function getPropertyAccessor() + { + if (!$this->propertyAccessor) { + $this->propertyAccessor = new PropertyAccessor(); + } + + return $this->propertyAccessor; + } } From 40436716260e70be7e0124cc9aeac63a867c422a Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Fri, 4 Dec 2015 12:21:23 +0100 Subject: [PATCH 066/471] BAP-9430: Report builder filters does not contain aggregation columns - cs fixes --- .../QueryDesigner/GroupingOrmQueryConverter.php | 16 ++++++++-------- .../public/js/aggregated-field-condition.js | 6 ++++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/Oro/Bundle/QueryDesignerBundle/QueryDesigner/GroupingOrmQueryConverter.php b/src/Oro/Bundle/QueryDesignerBundle/QueryDesigner/GroupingOrmQueryConverter.php index 4d5e64167e6..344b8c20f20 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/QueryDesigner/GroupingOrmQueryConverter.php +++ b/src/Oro/Bundle/QueryDesignerBundle/QueryDesigner/GroupingOrmQueryConverter.php @@ -100,14 +100,14 @@ protected function addWhereCondition( $tableAlias, $fieldName, $functionExpr - ? $this->prepareFunctionExpression( - $functionExpr, - $tableAlias, - $fieldName, - $columnExpr, - $columnAlias - ) - : $columnExpr + ? $this->prepareFunctionExpression( + $functionExpr, + $tableAlias, + $fieldName, + $columnExpr, + $columnAlias + ) + : $columnExpr ), 'filter' => $filterName, 'filterData' => $filterData diff --git a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js index ab467a2ec3b..ace264b3f88 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js +++ b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js @@ -2,7 +2,9 @@ define([ 'jquery', 'underscore', 'oroquerydesigner/js/field-condition' -], function ($, _) { +], function($, _) { + 'use strict'; + $.widget('oroauditquerydesigner.aggregatedFieldCondition', $.oroquerydesigner.fieldCondition, { options: { columnsCollection: null @@ -57,7 +59,7 @@ define([ _getAggregatedSelectData: function() { return _.map( this._getAggregatedColumns(), - function (model) { + function(model) { return { id: model.get('name'), text: model.get('label') From 5ef58b687eea2405faa63292eeccb5aa6d222545 Mon Sep 17 00:00:00 2001 From: Andrii Muzalevskyi Date: Fri, 4 Dec 2015 13:26:54 +0200 Subject: [PATCH 067/471] BAP-9304 Date have border around similar values --- .../Resources/public/js/jquery-ui-datepicker-l10n.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Oro/Bundle/UIBundle/Resources/public/js/jquery-ui-datepicker-l10n.js b/src/Oro/Bundle/UIBundle/Resources/public/js/jquery-ui-datepicker-l10n.js index 80971a5437b..3c1b354cd67 100644 --- a/src/Oro/Bundle/UIBundle/Resources/public/js/jquery-ui-datepicker-l10n.js +++ b/src/Oro/Bundle/UIBundle/Resources/public/js/jquery-ui-datepicker-l10n.js @@ -76,10 +76,13 @@ define(function(require) { .find('a').removeClass('ui-state-highlight'); if (inst.drawYear === today.year() && inst.drawMonth === today.month()) { - // highlighted today date in system tumezone + // highlighted today date in system timezone inst.dpDiv - .find('td > a.ui-state-default:contains("' + today.date() + '")').addClass('ui-state-highlight') - .parent().addClass('ui-datepicker-today'); + .find('td > a.ui-state-default').each(function() { + if (today.date().toString() === this.innerHTML) { + $(this).addClass('ui-state-highlight').parent().addClass('ui-datepicker-today'); + } + }); } }; })(); From 31c1a2f6487b60f53c6aef75deff98d441d931ea Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Fri, 4 Dec 2015 12:31:00 +0100 Subject: [PATCH 068/471] BAP-9430: Report builder filters does not contain aggregation columns - correct order of filters --- .../Resources/config/placeholders.yml | 3 ++- .../Resources/config/placeholders.yml | 8 ++++++++ .../views/Segment/field_condition.html.twig | 15 +++++++++++++++ .../Resources/views/macros.html.twig | 14 -------------- 4 files changed, 25 insertions(+), 15 deletions(-) create mode 100644 src/Oro/Bundle/SegmentBundle/Resources/config/placeholders.yml create mode 100644 src/Oro/Bundle/SegmentBundle/Resources/views/Segment/field_condition.html.twig diff --git a/src/Oro/Bundle/ReportBundle/Resources/config/placeholders.yml b/src/Oro/Bundle/ReportBundle/Resources/config/placeholders.yml index f5863f42487..f07a4d54a81 100644 --- a/src/Oro/Bundle/ReportBundle/Resources/config/placeholders.yml +++ b/src/Oro/Bundle/ReportBundle/Resources/config/placeholders.yml @@ -1,7 +1,8 @@ placeholders: segment_criteria_list: items: - aggregated_field_condition: ~ + aggregated_field_condition: + order: 10 items: aggregated_field_condition: diff --git a/src/Oro/Bundle/SegmentBundle/Resources/config/placeholders.yml b/src/Oro/Bundle/SegmentBundle/Resources/config/placeholders.yml new file mode 100644 index 00000000000..6ee0b46990a --- /dev/null +++ b/src/Oro/Bundle/SegmentBundle/Resources/config/placeholders.yml @@ -0,0 +1,8 @@ +placeholders: + segment_criteria_list: + items: + field_condition: ~ + +items: + field_condition: + template: OroSegmentBundle:Segment:field_condition.html.twig diff --git a/src/Oro/Bundle/SegmentBundle/Resources/views/Segment/field_condition.html.twig b/src/Oro/Bundle/SegmentBundle/Resources/views/Segment/field_condition.html.twig new file mode 100644 index 00000000000..26aebb9bc61 --- /dev/null +++ b/src/Oro/Bundle/SegmentBundle/Resources/views/Segment/field_condition.html.twig @@ -0,0 +1,15 @@ +{% set fieldConditionOptions = { + fieldChoice: { + select2: { + placeholder: 'oro.query_designer.condition_builder.choose_entity_field'|trans + }, + fieldsLoaderSelector: '[data-ftid=' ~ params.entity_choice_id ~ 'oro_api_querydesigner_fields_entity]' + } +} %} + +
        • + {{ 'oro.query_designer.condition_builder.criteria.field_condition'|trans }} +
        • diff --git a/src/Oro/Bundle/SegmentBundle/Resources/views/macros.html.twig b/src/Oro/Bundle/SegmentBundle/Resources/views/macros.html.twig index 3dd8ec2a38b..c3096497062 100644 --- a/src/Oro/Bundle/SegmentBundle/Resources/views/macros.html.twig +++ b/src/Oro/Bundle/SegmentBundle/Resources/views/macros.html.twig @@ -1,12 +1,4 @@ {% macro query_designer_condition_builder(params) %} - {% set fieldConditionOptions = { - fieldChoice: { - select2: { - placeholder: 'oro.query_designer.condition_builder.choose_entity_field'|trans - }, - fieldsLoaderSelector: '[data-ftid=' ~ params.entity_choice_id ~ 'oro_api_querydesigner_fields_entity]' - } - } %} {% set segmentConditionOptions = { segmentChoice: { select2: { @@ -35,12 +27,6 @@
          {{ 'oro.query_designer.condition_builder.criteria.drag_hint'|trans }}
            {% placeholder segment_criteria_list with {params: params} %} -
          • - {{ 'oro.query_designer.condition_builder.criteria.field_condition'|trans }} -
          • Date: Fri, 4 Dec 2015 12:57:31 +0100 Subject: [PATCH 069/471] BAP-9430: Report builder filters does not contain aggregation columns - remove aggregated field condition when aggregated column is removed --- .../Resources/public/js/aggregated-field-condition.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js index ace264b3f88..3330d8ee565 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js +++ b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js @@ -20,6 +20,11 @@ define([ this.$fieldChoice.fieldChoice(this.options.fieldChoice); this._updateFieldChoice(); + this.options.columnsCollection.on('remove', function(model) { + if (model.get('name') === this._getColumnName()) { + this.element.closest('.condition').find('.close').click(); + } + }, this); if (data && data.columnName) { this.selectField(data.columnName); this._renderFilter(data.columnName); @@ -77,7 +82,7 @@ define([ _onUpdate: function() { var value; - var columnName = this.element.find('input.select').select2('val'); + var columnName = this._getColumnName(); var columnFunc = this._getColumnFunc(columnName); if (this.filter && !this.filter.isEmptyValue() && !_.isEmpty(columnFunc)) { @@ -94,6 +99,10 @@ define([ this.element.trigger('changed'); }, + _getColumnName: function() { + return this.element.find('input.select').select2('val'); + }, + _getColumnFunc: function(columnName) { var column = this.options.columnsCollection.findWhere({name: columnName}); if (_.isEmpty(column)) { From a69139b682e6b3078837922a17cc64feb2b82f7f Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Fri, 4 Dec 2015 13:03:32 +0100 Subject: [PATCH 070/471] BAP-9430: Report builder filters does not contain aggregation columns - remove aggregated field condition when aggregated column removed function --- .../Resources/public/js/aggregated-field-condition.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js index 3330d8ee565..a77eda64262 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js +++ b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js @@ -25,6 +25,11 @@ define([ this.element.closest('.condition').find('.close').click(); } }, this); + this.options.columnsCollection.on('change:func', function(model) { + if (model.get('name') === this._getColumnName() && _.isEmpty(model.get('func'))) { + this.element.closest('.condition').find('.close').click(); + } + }, this); if (data && data.columnName) { this.selectField(data.columnName); this._renderFilter(data.columnName); From 5ed8a95e29e33e3d15d9d353ba482327a2a6b3c2 Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Fri, 4 Dec 2015 13:56:05 +0100 Subject: [PATCH 071/471] BAP-9430: Report builder filters does not contain aggregation columns - fixed condition builder css --- .../Resources/public/css/less/condition-builder.less | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/css/less/condition-builder.less b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/css/less/condition-builder.less index da8c1fc785b..6a392cbd42a 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/css/less/condition-builder.less +++ b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/css/less/condition-builder.less @@ -29,7 +29,6 @@ list-style: none; margin: 0; padding: 0; - min-height: 199px; box-sizing: border-box; &:before, &:after { content: ""; @@ -249,13 +248,15 @@ border: 1px solid #c9c9c9; min-width: 582px;/* @TODO temporary solution, will be fixed in CRM-2025 */ background-color: #eff2f5; + + & > div { + display: flex; + } .left-area { width: 150px; - float: left; } .right-area { width: calc(~"100% - 150px"); - float: left; padding: 0 10px; border-left: 1px solid #c9c9c9; background-color: white; From 10fd1ad8a266577a649bcc027e857152d07cacf4 Mon Sep 17 00:00:00 2001 From: Oleksandr Nechyporenko Date: Fri, 4 Dec 2015 19:56:06 +0200 Subject: [PATCH 072/471] BB-1491: Login and Registration Page Layouts - cover Layout with tests --- .../Unit/Layout/Block/Type/FormTypeTest.php | 2 +- ...cyInjectionFormContextConfiguratorTest.php | 51 +++++++++++++++ .../DependencyInjectionFormAccessorTest.php | 63 ++++++++++++++++++- .../Layout/Tests/Unit/BlockBuilderTest.php | 5 ++ .../Layout/Tests/Unit/RawLayoutTest.php | 20 ++++++ 5 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Extension/DependencyInjectionFormContextConfiguratorTest.php diff --git a/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Block/Type/FormTypeTest.php b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Block/Type/FormTypeTest.php index d8e2e4d4acf..f621c6e509c 100644 --- a/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Block/Type/FormTypeTest.php +++ b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Block/Type/FormTypeTest.php @@ -168,7 +168,7 @@ public function testBuildView() $view = new BlockView(); $block = $this->getMock('Oro\Component\Layout\BlockInterface'); - $formAccessor = $this->getMock('Oro\Bundle\LayoutBundle\Layout\Form\FormAccessorInterface'); + $formAccessor = $this->getMock('Oro\Bundle\LayoutBundle\Layout\Form\ConfigurableFormAccessorInterface'); $context = new LayoutContext(); $formView = new FormView(); diff --git a/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Extension/DependencyInjectionFormContextConfiguratorTest.php b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Extension/DependencyInjectionFormContextConfiguratorTest.php new file mode 100644 index 00000000000..5fafa2065aa --- /dev/null +++ b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Extension/DependencyInjectionFormContextConfiguratorTest.php @@ -0,0 +1,51 @@ +container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + + $this->contextConfigurator = new DependencyInjectionFormContextConfigurator($this->container); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage formServiceId should be specified. + */ + public function testConfigureContextWithoutForm() + { + $context = new LayoutContext(); + $this->contextConfigurator->configureContext($context); + } + + public function testConfigureContext() + { + $serviceId = 'test_service_id'; + $contextOptionName = 'test_context_form'; + $this->contextConfigurator->setFormServiceId($serviceId); + $this->contextConfigurator->setContextOptionName($contextOptionName); + + $context = new LayoutContext(); + $this->contextConfigurator->configureContext($context); + $context->resolve(); + + $formAccessor = $context->get($contextOptionName); + $this->assertInstanceOf('Oro\Bundle\LayoutBundle\Layout\Form\DependencyInjectionFormAccessor', $formAccessor); + $this->assertAttributeEquals($serviceId, 'formServiceId', $formAccessor); + } +} diff --git a/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Form/DependencyInjectionFormAccessorTest.php b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Form/DependencyInjectionFormAccessorTest.php index 9fc5fccee61..2e239f0441f 100644 --- a/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Form/DependencyInjectionFormAccessorTest.php +++ b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Form/DependencyInjectionFormAccessorTest.php @@ -2,6 +2,7 @@ namespace Oro\Bundle\LayoutBundle\Tests\Unit\Layout\Form; +use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Form\FormView; use Oro\Bundle\LayoutBundle\Layout\Form\DependencyInjectionFormAccessor; @@ -11,7 +12,7 @@ class DependencyInjectionFormAccessorTest extends \PHPUnit_Framework_TestCase { const FORM_SERVICE_ID = 'test_service_id'; - /** @var \PHPUnit_Framework_MockObject_MockObject */ + /** @var ContainerInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $container; protected function setUp() @@ -22,6 +23,8 @@ protected function setUp() public function testGetForm() { $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $form->expects($this->once())->method('getName')->willReturn('form_name'); + $this->container->expects($this->once()) ->method('get') ->with(self::FORM_SERVICE_ID) @@ -29,6 +32,7 @@ public function testGetForm() $formAccessor = new DependencyInjectionFormAccessor($this->container, self::FORM_SERVICE_ID); $this->assertSame($form, $formAccessor->getForm()); + $this->assertEquals('form_name', $formAccessor->getName()); } public function testToString() @@ -130,6 +134,7 @@ public function testGetView() // field1 // field2 $formView = new FormView(); + $formView->vars['id'] = self::FORM_SERVICE_ID; $field1View = new FormView($formView); $formView->children['field1'] = $field1View; $field2View = new FormView($field1View); @@ -148,6 +153,7 @@ public function testGetView() $this->assertSame($formView, $formAccessor->getView()); $this->assertSame($field1View, $formAccessor->getView('field1')); $this->assertSame($field2View, $formAccessor->getView('field1.field2')); + $this->assertSame($formView->vars['id'], $formAccessor->getId()); } public function testProcessedFields() @@ -160,4 +166,59 @@ public function testProcessedFields() $formAccessor->setProcessedFields($processedFields); $this->assertSame($processedFields, $formAccessor->getProcessedFields()); } + + public function testSetFormData() + { + $data = ['test']; + + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $form->expects($this->once())->method('setData')->with($data); + $form->expects($this->at(2))->method('getData')->willReturn($data); + + $this->container->expects($this->once()) + ->method('get') + ->with(self::FORM_SERVICE_ID) + ->will($this->returnValue($form)); + + $formAccessor = new DependencyInjectionFormAccessor($this->container, self::FORM_SERVICE_ID); + + $this->assertNull($formAccessor->getForm()->getData()); + $formAccessor->setFormData($data); + $this->assertEquals($data, $formAccessor->getForm()->getData()); + } + + public function testSetters() + { + $formConfig = $this->getMock('Symfony\Component\Form\FormConfigInterface'); + $formView = new FormView(); + $formView->vars['multipart'] = true; + + $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $form->expects($this->any()) + ->method('getConfig') + ->will($this->returnValue($formConfig)); + $form->expects($this->once()) + ->method('createView') + ->will($this->returnValue($formView)); + + $this->container->expects($this->once()) + ->method('get') + ->with(self::FORM_SERVICE_ID) + ->will($this->returnValue($form)); + + $formAccessor = new DependencyInjectionFormAccessor($this->container, self::FORM_SERVICE_ID); + + $action = FormAction::createByRoute('test_route', ['foo' => 'bar']); + $formAccessor->setAction($action); + $this->assertEquals($action, $formAccessor->getAction()); + + $formAccessor->setActionRoute('test_route', []); + $this->assertEquals(FormAction::createByRoute('test_route', []), $formAccessor->getAction()); + + $formAccessor->setMethod('post'); + $this->assertEquals('post', $formAccessor->getMethod()); + + $formAccessor->setEnctype('multipart/form-data'); + $this->assertEquals('multipart/form-data', $formAccessor->getEnctype()); + } } diff --git a/src/Oro/Component/Layout/Tests/Unit/BlockBuilderTest.php b/src/Oro/Component/Layout/Tests/Unit/BlockBuilderTest.php index 8c3b6842578..83f7d7f6382 100644 --- a/src/Oro/Component/Layout/Tests/Unit/BlockBuilderTest.php +++ b/src/Oro/Component/Layout/Tests/Unit/BlockBuilderTest.php @@ -61,6 +61,11 @@ public function testGetContext() $this->assertSame($this->context, $this->blockBuilder->getContext()); } + public function testGetDataAccessor() + { + $this->assertSame($this->dataAccessor, $this->blockBuilder->getDataAccessor()); + } + public function testGetLayoutManipulator() { $this->assertSame($this->layoutManipulator, $this->blockBuilder->getLayoutManipulator()); diff --git a/src/Oro/Component/Layout/Tests/Unit/RawLayoutTest.php b/src/Oro/Component/Layout/Tests/Unit/RawLayoutTest.php index bfc6994d5a7..e16ab2b80a4 100644 --- a/src/Oro/Component/Layout/Tests/Unit/RawLayoutTest.php +++ b/src/Oro/Component/Layout/Tests/Unit/RawLayoutTest.php @@ -1007,6 +1007,26 @@ public function testSetBlockThemeWithInvalidThemeType() $this->rawLayout->setBlockTheme('root', 123); } + /** + * @expectedException \Oro\Component\Layout\Exception\InvalidArgumentException + * @expectedExceptionMessage The theme must not be empty. + */ + public function testSetFormThemeWithEmptyThemes() + { + $this->rawLayout->add('root', null, 'root'); + $this->rawLayout->setFormTheme([]); + } + + /** + * @expectedException \Oro\Component\Layout\Exception\InvalidArgumentException + * @expectedExceptionMessage Invalid "themes" argument type. Expected "string or array of strings", "integer" given. + */ + public function testSetFormThemeWithInvalidThemeType() + { + $this->rawLayout->add('root', null, 'root'); + $this->rawLayout->setFormTheme(123); + } + public function testSetFormTheme() { // prepare test data From 41cee3dad5aaec1c1ac88f12f23830c8385325ac Mon Sep 17 00:00:00 2001 From: rodolfo Date: Fri, 4 Dec 2015 14:04:42 -0500 Subject: [PATCH 073/471] read() must return $item to work --- .../Resources/doc/reference/additional-capabilities.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Oro/Bundle/IntegrationBundle/Resources/doc/reference/additional-capabilities.md b/src/Oro/Bundle/IntegrationBundle/Resources/doc/reference/additional-capabilities.md index b0e971a2536..d64e238d0ed 100644 --- a/src/Oro/Bundle/IntegrationBundle/Resources/doc/reference/additional-capabilities.md +++ b/src/Oro/Bundle/IntegrationBundle/Resources/doc/reference/additional-capabilities.md @@ -29,6 +29,8 @@ and then methods `addStatusData` and `getStatusData` will be available. if (null !== $item && !$this->getSourceIterator()->valid()) { $this->addStatusData('lastItemUpdatedAt', $item['updated_at']); } + + return $item; } // ... From a1ab9d50c758d6b7858980d056c8bde63413ed51 Mon Sep 17 00:00:00 2001 From: ReenExe Date: Sun, 6 Dec 2015 02:39:07 +0200 Subject: [PATCH 074/471] clear code --- .../EntityBundle/Provider/AbstractChainProvider.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Oro/Bundle/EntityBundle/Provider/AbstractChainProvider.php b/src/Oro/Bundle/EntityBundle/Provider/AbstractChainProvider.php index 49b7de6d41a..bc2589cd307 100644 --- a/src/Oro/Bundle/EntityBundle/Provider/AbstractChainProvider.php +++ b/src/Oro/Bundle/EntityBundle/Provider/AbstractChainProvider.php @@ -36,11 +36,9 @@ protected function getProviders() if (null === $this->sorted) { ksort($this->providers); - if (empty($this->providers)) { - $this->sorted = []; - } else { - $this->sorted = call_user_func_array('array_merge', $this->providers); - } + $this->sorted = $this->providers + ? call_user_func_array('array_merge', $this->providers) + : []; } return $this->sorted; From 9974fe910b27db05ae6dce742683c15c80519da8 Mon Sep 17 00:00:00 2001 From: ReenExe Date: Sun, 6 Dec 2015 02:48:37 +0200 Subject: [PATCH 075/471] clear code --- src/Oro/Bundle/UserBundle/Entity/AbstractRole.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Oro/Bundle/UserBundle/Entity/AbstractRole.php b/src/Oro/Bundle/UserBundle/Entity/AbstractRole.php index 6a75577e5d3..516de5d658d 100644 --- a/src/Oro/Bundle/UserBundle/Entity/AbstractRole.php +++ b/src/Oro/Bundle/UserBundle/Entity/AbstractRole.php @@ -25,11 +25,9 @@ abstract public function getPrefix(); */ public function setRole($role, $generateUnique = true) { - if ($generateUnique) { - $this->role = $this->generateUniqueRole($role); - } else { - $this->role = $this->addPrefix($this->normalize($role)); - } + $this->role = $generateUnique + ? $this->generateUniqueRole($role) + : $this->addPrefix($this->normalize($role)); return $this; } From 9ef3ff7ae0ab1170838acea0b4fe2848f6d0cf84 Mon Sep 17 00:00:00 2001 From: ReenExe Date: Sun, 6 Dec 2015 02:55:00 +0200 Subject: [PATCH 076/471] clear code --- src/Oro/Bundle/UserBundle/Entity/Group.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Oro/Bundle/UserBundle/Entity/Group.php b/src/Oro/Bundle/UserBundle/Entity/Group.php index d49d067a01c..23b10c7343c 100644 --- a/src/Oro/Bundle/UserBundle/Entity/Group.php +++ b/src/Oro/Bundle/UserBundle/Entity/Group.php @@ -243,11 +243,7 @@ public function removeRole($role) public function setRoles($roles) { if ($roles instanceof Collection) { - $this->roles->clear(); - - foreach ($roles as $role) { - $this->addRole($role); - } + $this->roles = new ArrayCollection($roles->toArray()); } elseif (is_array($roles)) { $this->roles = new ArrayCollection($roles); } else { From 87880164600bbee65af841755356fb6a2ebc04f8 Mon Sep 17 00:00:00 2001 From: ReenExe Date: Sun, 6 Dec 2015 03:01:53 +0200 Subject: [PATCH 077/471] clear code --- .../Api/Rest/CalendarConnectionController.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Oro/Bundle/CalendarBundle/Controller/Api/Rest/CalendarConnectionController.php b/src/Oro/Bundle/CalendarBundle/Controller/Api/Rest/CalendarConnectionController.php index 65c5b9c383b..1b251e4d4ec 100644 --- a/src/Oro/Bundle/CalendarBundle/Controller/Api/Rest/CalendarConnectionController.php +++ b/src/Oro/Bundle/CalendarBundle/Controller/Api/Rest/CalendarConnectionController.php @@ -138,11 +138,13 @@ protected function fixFormData(array &$data, $entity) { parent::fixFormData($data, $entity); - unset($data['calendarName']); - unset($data['removable']); - unset($data['canAddEvent']); - unset($data['canEditEvent']); - unset($data['canDeleteEvent']); + unset( + $data['calendarName'], + $data['removable'], + $data['canAddEvent'], + $data['canEditEvent'], + $data['canDeleteEvent'] + ); return true; } From 9e07b7e54f7d36869d55e7765eb8448d71154b8c Mon Sep 17 00:00:00 2001 From: ReenExe Date: Sun, 6 Dec 2015 03:11:46 +0200 Subject: [PATCH 078/471] clear code --- .../Bundle/CalendarBundle/Provider/CalendarPropertyProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Oro/Bundle/CalendarBundle/Provider/CalendarPropertyProvider.php b/src/Oro/Bundle/CalendarBundle/Provider/CalendarPropertyProvider.php index 7c17251ec04..6dd8ae65240 100644 --- a/src/Oro/Bundle/CalendarBundle/Provider/CalendarPropertyProvider.php +++ b/src/Oro/Bundle/CalendarBundle/Provider/CalendarPropertyProvider.php @@ -91,7 +91,7 @@ public function getItems($calendarId) $result = $qb->getQuery()->getArrayResult(); // normalize integer foreign keys due Doctrine IDENTITY function always returns a string - if (!empty($intCasts)) { + if ($intCasts) { foreach ($result as &$item) { foreach ($intCasts as $fieldName) { if (isset($item[$fieldName]) && is_string($item[$fieldName])) { From 2ad636d15925a8ee65ccbae4bb2214a3cfa7711e Mon Sep 17 00:00:00 2001 From: ReenExe Date: Sun, 6 Dec 2015 03:13:51 +0200 Subject: [PATCH 079/471] clear code --- .../CalendarBundle/Provider/PublicCalendarProvider.php | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Oro/Bundle/CalendarBundle/Provider/PublicCalendarProvider.php b/src/Oro/Bundle/CalendarBundle/Provider/PublicCalendarProvider.php index d87d043afa2..ac39b9f876c 100644 --- a/src/Oro/Bundle/CalendarBundle/Provider/PublicCalendarProvider.php +++ b/src/Oro/Bundle/CalendarBundle/Provider/PublicCalendarProvider.php @@ -48,16 +48,11 @@ public function __construct( */ public function getCalendarDefaultValues($organizationId, $userId, $calendarId, array $calendarIds) { - $result = []; - if (!$this->calendarConfig->isPublicCalendarEnabled()) { - foreach ($calendarIds as $id) { - $result[$id] = null; - } - - return $result; + return array_fill_keys($calendarIds, null); } + $result = []; /** @var SystemCalendarRepository $repo */ $repo = $this->doctrineHelper->getEntityRepository('OroCalendarBundle:SystemCalendar'); $qb = $repo->getPublicCalendarsQueryBuilder(); From 56af71049485d2aa9346b5e214727a1320908572 Mon Sep 17 00:00:00 2001 From: ReenExe Date: Sun, 6 Dec 2015 03:17:08 +0200 Subject: [PATCH 080/471] clear code --- .../Provider/SystemCalendarProvider.php | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/Oro/Bundle/CalendarBundle/Provider/SystemCalendarProvider.php b/src/Oro/Bundle/CalendarBundle/Provider/SystemCalendarProvider.php index cd903d52194..6d617db77fa 100644 --- a/src/Oro/Bundle/CalendarBundle/Provider/SystemCalendarProvider.php +++ b/src/Oro/Bundle/CalendarBundle/Provider/SystemCalendarProvider.php @@ -48,18 +48,13 @@ public function __construct( */ public function getCalendarDefaultValues($organizationId, $userId, $calendarId, array $calendarIds) { - $result = []; - if (!$this->calendarConfig->isSystemCalendarEnabled() || !$this->securityFacade->isGranted('oro_system_calendar_view') ) { - foreach ($calendarIds as $id) { - $result[$id] = null; - } - - return $result; + return array_fill_keys($calendarIds, null); } + $result = []; /** @var SystemCalendarRepository $repo */ $repo = $this->doctrineHelper->getEntityRepository('OroCalendarBundle:SystemCalendar'); @@ -133,7 +128,7 @@ public function getCalendarEvents( $invisibleIds[] = $id; } } - if (!empty($invisibleIds)) { + if ($invisibleIds) { $qb ->andWhere('c.id NOT IN (:invisibleIds)') ->setParameter('invisibleIds', $invisibleIds); From e755bd6df1d81aea3b49fc963b11583994ab1768 Mon Sep 17 00:00:00 2001 From: ReenExe Date: Sun, 6 Dec 2015 03:19:28 +0200 Subject: [PATCH 081/471] clear code --- .../CalendarBundle/Provider/UserCalendarEventNormalizer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Oro/Bundle/CalendarBundle/Provider/UserCalendarEventNormalizer.php b/src/Oro/Bundle/CalendarBundle/Provider/UserCalendarEventNormalizer.php index 7d1b819164b..7cb882c1170 100644 --- a/src/Oro/Bundle/CalendarBundle/Provider/UserCalendarEventNormalizer.php +++ b/src/Oro/Bundle/CalendarBundle/Provider/UserCalendarEventNormalizer.php @@ -83,7 +83,7 @@ protected function serializeCalendarEvent(CalendarEvent $event) protected function applyAdditionalData(&$items, $calendarId) { $parentEventIds = $this->getParentEventIds($items); - if (!empty($parentEventIds)) { + if ($parentEventIds) { /** @var CalendarEventRepository $repo */ $repo = $this->doctrineHelper->getEntityRepository('OroCalendarBundle:CalendarEvent'); $invitees = $repo->getInvitedUsersByParentsQueryBuilder($parentEventIds) From 816f59f3bd79bb047979e5b5f0aacbc146a38dd8 Mon Sep 17 00:00:00 2001 From: ReenExe Date: Sun, 6 Dec 2015 03:26:24 +0200 Subject: [PATCH 082/471] clear code --- src/Oro/Bundle/CalendarBundle/Provider/UserCalendarProvider.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Oro/Bundle/CalendarBundle/Provider/UserCalendarProvider.php b/src/Oro/Bundle/CalendarBundle/Provider/UserCalendarProvider.php index 829e410dd1e..55c333fadf4 100644 --- a/src/Oro/Bundle/CalendarBundle/Provider/UserCalendarProvider.php +++ b/src/Oro/Bundle/CalendarBundle/Provider/UserCalendarProvider.php @@ -60,8 +60,6 @@ public function getCalendarDefaultValues($organizationId, $userId, $calendarId, // prohibit to remove the current calendar from the list of connected calendars if ($calendar->getId() === $calendarId) { $resultItem['removable'] = false; - } - if ($calendarId === $calendar->getId()) { $resultItem['canAddEvent'] = true; $resultItem['canEditEvent'] = true; $resultItem['canDeleteEvent'] = true; From d3e62ee18270a02ba84c3bb902a6911ba26cdb60 Mon Sep 17 00:00:00 2001 From: ReenExe Date: Sun, 6 Dec 2015 03:29:24 +0200 Subject: [PATCH 083/471] clear code --- .../CalendarBundle/Provider/UserCalendarProvider.php | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Oro/Bundle/CalendarBundle/Provider/UserCalendarProvider.php b/src/Oro/Bundle/CalendarBundle/Provider/UserCalendarProvider.php index 55c333fadf4..cd410b89b2c 100644 --- a/src/Oro/Bundle/CalendarBundle/Provider/UserCalendarProvider.php +++ b/src/Oro/Bundle/CalendarBundle/Provider/UserCalendarProvider.php @@ -92,7 +92,7 @@ public function getCalendarEvents( $visibleIds[] = $id; } } - if (!empty($visibleIds)) { + if ($visibleIds) { $qb ->andWhere('c.id IN (:visibleIds)') ->setParameter('visibleIds', $visibleIds); @@ -111,11 +111,6 @@ public function getCalendarEvents( */ protected function buildCalendarName(Calendar $calendar) { - $name = $calendar->getName(); - if (!$name) { - $name = $this->entityNameResolver->getName($calendar->getOwner()); - } - - return $name; + return $calendar->getName() ?: $this->entityNameResolver->getName($calendar->getOwner()); } } From 3574990d63a9cf1a0e3f4bdce384982546eb81ed Mon Sep 17 00:00:00 2001 From: ReenExe Date: Sun, 6 Dec 2015 03:32:40 +0200 Subject: [PATCH 084/471] clear code --- .../Datagrid/EntityPaginationExtension.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Oro/Bundle/EntityPaginationBundle/Datagrid/EntityPaginationExtension.php b/src/Oro/Bundle/EntityPaginationBundle/Datagrid/EntityPaginationExtension.php index 075a86b3371..54619668131 100644 --- a/src/Oro/Bundle/EntityPaginationBundle/Datagrid/EntityPaginationExtension.php +++ b/src/Oro/Bundle/EntityPaginationBundle/Datagrid/EntityPaginationExtension.php @@ -36,7 +36,6 @@ public function processConfigs(DatagridConfiguration $config) throw new \LogicException('Entity pagination is not boolean'); } - $pagination = $pagination ? true : false; - $config->offsetSetByPath(self::ENTITY_PAGINATION_PATH, $pagination); + $config->offsetSetByPath(self::ENTITY_PAGINATION_PATH, (bool)$pagination); } } From 0cd65546cba0004db82553c1efcd5827c2bafbdb Mon Sep 17 00:00:00 2001 From: ReenExe Date: Sun, 6 Dec 2015 04:02:50 +0200 Subject: [PATCH 085/471] clear code --- .../Manager/EntityPaginationManager.php | 10 +++------- .../EntityPaginationBundle/Manager/MessageManager.php | 10 +++------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/Oro/Bundle/EntityPaginationBundle/Manager/EntityPaginationManager.php b/src/Oro/Bundle/EntityPaginationBundle/Manager/EntityPaginationManager.php index 12050a065cb..4b1fcee5575 100644 --- a/src/Oro/Bundle/EntityPaginationBundle/Manager/EntityPaginationManager.php +++ b/src/Oro/Bundle/EntityPaginationBundle/Manager/EntityPaginationManager.php @@ -58,15 +58,11 @@ public static function getPermission($scope) { switch ($scope) { case self::VIEW_SCOPE: - $permission = 'VIEW'; - break; + return 'VIEW'; case self::EDIT_SCOPE: - $permission = 'EDIT'; - break; - default: - throw new \LogicException(sprintf('Scope "%s" is not available.', $scope)); + return 'EDIT'; } - return $permission; + throw new \LogicException(sprintf('Scope "%s" is not available.', $scope)); } } diff --git a/src/Oro/Bundle/EntityPaginationBundle/Manager/MessageManager.php b/src/Oro/Bundle/EntityPaginationBundle/Manager/MessageManager.php index 67c1ec9e014..5e5ec38d5a7 100644 --- a/src/Oro/Bundle/EntityPaginationBundle/Manager/MessageManager.php +++ b/src/Oro/Bundle/EntityPaginationBundle/Manager/MessageManager.php @@ -150,19 +150,15 @@ protected function getStatsMessage($scope) { switch ($scope) { case EntityPaginationManager::VIEW_SCOPE: - $message = + return 'oro.entity_pagination.message.' . 'stats_number_view_%count%_record|stats_number_view_%count%_records'; - break; case EntityPaginationManager::EDIT_SCOPE: - $message = + return 'oro.entity_pagination.message.' . 'stats_number_edit_%count%_record|stats_number_edit_%count%_records'; - break; - default: - throw new \LogicException(sprintf('Scope "%s" is not available.', $scope)); } - return $message; + throw new \LogicException(sprintf('Scope "%s" is not available.', $scope)); } } From 7d8f98d24ff2a0c9fdd3de26711eb630018c4979 Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Mon, 7 Dec 2015 10:23:31 +0100 Subject: [PATCH 086/471] BAP-9454: having support pro GroupingOrmFilterDatasourceAdapter - add having support --- .../GroupingOrmFilterDatasourceAdapter.php | 93 ++-------- .../Model/ExpressionBuilder.php | 120 +++++++++++++ .../QueryDesignerBundle/Model/GroupNode.php | 97 ++++++++++ .../QueryDesignerBundle/Model/Restriction.php | 51 ++++++ ...GroupingOrmFilterDatasourceAdapterTest.php | 167 ++++++++++++++++++ src/Oro/Component/PhpUtils/ArrayUtil.php | 19 ++ 6 files changed, 464 insertions(+), 83 deletions(-) create mode 100644 src/Oro/Bundle/QueryDesignerBundle/Model/ExpressionBuilder.php create mode 100644 src/Oro/Bundle/QueryDesignerBundle/Model/GroupNode.php create mode 100644 src/Oro/Bundle/QueryDesignerBundle/Model/Restriction.php diff --git a/src/Oro/Bundle/QueryDesignerBundle/Grid/Extension/GroupingOrmFilterDatasourceAdapter.php b/src/Oro/Bundle/QueryDesignerBundle/Grid/Extension/GroupingOrmFilterDatasourceAdapter.php index 43f9c4e5efd..23bd47ebd27 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Grid/Extension/GroupingOrmFilterDatasourceAdapter.php +++ b/src/Oro/Bundle/QueryDesignerBundle/Grid/Extension/GroupingOrmFilterDatasourceAdapter.php @@ -3,9 +3,10 @@ namespace Oro\Bundle\QueryDesignerBundle\Grid\Extension; use Doctrine\ORM\QueryBuilder; -use Doctrine\ORM\Query\Expr; + use Oro\Bundle\FilterBundle\Datasource\Orm\OrmFilterDatasourceAdapter; -use Oro\Bundle\FilterBundle\Filter\FilterUtility; +use Oro\Bundle\QueryDesignerBundle\Model\ExpressionBuilder; +use Oro\Bundle\QueryDesignerBundle\Model\Restriction; /** * Represents ORM data source adapter which allows to combine restrictions in groups, @@ -13,25 +14,8 @@ */ class GroupingOrmFilterDatasourceAdapter extends OrmFilterDatasourceAdapter { - /** - * @var Expr\Composite[] - */ - protected $exprStack; - - /** - * @var string[] - */ - protected $conditionStack; - - /** - * @var Expr\Composite - */ - protected $currentExpr = null; - - /** - * @var string - */ - protected $currentCondition = null; + /** @var ExpressionBuilder */ + protected $expressionBuilder; /** * Constructor @@ -41,7 +25,7 @@ class GroupingOrmFilterDatasourceAdapter extends OrmFilterDatasourceAdapter public function __construct(QueryBuilder $qb) { parent::__construct($qb); - $this->resetState(); + $this->expressionBuilder = new ExpressionBuilder(); } /** @@ -49,54 +33,17 @@ public function __construct(QueryBuilder $qb) */ public function addRestriction($restriction, $condition, $isComputed = false) { - if ($isComputed) { - if ($condition === FilterUtility::CONDITION_OR) { - $this->qb->orHaving($restriction); - } else { - $this->qb->andHaving($restriction); - } - - return; - } - - if ($this->currentExpr === null) { - // this is the first item in a group - $this->currentExpr = $restriction; - } else { - // other items - if ($condition === FilterUtility::CONDITION_OR) { - if ($this->currentExpr instanceof Expr\Orx) { - $this->currentExpr->add($restriction); - } else { - $this->currentExpr = $this->qb->expr()->orX($this->currentExpr, $restriction); - } - } else { - if ($this->currentExpr instanceof Expr\Andx) { - $this->currentExpr->add($restriction); - } else { - $this->currentExpr = $this->qb->expr()->andX($this->currentExpr, $restriction); - } - } - } + $this->expressionBuilder->addRestriction(new Restriction($restriction, $condition, $isComputed)); } public function beginRestrictionGroup($condition) { - array_push($this->exprStack, $this->currentExpr); - array_push($this->conditionStack, $this->currentCondition); - - $this->currentExpr = null; - $this->currentCondition = $condition; + $this->expressionBuilder->beginGroup($condition); } public function endRestrictionGroup() { - $tmpExpr = $this->currentExpr; - $tmpCondition = $this->currentCondition; - $this->currentExpr = array_pop($this->exprStack); - $this->currentCondition = array_pop($this->conditionStack); - - $this->addRestriction($tmpExpr, $tmpCondition); + $this->expressionBuilder->endGroup(); } /** @@ -104,26 +51,6 @@ public function endRestrictionGroup() */ public function applyRestrictions() { - if ($this->currentExpr === null && empty($this->exprStack)) { - return; - } - - if ($this->currentExpr === null) { - $this->currentExpr = array_pop($this->exprStack); - } - - $this->qb->andWhere($this->currentExpr); - $this->resetState(); - } - - /** - * Resets all 'state' variables of this adapter - */ - protected function resetState() - { - $this->exprStack = []; - $this->conditionStack = []; - $this->currentExpr = null; - $this->currentCondition = null; + $this->expressionBuilder->applyRestrictions($this->qb); } } diff --git a/src/Oro/Bundle/QueryDesignerBundle/Model/ExpressionBuilder.php b/src/Oro/Bundle/QueryDesignerBundle/Model/ExpressionBuilder.php new file mode 100644 index 00000000000..87eb34adf49 --- /dev/null +++ b/src/Oro/Bundle/QueryDesignerBundle/Model/ExpressionBuilder.php @@ -0,0 +1,120 @@ +currentGroupNode) { + $this->currentGroupNode->addNode($groupNode); + $this->currentGroupNode = $groupNode; + } else { + $this->groupNode = $groupNode; + $this->currentGroupNode = $this->groupNode; + } + } + + /** + * @param Restriction $restriction + */ + public function addRestriction(Restriction $restriction) + { + if (!$this->groupNode) { + $this->groupNode = new GroupNode(FilterUtility::CONDITION_AND); + $this->currentGroupNode = $this->groupNode; + } + + $this->currentGroupNode->addNode($restriction); + } + + public function endGroup() + { + $this->currentGroupNode = $this->currentGroupNode->getParent(); + } + + /** + * @param QueryBuilder $qb + */ + public function applyRestrictions(QueryBuilder $qb) + { + if (!$this->groupNode) { + return; + } + + $expr = $this->resolveGroupNode($this->groupNode); + if ($this->groupNode->isComputed()) { + $qb->andHaving($expr); + } else { + $qb->andWhere($expr); + } + } + + /** + * @param GroupNode $gNode + * + * @return mixed Expr + */ + protected function resolveGroupNode(GroupNode $gNode) + { + $restrictions = []; + foreach ($gNode->getChildren() as $node) { + if ($node instanceof Restriction) { + $restrictions[] = $node; + } else { + $expr = $this->resolveGroupNode($node); + $restrictions[] = new Restriction($expr, $node->getCondition(), $node->isComputed()); + } + } + + return $this->createExprFromRestrictions($restrictions); + } + + /** + * @param Restriction[] $restrictions + * + * @return mixed Expr + */ + protected function createExprFromRestrictions(array $restrictions) + { + return array_reduce( + $restrictions, + function ($expr = null, Restriction $restriction) { + if ($expr === null) { + return $restriction->getRestriction(); + } + + if ($restriction->getCondition() === FilterUtility::CONDITION_OR) { + if ($expr instanceof Expr\Orx) { + $expr->add($restriction->getRestriction()); + } else { + $expr = new Expr\Orx([$expr, $restriction->getRestriction()]); + } + } else { + if ($expr instanceof Expr\Andx) { + $expr->add($restriction->getRestriction()); + } else { + $expr = new Expr\Andx([$expr, $restriction->getRestriction()]); + } + } + + return $expr; + } + ); + } +} diff --git a/src/Oro/Bundle/QueryDesignerBundle/Model/GroupNode.php b/src/Oro/Bundle/QueryDesignerBundle/Model/GroupNode.php new file mode 100644 index 00000000000..c2a49065b64 --- /dev/null +++ b/src/Oro/Bundle/QueryDesignerBundle/Model/GroupNode.php @@ -0,0 +1,97 @@ +condition = $condition; + } + + /** + * @param GroupNode|Restriction $node + */ + public function addNode($node) + { + $this->nodes[] = $node; + if ($node instanceof GroupNode) { + $node->setParent($this); + } + } + + /** + * @param GroupNode $node + */ + public function setParent(GroupNode $node) + { + $this->parentNode = $node; + } + + /** + * @return GroupNode|null + */ + public function getParent() + { + return $this->parentNode; + } + + /** + * @param GroupNode[]|Restriction[] $node + */ + public function getChildren() + { + return $this->nodes; + } + + /** + * @return string + */ + public function getCondition() + { + return $this->condition; + } + + /** + * @return bool + * + * @throws LogicException If computed restrictions are mixed with uncomputed + */ + public function isComputed() + { + $computed = ArrayUtil::some( + function ($node) { + return $node->isComputed(); + }, + $this->nodes + ); + + $unComputed = ArrayUtil::some( + function ($node) { + return !$node->isComputed(); + }, + $this->nodes + ); + + if ($computed && $unComputed) { + throw new LogicException('Mixing ofcomputed nodes with uncomputed is not implemented'); + } + + return $computed; + } +} diff --git a/src/Oro/Bundle/QueryDesignerBundle/Model/Restriction.php b/src/Oro/Bundle/QueryDesignerBundle/Model/Restriction.php new file mode 100644 index 00000000000..72161890ef0 --- /dev/null +++ b/src/Oro/Bundle/QueryDesignerBundle/Model/Restriction.php @@ -0,0 +1,51 @@ +restriction = $restriction; + $this->condition = $condition; + $this->computed = $computed; + } + + /** + * @return mixed Expr + */ + public function getRestriction() + { + return $this->restriction; + } + + /** + * @return string + */ + public function getCondition() + { + return $this->condition; + } + + /** + * @return bool + */ + public function isComputed() + { + return $this->computed; + } +} diff --git a/src/Oro/Bundle/QueryDesignerBundle/Tests/Unit/Grid/Extension/GroupingOrmFilterDatasourceAdapterTest.php b/src/Oro/Bundle/QueryDesignerBundle/Tests/Unit/Grid/Extension/GroupingOrmFilterDatasourceAdapterTest.php index 651983a0520..4039e653645 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Tests/Unit/Grid/Extension/GroupingOrmFilterDatasourceAdapterTest.php +++ b/src/Oro/Bundle/QueryDesignerBundle/Tests/Unit/Grid/Extension/GroupingOrmFilterDatasourceAdapterTest.php @@ -145,6 +145,27 @@ public function testOneRestrictionInGroup() ); } + public function testOneComputedRestrictionInGroup() + { + $qb = new QueryBuilder($this->getTestEntityManager()); + $qb->select(['u.status, COUNT(u.id)']) + ->from('Oro\Bundle\QueryDesignerBundle\Tests\Unit\Fixtures\Models\CMS\CmsUser', 'u') + ->groupBy('u.status'); + $ds = new GroupingOrmFilterDatasourceAdapter($qb); + + $ds->beginRestrictionGroup(FilterUtility::CONDITION_AND); + $ds->addRestriction($qb->expr()->eq('COUNT(u.id)', '1'), FilterUtility::CONDITION_AND, true); + $ds->endRestrictionGroup(); + $ds->applyRestrictions(); + + $this->assertEquals( + 'SELECT u.status, COUNT(u.id) FROM Oro\Bundle\QueryDesignerBundle\Tests\Unit\Fixtures\Models\CMS\CmsUser u ' + . 'GROUP BY u.status ' + . 'HAVING COUNT(u.id) = 1', + $qb->getDQL() + ); + } + public function testSeveralRestrictionsInGroup() { $qb = new QueryBuilder($this->getTestEntityManager()); @@ -168,6 +189,29 @@ public function testSeveralRestrictionsInGroup() ); } + public function testSeveralComputedRestrictionsInGroup() + { + $qb = new QueryBuilder($this->getTestEntityManager()); + $qb->select(['u.status, COUNT(u.id)']) + ->from('Oro\Bundle\QueryDesignerBundle\Tests\Unit\Fixtures\Models\CMS\CmsUser', 'u') + ->groupBy('u.status'); + $ds = new GroupingOrmFilterDatasourceAdapter($qb); + + $ds->beginRestrictionGroup(FilterUtility::CONDITION_AND); + $ds->addRestriction($qb->expr()->eq('COUNT(u.id)', '1'), FilterUtility::CONDITION_AND, true); + $ds->addRestriction($qb->expr()->eq('MIN(u.id)', '2'), FilterUtility::CONDITION_OR, true); + $ds->addRestriction($qb->expr()->eq('MAX(u.id)', '3'), FilterUtility::CONDITION_AND, true); + $ds->endRestrictionGroup(); + $ds->applyRestrictions(); + + $this->assertEquals( + 'SELECT u.status, COUNT(u.id) FROM Oro\Bundle\QueryDesignerBundle\Tests\Unit\Fixtures\Models\CMS\CmsUser u ' + . 'GROUP BY u.status ' + . 'HAVING (COUNT(u.id) = 1 OR MIN(u.id) = 2) AND MAX(u.id) = 3', + $qb->getDQL() + ); + } + public function testNestedGroupsWithOneRestrictionInNestedGroup() { $qb = new QueryBuilder($this->getTestEntityManager()); @@ -194,6 +238,33 @@ public function testNestedGroupsWithOneRestrictionInNestedGroup() ); } + public function testNestedGroupsWithOneComputedRestrictionInNestedGroup() + { + $qb = new QueryBuilder($this->getTestEntityManager()); + $qb->select(['u.status, COUNT(u.id)']) + ->from('Oro\Bundle\QueryDesignerBundle\Tests\Unit\Fixtures\Models\CMS\CmsUser', 'u') + ->groupBy('u.status') + ->andHaving('MAX(u.id) > 0'); + $ds = new GroupingOrmFilterDatasourceAdapter($qb); + + // src: (1 OR (2)) + // dest: (1 OR 2) + $ds->beginRestrictionGroup(FilterUtility::CONDITION_AND); + $ds->addRestriction($qb->expr()->eq('COUNT(u.id)', '1'), FilterUtility::CONDITION_AND, true); + $ds->beginRestrictionGroup(FilterUtility::CONDITION_OR); + $ds->addRestriction($qb->expr()->eq('MIN(u.id)', '2'), FilterUtility::CONDITION_AND, true); + $ds->endRestrictionGroup(); + $ds->endRestrictionGroup(); + $ds->applyRestrictions(); + + $this->assertEquals( + 'SELECT u.status, COUNT(u.id) FROM Oro\Bundle\QueryDesignerBundle\Tests\Unit\Fixtures\Models\CMS\CmsUser u ' + . 'GROUP BY u.status ' + . 'HAVING MAX(u.id) > 0 AND (COUNT(u.id) = 1 OR MIN(u.id) = 2)', + $qb->getDQL() + ); + } + public function testNestedGroupsWithSameCondition() { $qb = new QueryBuilder($this->getTestEntityManager()); @@ -221,6 +292,33 @@ public function testNestedGroupsWithSameCondition() ); } + public function testNestedComputedGroupsWithSameCondition() + { + $qb = new QueryBuilder($this->getTestEntityManager()); + $qb->select(['u.status, COUNT(u.id)']) + ->from('Oro\Bundle\QueryDesignerBundle\Tests\Unit\Fixtures\Models\CMS\CmsUser', 'u') + ->groupBy('u.status'); + $ds = new GroupingOrmFilterDatasourceAdapter($qb); + + // src: (1 OR (2 OR 3)) + // dest: (1 OR (2 OR 3)) + $ds->beginRestrictionGroup(FilterUtility::CONDITION_AND); + $ds->addRestriction($qb->expr()->eq('COUNT(u.id)', '1'), FilterUtility::CONDITION_AND, true); + $ds->beginRestrictionGroup(FilterUtility::CONDITION_OR); + $ds->addRestriction($qb->expr()->eq('COUNT(u.id)', '2'), FilterUtility::CONDITION_AND, true); + $ds->addRestriction($qb->expr()->eq('COUNT(u.id)', '3'), FilterUtility::CONDITION_OR, true); + $ds->endRestrictionGroup(); + $ds->endRestrictionGroup(); + $ds->applyRestrictions(); + + $this->assertEquals( + 'SELECT u.status, COUNT(u.id) FROM Oro\Bundle\QueryDesignerBundle\Tests\Unit\Fixtures\Models\CMS\CmsUser u ' + . 'GROUP BY u.status ' + . 'HAVING COUNT(u.id) = 1 OR (COUNT(u.id) = 2 OR COUNT(u.id) = 3)', + $qb->getDQL() + ); + } + public function testNestedGroupsWithDifferentConditions() { $qb = new QueryBuilder($this->getTestEntityManager()); @@ -248,6 +346,33 @@ public function testNestedGroupsWithDifferentConditions() ); } + public function testNestedComputedGroupsWithDifferentConditions() + { + $qb = new QueryBuilder($this->getTestEntityManager()); + $qb->select(['u.status, COUNT(u.id)']) + ->from('Oro\Bundle\QueryDesignerBundle\Tests\Unit\Fixtures\Models\CMS\CmsUser', 'u') + ->groupBy('u.status'); + $ds = new GroupingOrmFilterDatasourceAdapter($qb); + + // src: (1 OR (2 AND 3)) + // dest: (1 OR (2 AND 3)) + $ds->beginRestrictionGroup(FilterUtility::CONDITION_AND); + $ds->addRestriction($qb->expr()->eq('COUNT(u.id)', '1'), FilterUtility::CONDITION_AND, true); + $ds->beginRestrictionGroup(FilterUtility::CONDITION_OR); + $ds->addRestriction($qb->expr()->eq('COUNT(u.id)', '2'), FilterUtility::CONDITION_AND, true); + $ds->addRestriction($qb->expr()->eq('COUNT(u.id)', '3'), FilterUtility::CONDITION_AND, true); + $ds->endRestrictionGroup(); + $ds->endRestrictionGroup(); + $ds->applyRestrictions(); + + $this->assertEquals( + 'SELECT u.status, COUNT(u.id) FROM Oro\Bundle\QueryDesignerBundle\Tests\Unit\Fixtures\Models\CMS\CmsUser u ' + . 'GROUP BY u.status ' + . 'HAVING COUNT(u.id) = 1 OR (COUNT(u.id) = 2 AND COUNT(u.id) = 3)', + $qb->getDQL() + ); + } + public function testComplexExpr() { $qb = new QueryBuilder($this->getTestEntityManager()); @@ -287,4 +412,46 @@ public function testComplexExpr() $qb->getDQL() ); } + + public function testComplexComputedExpr() + { + $qb = new QueryBuilder($this->getTestEntityManager()); + $qb->select(['u.status, COUNT(u.id)']) + ->from('Oro\Bundle\QueryDesignerBundle\Tests\Unit\Fixtures\Models\CMS\CmsUser', 'u') + ->groupBy('u.status') + ->having('COUNT(u.id) = 0'); + $ds = new GroupingOrmFilterDatasourceAdapter($qb); + + // src: (1 AND ((2 AND (3 OR 4)) OR (5) OR (6 AND 7)) AND 8) + // dest: (1 AND ((2 AND (3 OR 4)) OR 5 OR (6 AND 7)) AND 8) + $ds->addRestriction($qb->expr()->eq('COUNT(u.id)', '1'), FilterUtility::CONDITION_AND, true); + $ds->beginRestrictionGroup(FilterUtility::CONDITION_AND); + $ds->beginRestrictionGroup(FilterUtility::CONDITION_AND); + $ds->addRestriction($qb->expr()->eq('COUNT(u.id)', '2'), FilterUtility::CONDITION_AND, true); + $ds->beginRestrictionGroup(FilterUtility::CONDITION_AND); + $ds->addRestriction($qb->expr()->eq('COUNT(u.id)', '3'), FilterUtility::CONDITION_AND, true); + $ds->addRestriction($qb->expr()->eq('COUNT(u.id)', '4'), FilterUtility::CONDITION_OR, true); + $ds->endRestrictionGroup(); + $ds->endRestrictionGroup(); + $ds->beginRestrictionGroup(FilterUtility::CONDITION_OR); + $ds->addRestriction($qb->expr()->eq('COUNT(u.id)', '5'), FilterUtility::CONDITION_AND, true); + $ds->endRestrictionGroup(); + $ds->beginRestrictionGroup(FilterUtility::CONDITION_OR); + $ds->addRestriction($qb->expr()->eq('COUNT(u.id)', '6'), FilterUtility::CONDITION_AND, true); + $ds->addRestriction($qb->expr()->eq('COUNT(u.id)', '7'), FilterUtility::CONDITION_AND, true); + $ds->endRestrictionGroup(); + $ds->endRestrictionGroup(); + $ds->addRestriction($qb->expr()->eq('COUNT(u.id)', '8'), FilterUtility::CONDITION_AND, true); + $ds->applyRestrictions(); + + $this->assertEquals( + 'SELECT u.status, COUNT(u.id) FROM Oro\Bundle\QueryDesignerBundle\Tests\Unit\Fixtures\Models\CMS\CmsUser u ' + . 'GROUP BY u.status ' + . 'HAVING COUNT(u.id) = 0 AND ' + . '(COUNT(u.id) = 1 AND ' + . '((COUNT(u.id) = 2 AND (COUNT(u.id) = 3 OR COUNT(u.id) = 4)) OR COUNT(u.id) = 5 OR (COUNT(u.id) = 6 AND COUNT(u.id) = 7)) AND ' + . 'COUNT(u.id) = 8)', + $qb->getDQL() + ); + } } diff --git a/src/Oro/Component/PhpUtils/ArrayUtil.php b/src/Oro/Component/PhpUtils/ArrayUtil.php index f6c0e24245c..6318d1585cf 100644 --- a/src/Oro/Component/PhpUtils/ArrayUtil.php +++ b/src/Oro/Component/PhpUtils/ArrayUtil.php @@ -192,4 +192,23 @@ public static function createOrderedComparator(array $order) return $order[$a] - $order[$b]; }; } + + /** + * Return true if callback on any element returns truthy value, false otherwise + * + * @param callable $callback + * @param array $array + * + * @return boolean + */ + public static function some(callable $callback, array $array) + { + foreach ($array as $item) { + if (call_user_func($callback, $item)) { + return true; + } + } + + return false; + } } From 2af9c5ae7b8283ca09d3a265cabd5e2ccf164853 Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Mon, 7 Dec 2015 11:18:29 +0100 Subject: [PATCH 087/471] BAP-9430: Report builder filters does not contain aggregation columns - distinguish aggregation column filter from field condition filter in ui --- src/Oro/Bundle/QueryDesignerBundle/Model/GroupNode.php | 1 + .../Resources/public/js/aggregated-field-condition.js | 4 +++- .../Resources/translations/jsmessages.en.yml | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Oro/Bundle/QueryDesignerBundle/Model/GroupNode.php b/src/Oro/Bundle/QueryDesignerBundle/Model/GroupNode.php index c2a49065b64..a667a8ac105 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Model/GroupNode.php +++ b/src/Oro/Bundle/QueryDesignerBundle/Model/GroupNode.php @@ -3,6 +3,7 @@ namespace Oro\Bundle\QueryDesignerBundle\Model; use LogicException; + use Oro\Component\PhpUtils\ArrayUtil; class GroupNode diff --git a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js index a77eda64262..abe5981a336 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js +++ b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js @@ -1,8 +1,9 @@ define([ 'jquery', 'underscore', + 'orotranslation/js/translator', 'oroquerydesigner/js/field-condition' -], function($, _) { +], function($, _, __) { 'use strict'; $.widget('oroauditquerydesigner.aggregatedFieldCondition', $.oroquerydesigner.fieldCondition, { @@ -13,6 +14,7 @@ define([ _create: function() { var data = this.element.data('value'); + this.element.append('
            ').text(__('oro.querydesigner.aggregated_field_condition.label')); this.$fieldChoice = $('').addClass(this.options.fieldChoiceClass); this.$filterContainer = $('').addClass(this.options.filterContainerClass); this.element.append(this.$fieldChoice, this.$filterContainer); diff --git a/src/Oro/Bundle/QueryDesignerBundle/Resources/translations/jsmessages.en.yml b/src/Oro/Bundle/QueryDesignerBundle/Resources/translations/jsmessages.en.yml index f3fb0d0d6a3..51df1e7cecf 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Resources/translations/jsmessages.en.yml +++ b/src/Oro/Bundle/QueryDesignerBundle/Resources/translations/jsmessages.en.yml @@ -8,3 +8,4 @@ oro: field_condition: filter_not_supported: "This filter is not supported." lifetimevalue_filter_not_supported: "The filtering by the LifetimeValue is not supported yet." + aggregated_field_condition.label: "Aggregation column" From 358763ff5897d6141c37e6950561e94a445ae17c Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Mon, 7 Dec 2015 11:22:36 +0100 Subject: [PATCH 088/471] BAP-9430: Report builder filters does not contain aggregation columns - fix phpcs --- .../Grid/Extension/GroupingOrmFilterDatasourceAdapterTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Oro/Bundle/QueryDesignerBundle/Tests/Unit/Grid/Extension/GroupingOrmFilterDatasourceAdapterTest.php b/src/Oro/Bundle/QueryDesignerBundle/Tests/Unit/Grid/Extension/GroupingOrmFilterDatasourceAdapterTest.php index 4039e653645..a0c5e62e6f3 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Tests/Unit/Grid/Extension/GroupingOrmFilterDatasourceAdapterTest.php +++ b/src/Oro/Bundle/QueryDesignerBundle/Tests/Unit/Grid/Extension/GroupingOrmFilterDatasourceAdapterTest.php @@ -449,7 +449,8 @@ public function testComplexComputedExpr() . 'GROUP BY u.status ' . 'HAVING COUNT(u.id) = 0 AND ' . '(COUNT(u.id) = 1 AND ' - . '((COUNT(u.id) = 2 AND (COUNT(u.id) = 3 OR COUNT(u.id) = 4)) OR COUNT(u.id) = 5 OR (COUNT(u.id) = 6 AND COUNT(u.id) = 7)) AND ' + . '((COUNT(u.id) = 2 AND (COUNT(u.id) = 3 OR COUNT(u.id) = 4)) ' + . 'OR COUNT(u.id) = 5 OR (COUNT(u.id) = 6 AND COUNT(u.id) = 7)) AND ' . 'COUNT(u.id) = 8)', $qb->getDQL() ); From 1bb0c869e495f47ef984e1965079eb1f41f35fa1 Mon Sep 17 00:00:00 2001 From: Sergey Zhuravel Date: Mon, 7 Dec 2015 12:40:48 +0200 Subject: [PATCH 089/471] BB-1385: Add listener to add both backend and frontend audits to backend grid - update docs --- UPGRADE-1.9.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/UPGRADE-1.9.md b/UPGRADE-1.9.md index d37048ea11d..06dbfab9059 100644 --- a/UPGRADE-1.9.md +++ b/UPGRADE-1.9.md @@ -29,6 +29,34 @@ UPGRADE FROM 1.8 to 1.9 ####DataAuditBundle - `Oro\Bundle\DataauditBundle\EventListener\KernelListener` added to the class cache and constructor have container as performance improvement +- Grid merge uses distinct policy + +``` +grid-name: + source: + value: 1 +grid-name: + source: + value: 2 +``` + +will result + +``` +grid-name: + source: + value: 2 +``` + +instead of + +``` +grid-name: + source: + value: + - 1 + - 2 +``` ####DataGridBundle - Services with tag `oro_datagrid.extension.formatter.property` was marked as private From 9b330f7f3495cdce3301e23f72d9ee56d5895874 Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Mon, 7 Dec 2015 11:49:01 +0100 Subject: [PATCH 090/471] BAP-9454: having support pro GroupingOrmFilterDatasourceAdapter - fix tests --- .../Bundle/QueryDesignerBundle/Model/ExpressionBuilder.php | 5 +++++ .../Tests/Unit/Grid/Extension/OrmDatasourceExtensionTest.php | 5 ++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Oro/Bundle/QueryDesignerBundle/Model/ExpressionBuilder.php b/src/Oro/Bundle/QueryDesignerBundle/Model/ExpressionBuilder.php index 87eb34adf49..55b56a71fb2 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Model/ExpressionBuilder.php +++ b/src/Oro/Bundle/QueryDesignerBundle/Model/ExpressionBuilder.php @@ -21,6 +21,11 @@ class ExpressionBuilder public function beginGroup($condition) { $groupNode = new GroupNode($condition); + if (!$this->currentGroupNode && $this->groupNode) { + $this->groupNode = $groupNode->addNode($this->groupNode); + $this->currentGroupNode = $this->groupNode; + } + if ($this->currentGroupNode) { $this->currentGroupNode->addNode($groupNode); $this->currentGroupNode = $groupNode; diff --git a/src/Oro/Bundle/QueryDesignerBundle/Tests/Unit/Grid/Extension/OrmDatasourceExtensionTest.php b/src/Oro/Bundle/QueryDesignerBundle/Tests/Unit/Grid/Extension/OrmDatasourceExtensionTest.php index e9c48f4c291..9cf29548b33 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Tests/Unit/Grid/Extension/OrmDatasourceExtensionTest.php +++ b/src/Oro/Bundle/QueryDesignerBundle/Tests/Unit/Grid/Extension/OrmDatasourceExtensionTest.php @@ -188,10 +188,9 @@ function ($matches) use (&$counter) { . 'FROM Oro\Bundle\QueryDesignerBundle\Tests\Unit\Fixtures\Models\CMS\CmsUser user ' . 'INNER JOIN user.address address ' //. 'WHERE (user_name IS NULL OR NOT(user_name LIKE :string1)) AND (' - . 'WHERE user_name NOT LIKE :string1 AND (' + . 'WHERE user_name NOT LIKE :string1 AND ' . '(user_status < :datetime2 OR user_status > :datetime3) AND ' - . '(address.country LIKE :string4 OR address.city LIKE :string5 OR address.zip LIKE :string6)' - . ')', + . '(address.country LIKE :string4 OR address.city LIKE :string5 OR address.zip LIKE :string6)', $result ); } From 3b0e8c6850a3a3ef4269f63b38511ef3535503b3 Mon Sep 17 00:00:00 2001 From: Sergey Zhuravel Date: Mon, 7 Dec 2015 13:13:11 +0200 Subject: [PATCH 091/471] BB-1385: Add listener to add both backend and frontend audits to backend grid - fix test --- .../Bundle/DataAuditBundle/Tests/Functional/ControllersTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Oro/Bundle/DataAuditBundle/Tests/Functional/ControllersTest.php b/src/Oro/Bundle/DataAuditBundle/Tests/Functional/ControllersTest.php index 005886c106f..a0bf586b3f6 100644 --- a/src/Oro/Bundle/DataAuditBundle/Tests/Functional/ControllersTest.php +++ b/src/Oro/Bundle/DataAuditBundle/Tests/Functional/ControllersTest.php @@ -129,7 +129,7 @@ public function testAuditHistory($result) $this->assertEquals($this->userData[$key], $value); } - $this->assertEquals('John Doe - admin@example.com', $result['author']); + $this->assertEquals('John Doe - admin@example.com', $result['author']); } protected function clearResult($result) From fbcbfdfcac593db59c5195bb52924e9646d60d99 Mon Sep 17 00:00:00 2001 From: Oleksandr Nechyporenko Date: Mon, 7 Dec 2015 16:45:09 +0200 Subject: [PATCH 092/471] BB-1536: Create register page based on new layout - fix using non-initialize property --- .../Layout/Form/DependencyInjectionFormAccessor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Oro/Bundle/LayoutBundle/Layout/Form/DependencyInjectionFormAccessor.php b/src/Oro/Bundle/LayoutBundle/Layout/Form/DependencyInjectionFormAccessor.php index 94590bbb0fc..272a3813bde 100644 --- a/src/Oro/Bundle/LayoutBundle/Layout/Form/DependencyInjectionFormAccessor.php +++ b/src/Oro/Bundle/LayoutBundle/Layout/Form/DependencyInjectionFormAccessor.php @@ -67,7 +67,7 @@ public function toString() */ public function setFormData($formData) { - if (!$this->form->isSubmitted()) { + if (!$this->getForm()->isSubmitted()) { $this->getForm()->setData($formData); } } From 11b304cbe7400a6e01f9d6a7150f9abc9c2c99c3 Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Tue, 8 Dec 2015 10:43:07 +0100 Subject: [PATCH 093/471] BAP-9454: having support pro GroupingOrmFilterDatasourceAdapter - make ui work correctly with aggregated-field-condition --- .../Resources/public/js/condition-builder.js | 65 +++++++++++++++++-- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/condition-builder.js b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/condition-builder.js index 64e4426f2d9..5de4cc0b5fd 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/condition-builder.js +++ b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/condition-builder.js @@ -82,6 +82,7 @@ define(['jquery', 'underscore', 'orotranslation/js/translator', 'jquery-ui', opts.criteriaList = $.extend({}, opts.sortable, opts.criteriaList); opts.criteriaList.start = $.proxy(this._onCriteriaGrab, this); opts.criteriaList.stop = $.proxy(this._onCriteriaDrop, this); + opts.criteriaList.change = $.proxy(this._onCriteriaChange, this); opts.conditionsGroup.start = $.proxy(this._onConditionsGroupGrab, this); opts.conditionsGroup.stop = $.proxy(this._onConditionsGroupDrop, this); @@ -133,6 +134,9 @@ define(['jquery', 'underscore', 'orotranslation/js/translator', 'jquery-ui', _initControl: function() { var $content = this._createConditionContent(this.$rootCondition, this.$rootCondition.data('value')); this._initConditionsGroup($content); + this._updateRootAggregatedCondition( + this.$rootCondition.find('>[data-criteria]').has('[data-criteria=aggregated-condition-item]') + ); this._updateOperators(); this.$rootCondition.data('initialized', true); }, @@ -229,12 +233,46 @@ define(['jquery', 'underscore', 'orotranslation/js/translator', 'jquery-ui', this.$rootCondition.find('.hide-operator').removeClass('hide-operator'); }, - syncDropAreaOver: function() { + _onCriteriaChange: function(e, ui) { + if (this._isPlaceholderInValidPosition(e, ui)) { + this.element.find('.sortable-placeholder').removeClass('hide'); + } else { + this.element.find('.sortable-placeholder').addClass('hide'); + } + }, + + syncDropAreaOver: function(e, ui) { this.$rootCondition .parent() .toggleClass('drop-area-over', this.$rootCondition.find('.sortable-placeholder').length !== 0); }, + _isPlaceholderInValidPosition: function(e, ui) { + if (ui.item.data('criteria') === 'aggregated-condition-item') { + if ( + ui.placeholder.closest('[condition-type=aggregated-condition-item]').length || + (ui.placeholder.is(':last-child') && + !this.element.find('[condition-type=aggregated-condition-item]').length) + ) { + return true; + } + + return false; + } else if (ui.item.data('criteria') !== 'conditions-group' && + ui.placeholder.closest('[condition-type=aggregated-condition-item]').length + ) { + return false; + } + + return !ui.placeholder.prev('[condition-type=aggregated-condition-item]').length; + }, + + _updateRootAggregatedCondition: function($condition) { + $condition + .attr('condition-type', 'aggregated-condition-item') + .find('>.operator [data-value=OR]').parent('li').remove(); + }, + _getCriteriaOrigin: function(criteria) { var $criteria = this.$criteriaList.find('[data-criteria="' + criteria + '"]'); return $criteria.data('origin') || $criteria; @@ -329,8 +367,23 @@ define(['jquery', 'underscore', 'orotranslation/js/translator', 'jquery-ui', var $condition; // new condition if (ui.sender && ui.sender.is(this.$criteriaList)) { - $condition = this._createCondition(ui.item.data('criteria')); - $condition.insertBefore(ui.item); + if (ui.placeholder.hasClass('hide')) { + return; + } + + var criteria = ui.item.data('criteria'); + if (criteria === 'aggregated-condition-item' && + !this.element.find('[condition-type=aggregated-condition-item]').length + ) { + var $conditionsGroup = this._createCondition('conditions-group'); + this._updateRootAggregatedCondition($conditionsGroup); + $conditionsGroup.insertBefore(ui.item); + $condition = this._createCondition(ui.item.data('criteria')); + $conditionsGroup.find('.conditions-group').append($condition); + } else { + $condition = this._createCondition(criteria); + $condition.insertBefore(ui.item); + } } else { $condition = ui.item; } @@ -353,7 +406,7 @@ define(['jquery', 'underscore', 'orotranslation/js/translator', 'jquery-ui', // add operators to proper conditions $conditions.filter(':not(:first-child)').not(':has(>.operator)') .each(function() { - self._initConditionOperation(this); + self._initConditionOperation($(this)); }).trigger('changed'); }, @@ -365,7 +418,9 @@ define(['jquery', 'underscore', 'orotranslation/js/translator', 'jquery-ui', .prependTo($condition) .dropdownSelect({ buttonClass: 'btn btn-mini', - options: this.options.operations, + options: $condition.is('[condition-type=aggregated-condition-item]') + ? [operation] + : this.options.operations, selected: operation }); }, From ac8e986a05f386cda38fac556b9fd48d4c854b74 Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Tue, 8 Dec 2015 11:20:55 +0100 Subject: [PATCH 094/471] BAP-9454: having support pro GroupingOrmFilterDatasourceAdapter - implement mixing of computed restrictions with uncomputed in some cases --- .../Model/ExpressionBuilder.php | 35 +++++++++++++------ .../QueryDesignerBundle/Model/GroupNode.php | 2 +- ...GroupingOrmFilterDatasourceAdapterTest.php | 23 ++++++++++++ 3 files changed, 49 insertions(+), 11 deletions(-) diff --git a/src/Oro/Bundle/QueryDesignerBundle/Model/ExpressionBuilder.php b/src/Oro/Bundle/QueryDesignerBundle/Model/ExpressionBuilder.php index 55b56a71fb2..5d653c09c75 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Model/ExpressionBuilder.php +++ b/src/Oro/Bundle/QueryDesignerBundle/Model/ExpressionBuilder.php @@ -62,11 +62,12 @@ public function applyRestrictions(QueryBuilder $qb) return; } - $expr = $this->resolveGroupNode($this->groupNode); - if ($this->groupNode->isComputed()) { - $qb->andHaving($expr); - } else { - $qb->andWhere($expr); + list($uncomputedExpr, $computedExpr) = $this->resolveGroupNode($this->groupNode); + if ($computedExpr) { + $qb->andHaving($computedExpr); + } + if ($uncomputedExpr) { + $qb->andWhere($uncomputedExpr); } } @@ -77,17 +78,31 @@ public function applyRestrictions(QueryBuilder $qb) */ protected function resolveGroupNode(GroupNode $gNode) { - $restrictions = []; + $uncomputedRestrictions = []; + $computedRestrictions = []; + foreach ($gNode->getChildren() as $node) { if ($node instanceof Restriction) { - $restrictions[] = $node; + if ($node->isComputed()) { + $computedRestrictions[] = $node; + } else { + $uncomputedRestrictions[] = $node; + } } else { - $expr = $this->resolveGroupNode($node); - $restrictions[] = new Restriction($expr, $node->getCondition(), $node->isComputed()); + list($uncomputedExpr, $computedExpr) = $this->resolveGroupNode($node); + if ($uncomputedExpr) { + $uncomputedRestrictions[] = new Restriction($uncomputedExpr, $node->getCondition(), $node->isComputed()); + } + if ($computedExpr) { + $computedRestrictions[] = new Restriction($computedExpr, $node->getCondition(), $node->isComputed()); + } } } - return $this->createExprFromRestrictions($restrictions); + return [ + $this->createExprFromRestrictions($uncomputedRestrictions), + $this->createExprFromRestrictions($computedRestrictions), + ]; } /** diff --git a/src/Oro/Bundle/QueryDesignerBundle/Model/GroupNode.php b/src/Oro/Bundle/QueryDesignerBundle/Model/GroupNode.php index a667a8ac105..03c2213aa26 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Model/GroupNode.php +++ b/src/Oro/Bundle/QueryDesignerBundle/Model/GroupNode.php @@ -90,7 +90,7 @@ function ($node) { ); if ($computed && $unComputed) { - throw new LogicException('Mixing ofcomputed nodes with uncomputed is not implemented'); + throw new LogicException('Mixing of computed nodes with uncomputed is not implemented'); } return $computed; diff --git a/src/Oro/Bundle/QueryDesignerBundle/Tests/Unit/Grid/Extension/GroupingOrmFilterDatasourceAdapterTest.php b/src/Oro/Bundle/QueryDesignerBundle/Tests/Unit/Grid/Extension/GroupingOrmFilterDatasourceAdapterTest.php index a0c5e62e6f3..f29b3118b4d 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Tests/Unit/Grid/Extension/GroupingOrmFilterDatasourceAdapterTest.php +++ b/src/Oro/Bundle/QueryDesignerBundle/Tests/Unit/Grid/Extension/GroupingOrmFilterDatasourceAdapterTest.php @@ -455,4 +455,27 @@ public function testComplexComputedExpr() $qb->getDQL() ); } + + public function testComputedWithUnComputedRestrictionsTogether() + { + $qb = new QueryBuilder($this->getTestEntityManager()); + $qb->select(['u.status, COUNT(u.id)']) + ->from('Oro\Bundle\QueryDesignerBundle\Tests\Unit\Fixtures\Models\CMS\CmsUser', 'u') + ->groupBy('u.status'); + $ds = new GroupingOrmFilterDatasourceAdapter($qb); + + $ds->addRestriction($qb->expr()->eq('u.id', '1'), FilterUtility::CONDITION_AND); + $ds->addRestriction($qb->expr()->eq('u.id', '2'), FilterUtility::CONDITION_AND); + $ds->addRestriction($qb->expr()->eq('COUNT(u.id)', '3'), FilterUtility::CONDITION_AND, true); + $ds->addRestriction($qb->expr()->eq('COUNT(u.id)', '4'), FilterUtility::CONDITION_OR, true); + $ds->applyRestrictions(); + + $this->assertEquals( + 'SELECT u.status, COUNT(u.id) FROM Oro\Bundle\QueryDesignerBundle\Tests\Unit\Fixtures\Models\CMS\CmsUser u ' + . 'WHERE u.id = 1 AND u.id = 2 ' + . 'GROUP BY u.status ' + . 'HAVING COUNT(u.id) = 3 OR COUNT(u.id) = 4', + $qb->getDQL() + ); + } } From 1e94bdf4c3c5d4b8695bc4d2c0814ac05c4be384 Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Tue, 8 Dec 2015 12:05:24 +0100 Subject: [PATCH 095/471] BAP-9454: having support for GroupingOrmFilterDatasourceAdapter - fix aggregated condition in ui --- .../Resources/public/js/condition-builder.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/condition-builder.js b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/condition-builder.js index 5de4cc0b5fd..c5e6319768c 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/condition-builder.js +++ b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/condition-builder.js @@ -134,9 +134,6 @@ define(['jquery', 'underscore', 'orotranslation/js/translator', 'jquery-ui', _initControl: function() { var $content = this._createConditionContent(this.$rootCondition, this.$rootCondition.data('value')); this._initConditionsGroup($content); - this._updateRootAggregatedCondition( - this.$rootCondition.find('>[data-criteria]').has('[data-criteria=aggregated-condition-item]') - ); this._updateOperators(); this.$rootCondition.data('initialized', true); }, @@ -376,7 +373,6 @@ define(['jquery', 'underscore', 'orotranslation/js/translator', 'jquery-ui', !this.element.find('[condition-type=aggregated-condition-item]').length ) { var $conditionsGroup = this._createCondition('conditions-group'); - this._updateRootAggregatedCondition($conditionsGroup); $conditionsGroup.insertBefore(ui.item); $condition = this._createCondition(ui.item.data('criteria')); $conditionsGroup.find('.conditions-group').append($condition); @@ -442,6 +438,9 @@ define(['jquery', 'underscore', 'orotranslation/js/translator', 'jquery-ui', _onChanged: function() { this._setSourceValue(this.$rootCondition.data('value')); + this._updateRootAggregatedCondition( + this.$rootCondition.find('>[data-criteria]').has('[data-criteria=aggregated-condition-item]') + ); }, _setSourceValue: function(value) { From a657ef16e589d85fec8a5e89821acf09d26df0e9 Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Tue, 8 Dec 2015 14:54:26 +0100 Subject: [PATCH 096/471] BAP-9454: having support for GroupingOrmFilterDatasourceAdapter - add validation of GroupNode --- .../Model/ExpressionBuilder.php | 23 +++- .../QueryDesignerBundle/Model/GroupNode.php | 47 +++++-- ...GroupingOrmFilterDatasourceAdapterTest.php | 27 +++- .../Unit/Validator/GroupNodeValidatorTest.php | 117 ++++++++++++++++++ .../Constraints/GroupNodeConstraint.php | 19 +++ .../Validator/GroupNodeValidator.php | 62 ++++++++++ src/Oro/Component/PhpUtils/ArrayUtil.php | 18 +++ .../PhpUtils/Tests/Unit/ArrayUtilTest.php | 84 +++++++++++++ 8 files changed, 386 insertions(+), 11 deletions(-) create mode 100644 src/Oro/Bundle/QueryDesignerBundle/Tests/Unit/Validator/GroupNodeValidatorTest.php create mode 100644 src/Oro/Bundle/QueryDesignerBundle/Validator/Constraints/GroupNodeConstraint.php create mode 100644 src/Oro/Bundle/QueryDesignerBundle/Validator/GroupNodeValidator.php diff --git a/src/Oro/Bundle/QueryDesignerBundle/Model/ExpressionBuilder.php b/src/Oro/Bundle/QueryDesignerBundle/Model/ExpressionBuilder.php index 5d653c09c75..1cb379edf93 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Model/ExpressionBuilder.php +++ b/src/Oro/Bundle/QueryDesignerBundle/Model/ExpressionBuilder.php @@ -2,19 +2,33 @@ namespace Oro\Bundle\QueryDesignerBundle\Model; +use LogicException; + use Doctrine\ORM\Query\Expr; use Doctrine\ORM\QueryBuilder; +use Symfony\Component\Validator\Validation; +use Symfony\Component\Validator\ValidatorInterface; + use Oro\Bundle\FilterBundle\Filter\FilterUtility; +use Oro\Bundle\QueryDesignerBundle\Validator\Constraints\GroupNodeConstraint; class ExpressionBuilder { + /** @var ValidatorInterface */ + protected $validator; + /** @var GroupNode */ protected $groupNode; /** @var GroupNode */ protected $currentGroupNode; + public function __construct() + { + $this->validator = Validation::createValidator(); + } + /** * @param string $condition */ @@ -62,6 +76,11 @@ public function applyRestrictions(QueryBuilder $qb) return; } + $violationList = $this->validator->validate($this->groupNode, new GroupNodeConstraint()); + foreach ($violationList as $violation) { + throw new LogicException($violation->getMessage()); + } + list($uncomputedExpr, $computedExpr) = $this->resolveGroupNode($this->groupNode); if ($computedExpr) { $qb->andHaving($computedExpr); @@ -91,10 +110,10 @@ protected function resolveGroupNode(GroupNode $gNode) } else { list($uncomputedExpr, $computedExpr) = $this->resolveGroupNode($node); if ($uncomputedExpr) { - $uncomputedRestrictions[] = new Restriction($uncomputedExpr, $node->getCondition(), $node->isComputed()); + $uncomputedRestrictions[] = new Restriction($uncomputedExpr, $node->getCondition(), false); } if ($computedExpr) { - $computedRestrictions[] = new Restriction($computedExpr, $node->getCondition(), $node->isComputed()); + $computedRestrictions[] = new Restriction($computedExpr, $node->getCondition(), true); } } } diff --git a/src/Oro/Bundle/QueryDesignerBundle/Model/GroupNode.php b/src/Oro/Bundle/QueryDesignerBundle/Model/GroupNode.php index 03c2213aa26..5483c7d53ae 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Model/GroupNode.php +++ b/src/Oro/Bundle/QueryDesignerBundle/Model/GroupNode.php @@ -2,12 +2,14 @@ namespace Oro\Bundle\QueryDesignerBundle\Model; -use LogicException; - use Oro\Component\PhpUtils\ArrayUtil; class GroupNode { + const TYPE_COMPUTED = 'computed'; + const TYPE_UNCOMPUTED = 'uncomputed'; + const TYPE_MIXED = 'mixed'; + /** @var string*/ protected $condition; @@ -27,6 +29,8 @@ public function __construct($condition) /** * @param GroupNode|Restriction $node + * + * @return $this */ public function addNode($node) { @@ -34,14 +38,20 @@ public function addNode($node) if ($node instanceof GroupNode) { $node->setParent($this); } + + return $this; } /** * @param GroupNode $node + * + * @return $this */ public function setParent(GroupNode $node) { $this->parentNode = $node; + + return $this; } /** @@ -69,14 +79,31 @@ public function getCondition() } /** - * @return bool - * - * @throws LogicException If computed restrictions are mixed with uncomputed + * @return string */ - public function isComputed() + public function getType() { + $mixed = ArrayUtil::some( + function ($node) { + if ($node instanceof GroupNode) { + return $node->getType() === GroupNode::TYPE_MIXED; + } + + return false; + }, + $this->nodes + ); + + if ($mixed) { + return GroupNode::TYPE_MIXED; + } + $computed = ArrayUtil::some( function ($node) { + if ($node instanceof GroupNode) { + return $node->getType() === GroupNode::TYPE_COMPUTED; + } + return $node->isComputed(); }, $this->nodes @@ -84,15 +111,19 @@ function ($node) { $unComputed = ArrayUtil::some( function ($node) { + if ($node instanceof GroupNode) { + return $node->getType() === GroupNode::TYPE_UNCOMPUTED; + } + return !$node->isComputed(); }, $this->nodes ); if ($computed && $unComputed) { - throw new LogicException('Mixing of computed nodes with uncomputed is not implemented'); + return static::TYPE_MIXED; } - return $computed; + return $computed ? static::TYPE_COMPUTED : static::TYPE_UNCOMPUTED; } } diff --git a/src/Oro/Bundle/QueryDesignerBundle/Tests/Unit/Grid/Extension/GroupingOrmFilterDatasourceAdapterTest.php b/src/Oro/Bundle/QueryDesignerBundle/Tests/Unit/Grid/Extension/GroupingOrmFilterDatasourceAdapterTest.php index f29b3118b4d..26c58c75af7 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Tests/Unit/Grid/Extension/GroupingOrmFilterDatasourceAdapterTest.php +++ b/src/Oro/Bundle/QueryDesignerBundle/Tests/Unit/Grid/Extension/GroupingOrmFilterDatasourceAdapterTest.php @@ -5,7 +5,6 @@ use Doctrine\ORM\QueryBuilder; use Oro\Bundle\TestFrameworkBundle\Test\Doctrine\ORM\OrmTestCase; - use Oro\Bundle\FilterBundle\Filter\FilterUtility; use Oro\Bundle\QueryDesignerBundle\Grid\Extension\GroupingOrmFilterDatasourceAdapter; @@ -478,4 +477,30 @@ public function testComputedWithUnComputedRestrictionsTogether() $qb->getDQL() ); } + + /** + * @expectedException LogicException + * @expectedExceptionMessage Computed conditions cannot be mixed with uncomputed. + */ + public function testComputedWithUnComputedRestrictionsTogetherShouldReturnExceptionWhenRestrictionsAreMixed() + { + $qb = new QueryBuilder($this->getTestEntityManager()); + $qb->select(['u.status, COUNT(u.id)']) + ->from('Oro\Bundle\QueryDesignerBundle\Tests\Unit\Fixtures\Models\CMS\CmsUser', 'u') + ->groupBy('u.status'); + $ds = new GroupingOrmFilterDatasourceAdapter($qb); + + $ds->addRestriction($qb->expr()->eq('u.id', '1'), FilterUtility::CONDITION_AND); + $ds->addRestriction($qb->expr()->eq('COUNT(u.id)', '2'), FilterUtility::CONDITION_AND, true); + $ds->addRestriction($qb->expr()->eq('COUNT(u.id)', '3'), FilterUtility::CONDITION_OR); + $ds->applyRestrictions(); + + $this->assertEquals( + 'SELECT u.status, COUNT(u.id) FROM Oro\Bundle\QueryDesignerBundle\Tests\Unit\Fixtures\Models\CMS\CmsUser u ' + . 'WHERE u.id = 1 AND u.id = 2 ' + . 'GROUP BY u.status ' + . 'HAVING COUNT(u.id) = 3 OR COUNT(u.id) = 4', + $qb->getDQL() + ); + } } diff --git a/src/Oro/Bundle/QueryDesignerBundle/Tests/Unit/Validator/GroupNodeValidatorTest.php b/src/Oro/Bundle/QueryDesignerBundle/Tests/Unit/Validator/GroupNodeValidatorTest.php new file mode 100644 index 00000000000..4ee1aab9b5f --- /dev/null +++ b/src/Oro/Bundle/QueryDesignerBundle/Tests/Unit/Validator/GroupNodeValidatorTest.php @@ -0,0 +1,117 @@ +executionContext = $this->getMock('\Symfony\Component\Validator\ExecutionContextInterface'); + + $this->validator = new GroupNodeValidator(); + $this->validator->initialize($this->executionContext); + + $this->constraint = new GroupNodeConstraint(); + } + + /** + * @dataProvider validGroupNodesProvider + */ + public function testValidGroupNodes(GroupNode $node) + { + $this->executionContext->expects($this->never()) + ->method('addViolation'); + + $this->validator->validate($node, $this->constraint); + } + + public function validGroupNodesProvider() + { + return [ + [new GroupNode(FilterUtility::CONDITION_AND)], + [ + (new GroupNode(FilterUtility::CONDITION_AND)) + ->addNode(new Restriction('a = 5', FilterUtility::CONDITION_AND, false)) + ], + [ + (new GroupNode(FilterUtility::CONDITION_AND)) + ->addNode(new Restriction('a = 5', FilterUtility::CONDITION_AND, true)) + ], + [ + (new GroupNode(FilterUtility::CONDITION_AND)) + ->addNode(new Restriction('a = 5', FilterUtility::CONDITION_AND, false)) + ->addNode(new Restriction('a = 5', FilterUtility::CONDITION_AND, true)) + ], + [ + (new GroupNode(FilterUtility::CONDITION_AND)) + ->addNode( + (new GroupNode(FilterUtility::CONDITION_AND)) + ->addNode(new Restriction('a = 5', FilterUtility::CONDITION_AND, false)) + ) + ->addNode(new Restriction('a = 5', FilterUtility::CONDITION_AND, false)) + ->addNode(new Restriction('a = 5', FilterUtility::CONDITION_AND, true)) + ], + [ + (new GroupNode(FilterUtility::CONDITION_AND)) + ->addNode(new Restriction('a = 5', FilterUtility::CONDITION_AND, false)) + ->addNode( + (new GroupNode(FilterUtility::CONDITION_AND)) + ->addNode(new Restriction('a = 5', FilterUtility::CONDITION_AND, true)) + ) + ], + ]; + } + + /** + * @dataProvider invalidGroupNodesProvider + */ + public function testInvalidGroupNodes(GroupNode $node) + { + $this->executionContext->expects($this->once()) + ->method('addViolation') + ->with($this->constraint->mixedConditionsMessage); + + $this->validator->validate($node, $this->constraint); + } + + public function invalidGroupNodesProvider() + { + return [ + [ + (new GroupNode(FilterUtility::CONDITION_AND)) + ->addNode(new Restriction('a = 5', FilterUtility::CONDITION_AND, true)) + ->addNode(new Restriction('a = 5', FilterUtility::CONDITION_AND, false)) + ], + [ + (new GroupNode(FilterUtility::CONDITION_AND)) + ->addNode( + (new GroupNode(FilterUtility::CONDITION_AND)) + ->addNode(new Restriction('a = 5', FilterUtility::CONDITION_AND, true)) + ) + ->addNode(new Restriction('a = 5', FilterUtility::CONDITION_AND, false)) + ->addNode(new Restriction('a = 5', FilterUtility::CONDITION_AND, true)) + ], + [ + (new GroupNode(FilterUtility::CONDITION_AND)) + ->addNode( + (new GroupNode(FilterUtility::CONDITION_AND)) + ->addNode(new Restriction('a = 5', FilterUtility::CONDITION_AND, false)) + ->addNode(new Restriction('a = 5', FilterUtility::CONDITION_AND, true)) + ) + ->addNode(new Restriction('a = 5', FilterUtility::CONDITION_AND, false)) + ->addNode(new Restriction('a = 5', FilterUtility::CONDITION_AND, false)) + ], + ]; + } +} diff --git a/src/Oro/Bundle/QueryDesignerBundle/Validator/Constraints/GroupNodeConstraint.php b/src/Oro/Bundle/QueryDesignerBundle/Validator/Constraints/GroupNodeConstraint.php new file mode 100644 index 00000000000..fc41c512826 --- /dev/null +++ b/src/Oro/Bundle/QueryDesignerBundle/Validator/Constraints/GroupNodeConstraint.php @@ -0,0 +1,19 @@ +isValid($value)) { + $this->context->addViolation($constraint->mixedConditionsMessage); + } + } + + /** + * @param GroupNode $rootNode + * + * @return boolean + */ + protected function isValid(GroupNode $rootNode) + { + if ($rootNode->getType() !== GroupNode::TYPE_MIXED) { + return true; + } + + $types = array_map( + function ($node) { + if ($node instanceof GroupNode) { + return $node->getType(); + } + + return $node->isComputed() ? GroupNode::TYPE_COMPUTED : GroupNode::TYPE_UNCOMPUTED; + }, + $rootNode->getChildren() + ); + + if (in_array(GroupNode::TYPE_MIXED, $types)) { + return false; + } + + $computedTypes = ArrayUtil::dropWhile( + function($type) { + return $type === GroupNode::TYPE_UNCOMPUTED; + }, + $types + ); + + return !in_array(GroupNode::TYPE_UNCOMPUTED, $computedTypes); + } +} diff --git a/src/Oro/Component/PhpUtils/ArrayUtil.php b/src/Oro/Component/PhpUtils/ArrayUtil.php index 6318d1585cf..49e19fa9e61 100644 --- a/src/Oro/Component/PhpUtils/ArrayUtil.php +++ b/src/Oro/Component/PhpUtils/ArrayUtil.php @@ -211,4 +211,22 @@ public static function some(callable $callback, array $array) return false; } + + /** + * Return copy of the array starting with item for which callback returns falsity value + * + * @param callable $callback + * + * @param array $array + */ + public static function dropWhile(callable $callback, array $array) + { + foreach ($array as $key => $value) { + if (!call_user_func($callback, $value)) { + return array_slice($array, $key); + } + } + + return []; + } } diff --git a/src/Oro/Component/PhpUtils/Tests/Unit/ArrayUtilTest.php b/src/Oro/Component/PhpUtils/Tests/Unit/ArrayUtilTest.php index dad84748f03..7d842f5873d 100644 --- a/src/Oro/Component/PhpUtils/Tests/Unit/ArrayUtilTest.php +++ b/src/Oro/Component/PhpUtils/Tests/Unit/ArrayUtilTest.php @@ -455,6 +455,90 @@ public function testSortByCallable() ); } + /** + * @dataProvider someProvider + */ + public function testSome(callable $callback, array $array, $expectedResult) + { + $this->assertSame($expectedResult, ArrayUtil::some($callback, $array)); + } + + public function someProvider() + { + return [ + [ + function ($item) { + return $item === 1; + }, + [0, 1, 2, 3, 4], + true, + ], + [ + function ($item) { + return $item === 0; + }, + [0, 1, 2, 3, 4], + true, + ], + [ + function ($item) { + return $item === 4; + }, + [0, 1, 2, 3, 4], + true, + ], + [ + function ($item) { + return $item === 5; + }, + [0, 1, 2, 3, 4], + false, + ], + ]; + } + + /** + * @dataProvider dropWhileProvider + */ + public function testDropWhile(callable $callback, array $array, $expectedResult) + { + $this->assertEquals($expectedResult, ArrayUtil::dropWhile($callback, $array)); + } + + public function dropWhileProvider() + { + return [ + [ + function ($item) { + return $item !== 2; + }, + [], + [], + ], + [ + function ($item) { + return $item !== 2; + }, + [0, 1, 2, 3, 4, 5], + [2, 3, 4, 5], + ], + [ + function ($item) { + return $item !== 0; + }, + [0, 1, 2, 3, 4, 5], + [0, 1, 2, 3, 4, 5], + ], + [ + function ($item) { + return $item !== 6; + }, + [0, 1, 2, 3, 4, 5], + [], + ], + ]; + } + /** * @param object $obj * From 2893b51c3a97c1b0e3289e372d9fed63dbc90364 Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Tue, 8 Dec 2015 15:01:54 +0100 Subject: [PATCH 097/471] BAP-9454: having support for GroupingOrmFilterDatasourceAdapter - fix cs issues --- .../Resources/public/js/condition-builder.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/condition-builder.js b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/condition-builder.js index c5e6319768c..4a59b722fb3 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/condition-builder.js +++ b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/condition-builder.js @@ -414,9 +414,8 @@ define(['jquery', 'underscore', 'orotranslation/js/translator', 'jquery-ui', .prependTo($condition) .dropdownSelect({ buttonClass: 'btn btn-mini', - options: $condition.is('[condition-type=aggregated-condition-item]') - ? [operation] - : this.options.operations, + options: $condition.is('[condition-type=aggregated-condition-item]') ? + [operation] : this.options.operations, selected: operation }); }, From 25907ca972a00216e89204610a9c976c913389ef Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Tue, 8 Dec 2015 15:38:38 +0100 Subject: [PATCH 098/471] BAP-9454: having support for GroupingOrmFilterDatasourceAdapter --- .../QueryDesignerBundle/Model/ExpressionBuilder.php | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/Oro/Bundle/QueryDesignerBundle/Model/ExpressionBuilder.php b/src/Oro/Bundle/QueryDesignerBundle/Model/ExpressionBuilder.php index 1cb379edf93..3ffb79640dc 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Model/ExpressionBuilder.php +++ b/src/Oro/Bundle/QueryDesignerBundle/Model/ExpressionBuilder.php @@ -35,17 +35,13 @@ public function __construct() public function beginGroup($condition) { $groupNode = new GroupNode($condition); - if (!$this->currentGroupNode && $this->groupNode) { - $this->groupNode = $groupNode->addNode($this->groupNode); - $this->currentGroupNode = $this->groupNode; - } - if ($this->currentGroupNode) { $this->currentGroupNode->addNode($groupNode); $this->currentGroupNode = $groupNode; + } elseif ($this->groupNode) { + $this->currentGroupNode = $this->groupNode = $groupNode->addNode($this->groupNode); } else { - $this->groupNode = $groupNode; - $this->currentGroupNode = $this->groupNode; + $this->currentGroupNode = $this->groupNode = $groupNode; } } From fcc4ea545eb66c5880615bf70f8b90a9f166c129 Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Tue, 8 Dec 2015 15:51:03 +0100 Subject: [PATCH 099/471] BAP-9454: having support for GroupingOrmFilterDatasourceAdapter - fix js tests --- .../Resources/public/js/condition-builder.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/condition-builder.js b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/condition-builder.js index 4a59b722fb3..d41793c134d 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/condition-builder.js +++ b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/condition-builder.js @@ -364,7 +364,7 @@ define(['jquery', 'underscore', 'orotranslation/js/translator', 'jquery-ui', var $condition; // new condition if (ui.sender && ui.sender.is(this.$criteriaList)) { - if (ui.placeholder.hasClass('hide')) { + if (ui.placeholder && ui.placeholder.hasClass('hide')) { return; } From bd559d06a60d8b92dbfeef4bcd4100249942ab96 Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Tue, 8 Dec 2015 15:54:43 +0100 Subject: [PATCH 100/471] BAP-9454: having support for GroupingOrmFilterDatasourceAdapter - fix phpcs --- .../Bundle/QueryDesignerBundle/Validator/GroupNodeValidator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Oro/Bundle/QueryDesignerBundle/Validator/GroupNodeValidator.php b/src/Oro/Bundle/QueryDesignerBundle/Validator/GroupNodeValidator.php index 0478f5eba16..70e64e0e931 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Validator/GroupNodeValidator.php +++ b/src/Oro/Bundle/QueryDesignerBundle/Validator/GroupNodeValidator.php @@ -51,7 +51,7 @@ function ($node) { } $computedTypes = ArrayUtil::dropWhile( - function($type) { + function ($type) { return $type === GroupNode::TYPE_UNCOMPUTED; }, $types From f45598014d01917b07e577fba33acbc38d457a3b Mon Sep 17 00:00:00 2001 From: Oleksandr Nechyporenko Date: Tue, 8 Dec 2015 17:10:45 +0200 Subject: [PATCH 101/471] BB-1661: Fix tests - fix unit tests --- .../Unit/Layout/Form/DependencyInjectionFormAccessorTest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Form/DependencyInjectionFormAccessorTest.php b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Form/DependencyInjectionFormAccessorTest.php index 2e239f0441f..4cd777658ed 100644 --- a/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Form/DependencyInjectionFormAccessorTest.php +++ b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Form/DependencyInjectionFormAccessorTest.php @@ -173,7 +173,7 @@ public function testSetFormData() $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); $form->expects($this->once())->method('setData')->with($data); - $form->expects($this->at(2))->method('getData')->willReturn($data); + $form->expects($this->once())->method('getData')->willReturn($data); $this->container->expects($this->once()) ->method('get') @@ -182,7 +182,6 @@ public function testSetFormData() $formAccessor = new DependencyInjectionFormAccessor($this->container, self::FORM_SERVICE_ID); - $this->assertNull($formAccessor->getForm()->getData()); $formAccessor->setFormData($data); $this->assertEquals($data, $formAccessor->getForm()->getData()); } From 4bf864a3a3e0e0d7b2c3bb96bb651c55a4f18c91 Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Tue, 8 Dec 2015 17:37:17 +0100 Subject: [PATCH 102/471] BAP-9454: having support for GroupingOrmFilterDatasourceAdapter - refactoring, small fixes --- .../Model/ExpressionBuilder.php | 2 +- .../QueryDesigner/AbstractQueryConverter.php | 10 +++---- .../public/js/aggregated-field-condition.js | 9 +----- .../EventListener/SegmentSubscriber.php | 13 +-------- .../aggregated_field_condition.html.twig | 2 +- .../aggregated-field-condition-extension.js | 29 ------------------- .../Unit/Resolver/SystemAwareResolverTest.php | 13 ++++++++- 7 files changed, 21 insertions(+), 57 deletions(-) diff --git a/src/Oro/Bundle/QueryDesignerBundle/Model/ExpressionBuilder.php b/src/Oro/Bundle/QueryDesignerBundle/Model/ExpressionBuilder.php index 3ffb79640dc..a9a3f4c9f95 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Model/ExpressionBuilder.php +++ b/src/Oro/Bundle/QueryDesignerBundle/Model/ExpressionBuilder.php @@ -89,7 +89,7 @@ public function applyRestrictions(QueryBuilder $qb) /** * @param GroupNode $gNode * - * @return mixed Expr + * @return mixed Expr[] Where first item is uncomputed expr and 2nd one is computed */ protected function resolveGroupNode(GroupNode $gNode) { diff --git a/src/Oro/Bundle/QueryDesignerBundle/QueryDesigner/AbstractQueryConverter.php b/src/Oro/Bundle/QueryDesignerBundle/QueryDesigner/AbstractQueryConverter.php index 539cb46536f..6a9e075c421 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/QueryDesigner/AbstractQueryConverter.php +++ b/src/Oro/Bundle/QueryDesignerBundle/QueryDesigner/AbstractQueryConverter.php @@ -445,15 +445,15 @@ protected function prepareColumnAliases() * * @return array Where array has elements: string|FunctionInterface|null, string|null */ - protected function createColumnFuncion(array $column) + protected function createColumnFunction(array $column) { if (!empty($column['func'])) { - $function = $this->functionProvider->getFunction( + $function = $this->functionProvider->getFunction( $column['func']['name'], $column['func']['group_name'], $column['func']['group_type'] ); - $functionExpr = $function['expr']; + $functionExpr = $function['expr']; if (isset($function['return_type'])) { $functionReturnType = $function['return_type']; } else { @@ -474,7 +474,7 @@ protected function addSelectStatement() foreach ($this->definition['columns'] as $column) { $columnName = $column['name']; $fieldName = $this->getFieldName($columnName); - list($functionExpr, $functionReturnType) = $this->createColumnFuncion($column); + list($functionExpr, $functionReturnType) = $this->createColumnFunction($column); $isDistinct = !empty($column['distinct']); $tableAlias = $this->getTableAliasForColumn($columnName); if (isset($column['label'])) { @@ -667,7 +667,7 @@ protected function processFilter($filter) if (isset($filter['func'])) { $column['func'] = $filter['func']; } - list($functionExpr) = $this->createColumnFuncion($column); + list($functionExpr) = $this->createColumnFunction($column); $this->addWhereCondition( $this->getEntityClassName($columnName), diff --git a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js index abe5981a336..ba7a7d8db85 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js +++ b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js @@ -57,15 +57,8 @@ define([ }, _updateFieldChoice: function() { - var self = this; var fieldChoice = this.$fieldChoice.fieldChoice().data('oroentity-fieldChoice'); - var originalSelect2Data = fieldChoice._select2Data; - - fieldChoice._select2Data = function(path) { - originalSelect2Data.apply(this, arguments); - - return self._getAggregatedSelectData(); - }; + fieldChoice._select2Data = _.bind(this._getAggregatedSelectData, this); }, _getAggregatedSelectData: function() { diff --git a/src/Oro/Bundle/ReportBundle/EventListener/SegmentSubscriber.php b/src/Oro/Bundle/ReportBundle/EventListener/SegmentSubscriber.php index ab727b29a60..1021b8b5d45 100644 --- a/src/Oro/Bundle/ReportBundle/EventListener/SegmentSubscriber.php +++ b/src/Oro/Bundle/ReportBundle/EventListener/SegmentSubscriber.php @@ -29,20 +29,9 @@ public function loadAggregatedFieldsWidgetOptions(WidgetOptionsLoadEvent $event) return; } - $widgetOptions = $event->getWidgetOptions(); - $fieldsLoader = $widgetOptions['fieldsLoader']; - $event->setWidgetOptions(array_merge_recursive( - $widgetOptions, + $event->getWidgetOptions(), [ - 'aggregatedFieldsLoader' => [ - 'entityChoice' => $fieldsLoader['entityChoice'], - 'loadingMaskParent' => $fieldsLoader['loadingMaskParent'], - 'router' => 'oro_api_querydesigner_aggregated_fields', - 'routingParams' => [], - 'fieldsData' => $fieldsLoader['fieldsData'], - 'confirmMessage' => $fieldsLoader['confirmMessage'], - ], 'extensions' => [ 'orosegment/js/app/components/aggregated-field-condition-extension', ], diff --git a/src/Oro/Bundle/ReportBundle/Resources/views/Segment/aggregated_field_condition.html.twig b/src/Oro/Bundle/ReportBundle/Resources/views/Segment/aggregated_field_condition.html.twig index 540efb11502..1b16c9e4fa3 100644 --- a/src/Oro/Bundle/ReportBundle/Resources/views/Segment/aggregated_field_condition.html.twig +++ b/src/Oro/Bundle/ReportBundle/Resources/views/Segment/aggregated_field_condition.html.twig @@ -3,7 +3,7 @@ select2: { placeholder: 'oro.query_designer.condition_builder.choose_entity_field'|trans }, - fieldsLoaderSelector: '#' ~ params.entity_choice_id ~ 'oro_api_querydesigner_aggregated_fields', + fieldsLoaderSelector: '[data-ftid=' ~ params.entity_choice_id ~ 'oro_api_querydesigner_fields_entity]' } } %} diff --git a/src/Oro/Bundle/SegmentBundle/Resources/public/js/app/components/aggregated-field-condition-extension.js b/src/Oro/Bundle/SegmentBundle/Resources/public/js/app/components/aggregated-field-condition-extension.js index 6d9d37d93fb..18d58297d42 100644 --- a/src/Oro/Bundle/SegmentBundle/Resources/public/js/app/components/aggregated-field-condition-extension.js +++ b/src/Oro/Bundle/SegmentBundle/Resources/public/js/app/components/aggregated-field-condition-extension.js @@ -6,19 +6,6 @@ define([ return { load: function(segment) { - segment.defaults = $.extend(true, segment.defaults, { - defaults: { - aggregatedFieldsLoader: { - loadingMaskParent: '', - router: null, - routingParams: {}, - fieldsData: [], - confirmMessage: '', - loadEvent: 'aggregatedFieldsLoaded' - } - } - }); - segment.configureFilters = _.wrap(segment.configureFilters, function(original) { var $criteria = $(this.options.filters.criteriaList); @@ -37,22 +24,6 @@ define([ original.apply(this, _.rest(arguments)); }); - - segment.initFieldsLoader = _.wrap(segment.initFieldsLoader, function(original) { - this.$aggregatedFieldsLoader = original.call(this, this.options.aggregatedFieldsLoader); - - return original.apply(this, _.rest(arguments)); - }); - - segment.initEntityChangeEvents = _.wrap(segment.initEntityChangeEvents, function(original) { - this.trigger( - this.options.aggregatedFieldsLoader.loadEvent, - this.$aggregatedFieldsLoader.val(), - this.$aggregatedFieldsLoader.fieldsLoader('getFieldsData') - ); - - return original.apply(this, _.rest(arguments)); - }); } }; }); diff --git a/src/Oro/Component/Config/Tests/Unit/Resolver/SystemAwareResolverTest.php b/src/Oro/Component/Config/Tests/Unit/Resolver/SystemAwareResolverTest.php index c66a01894e6..5fcd54133ec 100644 --- a/src/Oro/Component/Config/Tests/Unit/Resolver/SystemAwareResolverTest.php +++ b/src/Oro/Component/Config/Tests/Unit/Resolver/SystemAwareResolverTest.php @@ -82,7 +82,10 @@ public static function otherFunc() */ public function testResolve($config, $expected) { - $result = $this->resolver->resolve($config, array('testVar' => 'test context var')); + $result = $this->resolver->resolve($config, array( + 'testVar' => 'test context var', + 'testArray' => ['param' => 'param from array'], + )); $this->assertEquals($expected, $result); } @@ -205,6 +208,14 @@ public function resolveProvider() ), array('root' => array('node' => 'func3 + test context var + const1')), ), + 'service call with two parameters ($testArray.param$ and const)' => array( + array( + 'root' => array( + 'node' => '@test.service1->func3($testArray.param$, ' . self::STATIC_CLASS . '::CONST1)' + ) + ), + array('root' => array('node' => 'func3 + param from array + const1')), + ), 'service call with two parameters (const and const the same)' => array( array( 'root' => array( From 5b79307a772b022aae7617dc2869a263fa2580e4 Mon Sep 17 00:00:00 2001 From: Ramunas Date: Tue, 8 Dec 2015 23:48:25 +0200 Subject: [PATCH 103/471] disable xdebug and increase memory limit in travis config --- .travis.yml | 1 + travis.php.ini | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1d0261e2a5c..d34f4fd4717 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ env: - SYMFONY_VERSION=2.7.* before_script: + - phpenv config-rm xdebug.ini - composer self-update - phpenv config-add travis.php.ini - travis_wait composer require symfony/symfony:${SYMFONY_VERSION} --prefer-source --update-no-dev diff --git a/travis.php.ini b/travis.php.ini index 5e5483e2d6a..2a30f41864f 100644 --- a/travis.php.ini +++ b/travis.php.ini @@ -1 +1 @@ -memory_limit = 1024M +memory_limit = 2G From 0489e964f453e176e04c3214cdc4a8f337f2d98d Mon Sep 17 00:00:00 2001 From: Ramunas Date: Tue, 8 Dec 2015 23:53:48 +0200 Subject: [PATCH 104/471] enable travis cache for composer --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index d34f4fd4717..066b99df524 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,10 @@ php: env: - SYMFONY_VERSION=2.7.* +cache: + directories: + - $HOME/.composer/cache/files + before_script: - phpenv config-rm xdebug.ini - composer self-update From 71c591660e4c074ef3214a050bd27e3811200c38 Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Wed, 9 Dec 2015 11:14:09 +0100 Subject: [PATCH 105/471] BAP-9454: having support for GroupingOrmFilterDatasourceAdapter - fix css --- .../Resources/public/css/less/condition-builder.less | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/css/less/condition-builder.less b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/css/less/condition-builder.less index 6a392cbd42a..09372a86b1d 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/css/less/condition-builder.less +++ b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/css/less/condition-builder.less @@ -26,6 +26,7 @@ display: none; } .conditions-group { + width: 100%; list-style: none; margin: 0; padding: 0; @@ -256,12 +257,15 @@ width: 150px; } .right-area { + display: flex; width: calc(~"100% - 150px"); padding: 0 10px; border-left: 1px solid #c9c9c9; background-color: white; > div { position: relative; + display: flex; + width: 100%; } } } From 3e3fd82afb004438e68d7bbff732a8287e9962da Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Wed, 9 Dec 2015 12:35:55 +0100 Subject: [PATCH 106/471] BAP-9454: having support for GroupingOrmFilterDatasourceAdapter - fixed case when multiple columns with the same name are used with aggregated functions --- .../public/js/aggregated-field-condition.js | 49 ++++++++++++++++--- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js index ba7a7d8db85..a7397360b0c 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js +++ b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js @@ -32,9 +32,12 @@ define([ this.element.closest('.condition').find('.close').click(); } }, this); - if (data && data.columnName) { - this.selectField(data.columnName); - this._renderFilter(data.columnName); + if (data && data.columnName && data.func) { + var column = this._getColumnByNameAndFunc(data.columnName, data.func); + if (column) { + this.$fieldChoice.fieldChoice('setData', {id: column.get('name'), text: column.get('label')}); + this._renderFilter(column.get('name')); + } } this._on(this.$fieldChoice, { @@ -59,6 +62,24 @@ define([ _updateFieldChoice: function() { var fieldChoice = this.$fieldChoice.fieldChoice().data('oroentity-fieldChoice'); fieldChoice._select2Data = _.bind(this._getAggregatedSelectData, this); + + fieldChoice.setData = function(data) { + this.element.select2('data', data, true); + }; + + var self = this; + fieldChoice.getApplicableConditions = _.wrap( + fieldChoice.getApplicableConditions, + function(original) { + var conditions = original.apply(this, _.rest(arguments)); + var func = self._getCurrentFunc(); + if (func && func.name === 'Count') { + conditions.type = 'integer'; + } + + return conditions; + } + ); }, _getAggregatedSelectData: function() { @@ -83,7 +104,7 @@ define([ _onUpdate: function() { var value; var columnName = this._getColumnName(); - var columnFunc = this._getColumnFunc(columnName); + var columnFunc = this._getCurrentFunc(); if (this.filter && !this.filter.isEmptyValue() && !_.isEmpty(columnFunc)) { value = { @@ -103,8 +124,14 @@ define([ return this.element.find('input.select').select2('val'); }, - _getColumnFunc: function(columnName) { - var column = this.options.columnsCollection.findWhere({name: columnName}); + _getColumnLabel: function() { + var obj = this.element.find('input.select').select2('data'); + + return obj ? obj.text : undefined; + }, + + _getCurrentFunc: function() { + var column = this.options.columnsCollection.findWhere({label: this._getColumnLabel()}); if (_.isEmpty(column)) { return; } @@ -112,6 +139,16 @@ define([ return column.get('func'); }, + _getColumnByNameAndFunc: function(name, func) { + if (!func) { + return; + } + + return _.find(this.options.columnsCollection.where({name: name}), function(column) { + return column.get('func') && column.get('func').name === func.name; + }); + }, + _getFilterCriterion: function() { var criterion = this._superApply(arguments); $.extend(true, criterion, {'data': {'params': {'filter_by_having': true}}}); From 7363697d1adef6cf079a3452e98e1df53a637997 Mon Sep 17 00:00:00 2001 From: Ievgenii Myshusta Date: Wed, 9 Dec 2015 13:47:00 +0200 Subject: [PATCH 107/471] BAP-9073: Change cron strategy to prevent overflow jobs queue --- .../Bundle/CronBundle/Command/CronCommand.php | 16 +++++++++++- .../CronBundle/Entity/Manager/JobManager.php | 25 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/Oro/Bundle/CronBundle/Command/CronCommand.php b/src/Oro/Bundle/CronBundle/Command/CronCommand.php index 93800edd336..30a8f586e77 100644 --- a/src/Oro/Bundle/CronBundle/Command/CronCommand.php +++ b/src/Oro/Bundle/CronBundle/Command/CronCommand.php @@ -2,6 +2,7 @@ namespace Oro\Bundle\CronBundle\Command; +use Oro\Bundle\CronBundle\Entity\Manager\JobManager; use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -98,7 +99,7 @@ function ($element) use ($name) { /** * @todo Add "Oro timezone" setting as parameter to isDue method */ - if ($cron->isDue()) { + if ($cron->isDue() && !$this->hasJobInQueue($name)) { $job = new Job($name); $em->persist($job); @@ -114,4 +115,17 @@ function ($element) use ($name) { $output->writeln(''); $output->writeln('All commands finished'); } + + /** + * @param string $name + * + * @return bool + */ + protected function hasJobInQueue($name) + { + /** @var JobManager $jobManager */ + $jobManager = $this->getContainer()->get('oro_cron.job_manager'); + + return $jobManager->hasJobInQueue($name, '[]'); + } } diff --git a/src/Oro/Bundle/CronBundle/Entity/Manager/JobManager.php b/src/Oro/Bundle/CronBundle/Entity/Manager/JobManager.php index 2312761e7f4..2d601584fb1 100644 --- a/src/Oro/Bundle/CronBundle/Entity/Manager/JobManager.php +++ b/src/Oro/Bundle/CronBundle/Entity/Manager/JobManager.php @@ -121,4 +121,29 @@ public function getRunningJobsCount($commandName) ->getQuery() ->getSingleScalarResult(); } + + /** + * @param string $name + * @param null|string $args + * + * @return bool + */ + public function hasJobInQueue($name, $args = null) + { + $qb = $this->em->getRepository('JMSJobQueueBundle:Job') + ->createQueryBuilder('j'); + + $qb->select('count(j.id)') + ->andWhere('j.command=:command') + ->andWhere('j.state in (:stateName)') + ->setParameter('command', $name) + ->setParameter('stateName', [Job::STATE_RUNNING, Job::STATE_PENDING]); + + if ($args) { + $qb->andWhere($qb->expr()->like('cast(j.args as text)', ':args')); + $qb->setParameter('args', $args); + } + + return $qb->getQuery()->getSingleScalarResult() > 0; + } } From a5f646cdd1426beac141434fe59edf9e12df61b1 Mon Sep 17 00:00:00 2001 From: Ilya Antipenko Date: Wed, 9 Dec 2015 14:25:14 +0200 Subject: [PATCH 108/471] BB-1385: Add listener to add both backend and frontend audits to backend grid - Use full class name for mapping --- src/Oro/Bundle/DataAuditBundle/Entity/AuditField.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Oro/Bundle/DataAuditBundle/Entity/AuditField.php b/src/Oro/Bundle/DataAuditBundle/Entity/AuditField.php index 02fc5b05b8a..d0caae079fe 100644 --- a/src/Oro/Bundle/DataAuditBundle/Entity/AuditField.php +++ b/src/Oro/Bundle/DataAuditBundle/Entity/AuditField.php @@ -17,7 +17,11 @@ class AuditField extends ExtendAuditField /** * @var Audit * - * @ORM\ManyToOne(targetEntity="AbstractAudit", inversedBy="fields", cascade={"persist"}) + * @ORM\ManyToOne( + * targetEntity="Oro\Bundle\DataAuditBundle\Entity\AbstractAudit", + * inversedBy="fields", + * cascade={"persist"} + * ) * @ORM\JoinColumn(name="audit_id", referencedColumnName="id", nullable=false, onDelete="CASCADE") */ protected $audit; From c7220e9afccbf7edaed851f9dbd02608b960b127 Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Wed, 9 Dec 2015 14:32:17 +0100 Subject: [PATCH 109/471] BAP-9454: having support for GroupingOrmFilterDatasourceAdapter - use return_type property to determine type of the filter --- .../Bundle/QueryDesignerBundle/QueryDesigner/Manager.php | 7 ++++++- .../Resources/public/js/aggregated-field-condition.js | 4 ++-- .../Resources/public/js/function-choice.js | 9 +++++---- .../public/js/app/components/segment-component.js | 5 +++++ 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/Oro/Bundle/QueryDesignerBundle/QueryDesigner/Manager.php b/src/Oro/Bundle/QueryDesignerBundle/QueryDesigner/Manager.php index 0d7a5938564..5f9dba7714d 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/QueryDesigner/Manager.php +++ b/src/Oro/Bundle/QueryDesignerBundle/QueryDesigner/Manager.php @@ -217,11 +217,16 @@ protected function getMetadataForFunctions($groupType, $queryType) $hintText = empty($function['hint_label']) ? null // if a label is empty it means that this function should inherit a label : $this->translator->trans($function['hint_label']); - $functions[] = [ + $func = [ 'name' => $function['name'], 'label' => $nameText, 'title' => $hintText, ]; + if (isset($function['return_type'])) { + $func['return_type'] = $function['return_type']; + } + + $functions[] = $func; } $attr['functions'] = $functions; $result[$name] = $attr; diff --git a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js index a7397360b0c..760f2b0e6d8 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js +++ b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js @@ -73,8 +73,8 @@ define([ function(original) { var conditions = original.apply(this, _.rest(arguments)); var func = self._getCurrentFunc(); - if (func && func.name === 'Count') { - conditions.type = 'integer'; + if (func && func.return_type) { + conditions.type = func.return_type; } return conditions; diff --git a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/function-choice.js b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/function-choice.js index 60f30e496b9..477c60c8137 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/function-choice.js +++ b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/function-choice.js @@ -7,9 +7,10 @@ define(['jquery', 'underscore', 'jquery-ui'], function($, _) { $.widget('oroquerydesigner.functionChoice', { options: { fieldChoiceSelector: '', - optionTemplate: _.template(''), converters: [], aggregates: [] @@ -90,7 +91,7 @@ define(['jquery', 'underscore', 'jquery-ui'], function($, _) { }); _.each(functions, function(func) { - content += options.optionTemplate(func); + content += options.optionTemplate({data: func}); }); if (content !== '') { diff --git a/src/Oro/Bundle/SegmentBundle/Resources/public/js/app/components/segment-component.js b/src/Oro/Bundle/SegmentBundle/Resources/public/js/app/components/segment-component.js index f06bccc07f8..d014cdc385d 100644 --- a/src/Oro/Bundle/SegmentBundle/Resources/public/js/app/components/segment-component.js +++ b/src/Oro/Bundle/SegmentBundle/Resources/public/js/app/components/segment-component.js @@ -492,6 +492,11 @@ define(function(require) { group_type: $el.find(':selected').data('group_type'), group_name: $el.find(':selected').data('group_name') }; + + var returnType = $el.find(':selected').data('return_type'); + if (value && returnType) { + value.return_type = returnType; + } } return value; } From 880f52c4f99f681f3287d87d281cf993d623bfde Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Wed, 9 Dec 2015 15:54:06 +0100 Subject: [PATCH 110/471] BAP-9454: having support for GroupingOrmFilterDatasourceAdapter - fix trifles --- .../public/js/aggregated-field-condition.js | 18 ++++++++++++++---- .../Resources/translations/jsmessages.en.yml | 1 - 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js index 760f2b0e6d8..207956267cd 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js +++ b/src/Oro/Bundle/QueryDesignerBundle/Resources/public/js/aggregated-field-condition.js @@ -14,7 +14,6 @@ define([ _create: function() { var data = this.element.data('value'); - this.element.append('
            ').text(__('oro.querydesigner.aggregated_field_condition.label')); this.$fieldChoice = $('').addClass(this.options.fieldChoiceClass); this.$filterContainer = $('').addClass(this.options.filterContainerClass); this.element.append(this.$fieldChoice, this.$filterContainer); @@ -23,12 +22,12 @@ define([ this._updateFieldChoice(); this.options.columnsCollection.on('remove', function(model) { - if (model.get('name') === this._getColumnName()) { + if (model.get('label') === this._getColumnLabel()) { this.element.closest('.condition').find('.close').click(); } }, this); - this.options.columnsCollection.on('change:func', function(model) { - if (model.get('name') === this._getColumnName() && _.isEmpty(model.get('func'))) { + this.options.columnsCollection.on('change:func change:label', function(model) { + if (model._previousAttributes.label === this._getColumnLabel()) { this.element.closest('.condition').find('.close').click(); } }, this); @@ -68,6 +67,17 @@ define([ }; var self = this; + + fieldChoice.formatChoice = _.wrap(fieldChoice.formatChoice, function(original) { + var formatted = original.apply(this, _.rest(arguments)); + var func = self._getCurrentFunc(); + if (func && func.name) { + formatted += ' (' + func.name + ')'; + } + + return formatted; + }); + fieldChoice.getApplicableConditions = _.wrap( fieldChoice.getApplicableConditions, function(original) { diff --git a/src/Oro/Bundle/QueryDesignerBundle/Resources/translations/jsmessages.en.yml b/src/Oro/Bundle/QueryDesignerBundle/Resources/translations/jsmessages.en.yml index 51df1e7cecf..f3fb0d0d6a3 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Resources/translations/jsmessages.en.yml +++ b/src/Oro/Bundle/QueryDesignerBundle/Resources/translations/jsmessages.en.yml @@ -8,4 +8,3 @@ oro: field_condition: filter_not_supported: "This filter is not supported." lifetimevalue_filter_not_supported: "The filtering by the LifetimeValue is not supported yet." - aggregated_field_condition.label: "Aggregation column" From 8dcdce95f221da255f4fdfd60db18699678236eb Mon Sep 17 00:00:00 2001 From: Sergey Zhuravel Date: Thu, 10 Dec 2015 20:44:43 +0200 Subject: [PATCH 111/471] BB-1292: Enable Data Audit for Commerce Entities - update doc --- UPGRADE-1.9.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/UPGRADE-1.9.md b/UPGRADE-1.9.md index ff7c5b9d41d..698a3000a4a 100644 --- a/UPGRADE-1.9.md +++ b/UPGRADE-1.9.md @@ -28,7 +28,13 @@ UPGRADE FROM 1.8 to 1.9 - Removed class `Oro\Bundle\ConfigBundle\Manager\UserConfigManager` and service `oro_config.user_config_manager`. Use `oro_config.user` service instead. ####DataAuditBundle -- `Oro\Bundle\DataauditBundle\EventListener\KernelListener` added to the class cache and constructor have container as performance improvement +- `Oro\Bundle\DataAuditBundle\EventListener\KernelListener` added to the class cache and constructor have container as performance improvement +- `Oro\Bundle\DataAuditBundle\Entity\AbstractAudit` has `@InheritanceType("SINGLE_TABLE")` +- `audit-grid` and `audit-history-grid` based on `Oro\Bundle\DataAuditBundle\Entity\AbstractAudit` now. Make join to get your entity on grid + +####DataGridBundle +- Services with tag `oro_datagrid.extension.formatter.property` was marked as private +- JS collection models format changed to maintain compatibility with Backbone collections: now it is always list of models, and additional parameters are passed through the options - Grid merge uses distinct policy ``` @@ -57,12 +63,7 @@ grid-name: - 1 - 2 ``` -- `Oro\Bundle\DataAuditBundle\EventListener\KernelListener` added to the class cache and constructor have container as performance improvement -####DataGridBundle -- Services with tag `oro_datagrid.extension.formatter.property` was marked as private -- JS collection models format changed to maintain compatibility with Backbone collections: now it is always list of models, and additional parameters are passed through the options - ####DistributionBundle: - Fix `priority` attribute handling for `routing.options_resolver` tag to be conform Symfony standards. New behaviour: the higher the priority, the sooner the resolver gets executed. From 85221c99b2e7237faa7e121840a2ee958ae14d73 Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Fri, 11 Dec 2015 15:19:50 +0100 Subject: [PATCH 112/471] CRM-3997: Make all mailboxes available under System organization - revert some modifications due to their usage in other places --- .../Entity/Repository/MailboxRepository.php | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/Oro/Bundle/EmailBundle/Entity/Repository/MailboxRepository.php b/src/Oro/Bundle/EmailBundle/Entity/Repository/MailboxRepository.php index ca907664fac..2d20aef9060 100644 --- a/src/Oro/Bundle/EmailBundle/Entity/Repository/MailboxRepository.php +++ b/src/Oro/Bundle/EmailBundle/Entity/Repository/MailboxRepository.php @@ -33,6 +33,41 @@ public function findOneByEmail($email) return $this->findOneBy(['email' => $email]); } + /** + * Returns a list of mailboxes available to user. + * + * @param User|integer $user User or user id + * @param Organization $organization + * + * @return Collection|Mailbox[] Array or collection of Mailboxes + */ + public function findAvailableMailboxes($user, $organization) + { + $qb = $this->createAvailableMailboxesQuery($user, $organization); + + return $qb->getQuery()->getResult(); + } + + /** + * Returns a list of ids of mailboxes available to user. + * + * @param User|integer $user User or user id + * @param Organization $organization + * + * @return array Array of ids + */ + public function findAvailableMailboxIds($user, $organization) + { + $mailboxes = $this->findAvailableMailboxes($user, $organization); + + $ids = []; + foreach ($mailboxes as $mailbox) { + $ids[] = $mailbox->getId(); + } + + return $ids; + } + /** * Creates query for mailboxes available to user logged under organization. * If no organization is provided, does not filter by it (useful when looking for mailboxes across organizations). From c98955fa6936eec825c31868b47fc8d753c74bc3 Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Fri, 11 Dec 2015 15:21:49 +0100 Subject: [PATCH 113/471] CRM-3997: Make all mailboxes available under System organization - cs --- .../Bundle/EmailBundle/Entity/Repository/MailboxRepository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Oro/Bundle/EmailBundle/Entity/Repository/MailboxRepository.php b/src/Oro/Bundle/EmailBundle/Entity/Repository/MailboxRepository.php index 2d20aef9060..9acd4277551 100644 --- a/src/Oro/Bundle/EmailBundle/Entity/Repository/MailboxRepository.php +++ b/src/Oro/Bundle/EmailBundle/Entity/Repository/MailboxRepository.php @@ -48,7 +48,7 @@ public function findAvailableMailboxes($user, $organization) return $qb->getQuery()->getResult(); } - /** + /** * Returns a list of ids of mailboxes available to user. * * @param User|integer $user User or user id From c08e0300ff858bc8ff12627f32ea9c8f63283327 Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Fri, 11 Dec 2015 16:13:24 +0100 Subject: [PATCH 114/471] CRM-3997: Make all mailboxes available under System organization - fix unit tests --- .../Unit/Builder/Helper/EmailModelBuilderHelperTest.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Oro/Bundle/EmailBundle/Tests/Unit/Builder/Helper/EmailModelBuilderHelperTest.php b/src/Oro/Bundle/EmailBundle/Tests/Unit/Builder/Helper/EmailModelBuilderHelperTest.php index 0a066d5d4ab..9e80c7ffbf9 100644 --- a/src/Oro/Bundle/EmailBundle/Tests/Unit/Builder/Helper/EmailModelBuilderHelperTest.php +++ b/src/Oro/Bundle/EmailBundle/Tests/Unit/Builder/Helper/EmailModelBuilderHelperTest.php @@ -95,6 +95,10 @@ protected function setUp() $this->templating = $this->getMock('Symfony\Component\Templating\EngineInterface'); + $mailboxManager = $this->getMockBuilder('Oro\Bundle\EmailBundle\Entity\Manager\MailboxManager') + ->disableOriginalConstructor() + ->getMock(); + $this->helper = new EmailModelBuilderHelper( $this->entityRoutingHelper, $this->emailAddressHelper, @@ -103,7 +107,8 @@ protected function setUp() $this->emailAddressManager, $this->entityManager, $this->emailCacheManager, - $this->templating + $this->templating, + $mailboxManager ); } From ffb2723e2f925cfc50ff4fa97774c6930a027534 Mon Sep 17 00:00:00 2001 From: Ilya Antipenko Date: Mon, 14 Dec 2015 11:05:19 +0200 Subject: [PATCH 115/471] BB-1656: CRUD Tax Jurisdiction - Move country/region validation from AbstractAddress to ValidRegion constraint --- UPGRADE-1.9.md | 1 + .../AddressBundle/Entity/AbstractAddress.php | 4 + .../Resources/config/services.yml | 7 + .../Resources/config/validation.yml | 3 +- .../Constraints/ValidRegionValidatorTest.php | 125 ++++++++++++++++++ .../Validator/Constraints/ValidRegion.php | 26 ++++ .../Constraints/ValidRegionValidator.php | 32 +++++ 7 files changed, 196 insertions(+), 2 deletions(-) create mode 100644 src/Oro/Bundle/AddressBundle/Tests/Unit/Validator/Constraints/ValidRegionValidatorTest.php create mode 100644 src/Oro/Bundle/AddressBundle/Validator/Constraints/ValidRegion.php create mode 100644 src/Oro/Bundle/AddressBundle/Validator/Constraints/ValidRegionValidator.php diff --git a/UPGRADE-1.9.md b/UPGRADE-1.9.md index 6dd26054a16..cb478620ef2 100644 --- a/UPGRADE-1.9.md +++ b/UPGRADE-1.9.md @@ -13,6 +13,7 @@ UPGRADE FROM 1.8 to 1.9 ####AddressBundle - `oro_address.address.manager` service was marked as private +- Validation `AbstractAddress::isRegionValid` was moved to `Oro\Bundle\AddressBundle\Validator\Constraints\ValidRegion` constraint ####CalendarBundle - `oro_calendar.calendar_provider.user` service was marked as private diff --git a/src/Oro/Bundle/AddressBundle/Entity/AbstractAddress.php b/src/Oro/Bundle/AddressBundle/Entity/AbstractAddress.php index 71a6375a1a7..2afc8763f49 100644 --- a/src/Oro/Bundle/AddressBundle/Entity/AbstractAddress.php +++ b/src/Oro/Bundle/AddressBundle/Entity/AbstractAddress.php @@ -764,6 +764,10 @@ public function beforeSave() $this->updated = new \DateTime('now', new \DateTimeZone('UTC')); } + /** + * @param ExecutionContextInterface $context + * @deprecated Use \Oro\Bundle\AddressBundle\Validator\Constraints\ValidRegionValidator instead + */ public function isRegionValid(ExecutionContextInterface $context) { if ($this->getCountry() && $this->getCountry()->hasRegions() && !$this->region && !$this->regionText) { diff --git a/src/Oro/Bundle/AddressBundle/Resources/config/services.yml b/src/Oro/Bundle/AddressBundle/Resources/config/services.yml index 2bf7a1373b3..229d0463416 100644 --- a/src/Oro/Bundle/AddressBundle/Resources/config/services.yml +++ b/src/Oro/Bundle/AddressBundle/Resources/config/services.yml @@ -4,6 +4,8 @@ parameters: oro_address.address.manager.class: Oro\Bundle\AddressBundle\Entity\Manager\AddressManager oro_address.provider.address.class: Oro\Bundle\AddressBundle\Provider\AddressProvider oro_address.provider.phone.class: Oro\Bundle\AddressBundle\Provider\PhoneProvider + oro_address.validator.valid_region.class: Oro\Bundle\AddressBundle\Validator\Constraints\ValidRegionValidator + services: oro_address.address.manager: @@ -22,3 +24,8 @@ services: class: %oro_address.provider.phone.class% arguments: - @oro_entity_config.provider.extend + + oro_address.validator.valid_region: + class: %oro_address.validator.valid_region.class% + tags: + - { name: validator.constraint_validator, alias: oro_address_valid_region } diff --git a/src/Oro/Bundle/AddressBundle/Resources/config/validation.yml b/src/Oro/Bundle/AddressBundle/Resources/config/validation.yml index c73577ce9c4..f02c6b1a9b7 100644 --- a/src/Oro/Bundle/AddressBundle/Resources/config/validation.yml +++ b/src/Oro/Bundle/AddressBundle/Resources/config/validation.yml @@ -57,8 +57,7 @@ Oro\Bundle\AddressBundle\Entity\Address: postalCode: - NotBlank: ~ constraints: - - Callback: - methods: [isRegionValid] + - Oro\Bundle\AddressBundle\Validator\Constraints\ValidRegion: ~ Oro\Bundle\AddressBundle\Entity\AbstractEmail: properties: diff --git a/src/Oro/Bundle/AddressBundle/Tests/Unit/Validator/Constraints/ValidRegionValidatorTest.php b/src/Oro/Bundle/AddressBundle/Tests/Unit/Validator/Constraints/ValidRegionValidatorTest.php new file mode 100644 index 00000000000..874dc5075ed --- /dev/null +++ b/src/Oro/Bundle/AddressBundle/Tests/Unit/Validator/Constraints/ValidRegionValidatorTest.php @@ -0,0 +1,125 @@ +constraint = new ValidRegion(); + $this->context = $this->getMock('Symfony\Component\Validator\Context\ExecutionContextInterface'); + $this->validator = new ValidRegionValidator(); + $this->validator->initialize($this->context); + } + + public function tearDown() + { + unset($this->constraint, $this->context); + } + + public function testConfiguration() + { + $this->assertEquals('oro_address_valid_region', $this->constraint->validatedBy()); + $this->assertEquals(Constraint::CLASS_CONSTRAINT, $this->constraint->getTargets()); + } + + public function testGetDefaultOption() + { + $this->assertNull($this->constraint->getDefaultOption()); + } + + public function testIsRegionValidNoCountry() + { + $this->context->expects($this->never()) + ->method('addViolationAt'); + + $address = $this->createAddress(); + $this->validator->validate($address, $this->constraint); + } + + public function testIsRegionValidNoRegion() + { + /** @var \PHPUnit_Framework_MockObject_MockObject|Country $country */ + $country = $this->getMockBuilder('Oro\Bundle\AddressBundle\Entity\Country') + ->disableOriginalConstructor() + ->getMock(); + $country->expects($this->once()) + ->method('hasRegions') + ->will($this->returnValue(false)); + + $this->context->expects($this->never()) + ->method('addViolationAt'); + + $address = $this->createAddress(); + $address->setCountry($country); + $this->validator->validate($address, $this->constraint); + } + + public function testIsRegionValid() + { + /** @var \PHPUnit_Framework_MockObject_MockObject|Country $country */ + $country = $this->getMockBuilder('Oro\Bundle\AddressBundle\Entity\Country') + ->disableOriginalConstructor() + ->getMock(); + $country->expects($this->once()) + ->method('hasRegions') + ->will($this->returnValue(true)); + $country->expects($this->once()) + ->method('getName') + ->will($this->returnValue('Country')); + + $this->context->expects($this->once()) + ->method('getPropertyPath') + ->will($this->returnValue('test')); + $this->context->expects($this->once()) + ->method('addViolationAt') + ->with( + 'test.region', + $this->constraint->message, + ['{{ country }}' => 'Country'] + ); + + $address = $this->createAddress(); + $address->setCountry($country); + $this->validator->validate($address, $this->constraint); + } + + /** + * @param int|null $id + * @return AbstractAddress|\PHPUnit_Framework_MockObject_MockObject + */ + protected function createAddress($id = null) + { + /** @var AbstractAddress $result */ + $result = $this->getMockForAbstractClass('Oro\Bundle\AddressBundle\Entity\AbstractAddress'); + + if (null !== $id) { + $result->setId($id); + } + + return $result; + } +} diff --git a/src/Oro/Bundle/AddressBundle/Validator/Constraints/ValidRegion.php b/src/Oro/Bundle/AddressBundle/Validator/Constraints/ValidRegion.php new file mode 100644 index 00000000000..ca4bdf8ee88 --- /dev/null +++ b/src/Oro/Bundle/AddressBundle/Validator/Constraints/ValidRegion.php @@ -0,0 +1,26 @@ +getCountry() && $entity->getCountry()->hasRegions() && + !$entity->getRegion() && !$entity->getRegionText() + ) { + // do not allow saving text region in case when region was checked from list + // except when in base data region text existed + // another way region_text field will be null, logic are placed in form listener + $propertyPath = $this->context->getPropertyPath() . '.region'; + $this->context->addViolationAt( + $propertyPath, + $constraint->message, + ['{{ country }}' => $entity->getCountry()->getName()] + ); + } + } +} From a68fd286dd1c79f582c8744d2ea33b29c5e83a68 Mon Sep 17 00:00:00 2001 From: Ilya Antipenko Date: Mon, 14 Dec 2015 11:07:07 +0200 Subject: [PATCH 116/471] BB-1656: CRUD Tax Jurisdiction - Use validator instead of duplicate code --- .../AddressBundle/Entity/AbstractAddress.php | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/Oro/Bundle/AddressBundle/Entity/AbstractAddress.php b/src/Oro/Bundle/AddressBundle/Entity/AbstractAddress.php index 2afc8763f49..19c575706ee 100644 --- a/src/Oro/Bundle/AddressBundle/Entity/AbstractAddress.php +++ b/src/Oro/Bundle/AddressBundle/Entity/AbstractAddress.php @@ -9,6 +9,8 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface; +use Oro\Bundle\AddressBundle\Validator\Constraints\ValidRegion; +use Oro\Bundle\AddressBundle\Validator\Constraints\ValidRegionValidator; use Oro\Bundle\EntityConfigBundle\Metadata\Annotation\ConfigField; use Oro\Bundle\FormBundle\Entity\EmptyItem; use Oro\Bundle\LocaleBundle\Model\AddressInterface; @@ -770,17 +772,11 @@ public function beforeSave() */ public function isRegionValid(ExecutionContextInterface $context) { - if ($this->getCountry() && $this->getCountry()->hasRegions() && !$this->region && !$this->regionText) { - // do not allow saving text region in case when region was checked from list - // except when in base data region text existed - // another way region_text field will be null, logic are placed in form listener - $propertyPath = $context->getPropertyPath() . '.region'; - $context->addViolationAt( - $propertyPath, - 'State is required for country {{ country }}', - ['{{ country }}' => $this->getCountry()->getName()] - ); - } + // Use validator instead of duplicate code + $constraint = new ValidRegion(); + $validator = new ValidRegionValidator(); + $validator->initialize($context); + $validator->validate($this, $constraint); } /** From 88642c9989bdb98ab06a57ff6f3e4a59067e096c Mon Sep 17 00:00:00 2001 From: Sergey Zhuravel Date: Mon, 14 Dec 2015 11:36:08 +0200 Subject: [PATCH 117/471] BB-1539: 500 error on frontend api/rest/latest/windows request - decouple to managers --- .../Controller/Api/WindowsStateController.php | 107 +++------- .../Entity/AbstractWindowsState.php | 196 ++++++++++++++++++ .../AbstractWindowsStateRepository.php | 67 ++++++ .../Repository/WindowsStateRepository.php | 7 + .../WindowsBundle/Entity/WindowsState.php | 182 +--------------- .../Manager/WindowsStateManager.php | 121 +++++++++++ .../Manager/WindowsStateRequestManager.php | 99 +++++++++ .../Schema/OroWindowsBundleInstaller.php | 5 +- .../Schema/v1_1/OroWindowsBundle.php | 21 ++ .../Resources/config/services.yml | 28 ++- .../WindowsBundle/Twig/WindowsExtension.php | 163 ++++----------- 11 files changed, 608 insertions(+), 388 deletions(-) create mode 100644 src/Oro/Bundle/WindowsBundle/Entity/AbstractWindowsState.php create mode 100644 src/Oro/Bundle/WindowsBundle/Entity/Repository/AbstractWindowsStateRepository.php create mode 100644 src/Oro/Bundle/WindowsBundle/Entity/Repository/WindowsStateRepository.php create mode 100644 src/Oro/Bundle/WindowsBundle/Manager/WindowsStateManager.php create mode 100644 src/Oro/Bundle/WindowsBundle/Manager/WindowsStateRequestManager.php create mode 100644 src/Oro/Bundle/WindowsBundle/Migrations/Schema/v1_1/OroWindowsBundle.php diff --git a/src/Oro/Bundle/WindowsBundle/Controller/Api/WindowsStateController.php b/src/Oro/Bundle/WindowsBundle/Controller/Api/WindowsStateController.php index 57e3d4db915..f35779899bf 100644 --- a/src/Oro/Bundle/WindowsBundle/Controller/Api/WindowsStateController.php +++ b/src/Oro/Bundle/WindowsBundle/Controller/Api/WindowsStateController.php @@ -10,9 +10,8 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\HttpException; -use Symfony\Component\Security\Core\User\UserInterface; -use Oro\Bundle\WindowsBundle\Entity\WindowsState; +use Oro\Bundle\WindowsBundle\Manager\WindowsStateManager; /** * @RouteResource("windows") @@ -31,11 +30,10 @@ class WindowsStateController extends FOSRestController */ public function cgetAction() { - $items = $this->getDoctrine()->getRepository('OroWindowsBundle:WindowsState') - ->findBy(['user' => $this->getUser()]); + $items = $this->getWindowsStatesManager()->getWindowsStates(); return $this->handleView( - $this->view($items, is_array($items) ? Codes::HTTP_OK : Codes::HTTP_NOT_FOUND) + $this->view($items, $items ? Codes::HTTP_OK : Codes::HTTP_NOT_FOUND) ); } @@ -50,23 +48,14 @@ public function cgetAction() */ public function postAction() { - $postArray = $this->getPost(); - - /** @var $user UserInterface */ - $user = $this->getUser(); - $postArray['user'] = $user; - - /** @var $entity \Oro\Bundle\WindowsBundle\Entity\WindowsState */ - $entity = new WindowsState(); - $entity->setData($postArray['data']); - $entity->setUser($user); - - $manager = $this->getManager(); - $manager->persist($entity); - $manager->flush(); + try { + $id = $this->getWindowsStatesManager()->createWindowsState(); + } catch (\InvalidArgumentException $e) { + throw new HttpException(Codes::HTTP_BAD_REQUEST, 'Wrong JSON inside POST body'); + } return $this->handleView( - $this->view(['id' => $entity->getId()], Codes::HTTP_CREATED) + $this->view(['id' => $id], Codes::HTTP_CREATED) ); } @@ -74,32 +63,22 @@ public function postAction() * REST PUT * * @param int $windowId Window state id - * + * @return Response * @ApiDoc( * description="Update Windows state item", * resource=true * ) - * @return Response */ public function putAction($windowId) { - $postArray = $this->getPost(); - - /** @var $entity \Oro\Bundle\WindowsBundle\Entity\WindowsState */ - $entity = $this->getManager()->find('OroWindowsBundle:WindowsState', (int)$windowId); - if (!$entity) { - return $this->handleView($this->view([], Codes::HTTP_NOT_FOUND)); - } - if (!$this->validatePermissions($entity->getUser())) { - return $this->handleView($this->view([], Codes::HTTP_FORBIDDEN)); + try { + if (!$this->getWindowsStatesManager()->updateWindowsState($windowId)) { + return $this->handleView($this->view([], Codes::HTTP_NOT_FOUND)); + } + } catch (\InvalidArgumentException $e) { + throw new HttpException(Codes::HTTP_BAD_REQUEST, 'Wrong JSON inside POST body'); } - $entity->setData($postArray['data']); - - $em = $this->getManager(); - $em->persist($entity); - $em->flush(); - return $this->handleView($this->view([], Codes::HTTP_OK)); } @@ -116,60 +95,22 @@ public function putAction($windowId) */ public function deleteAction($windowId) { - /** @var $entity \Oro\Bundle\WindowsBundle\Entity\WindowsState */ - $entity = $this->getManager()->find('OroWindowsBundle:WindowsState', (int)$windowId); - if (!$entity) { - return $this->handleView($this->view([], Codes::HTTP_NOT_FOUND)); - } - if (!$this->validatePermissions($entity->getUser())) { - return $this->handleView($this->view([], Codes::HTTP_FORBIDDEN)); - } - - $em = $this->getManager(); - $em->remove($entity); - $em->flush(); - - return $this->handleView($this->view([], Codes::HTTP_NO_CONTENT)); - } - - /** - * @throws \Symfony\Component\HttpKernel\Exception\HttpException - * @return array - */ - protected function getPost() - { - $postArray = $this->getRequest()->request->all(); - if (is_array($postArray) && array_key_exists('data', $postArray)) { - if (array_key_exists('url', $postArray['data'])) { - $postArray['data']['cleanUrl'] - = str_replace($this->getRequest()->server->get('SCRIPT_NAME'), '', $postArray['data']['url']); + try { + if (!$this->getWindowsStatesManager()->deleteWindowsState($windowId)) { + return $this->handleView($this->view([], Codes::HTTP_NOT_FOUND)); } - } else { + } catch (\InvalidArgumentException $e) { throw new HttpException(Codes::HTTP_BAD_REQUEST, 'Wrong JSON inside POST body'); } - return $postArray; - } - /** - * Validate permissions - * - * TODO: refactor this to use Symfony2 ACL - * - * @param UserInterface $user - * @return bool - */ - protected function validatePermissions(UserInterface $user) - { - return $user->getUsername() == $this->getUser()->getUsername(); + return $this->handleView($this->view([], Codes::HTTP_NO_CONTENT)); } /** - * Get entity Manager - * - * @return \Doctrine\Common\Persistence\ObjectManager + * @retrun WindowsStateManager */ - protected function getManager() + protected function getWindowsStatesManager() { - return $this->getDoctrine()->getManagerForClass('OroWindowsBundle:WindowsState'); + return $this->get('oro_windows.manager.windows_state'); } } diff --git a/src/Oro/Bundle/WindowsBundle/Entity/AbstractWindowsState.php b/src/Oro/Bundle/WindowsBundle/Entity/AbstractWindowsState.php new file mode 100644 index 00000000000..1ce3d84abec --- /dev/null +++ b/src/Oro/Bundle/WindowsBundle/Entity/AbstractWindowsState.php @@ -0,0 +1,196 @@ +id; + } + + /** + * Set data + * + * @param mixed $data + * @return $this + */ + public function setData($data) + { + $this->data = $data; + + return $this; + } + + /** + * Get data + * + * @return mixed + */ + public function getData() + { + return json_decode($this->data, true); + } + + /** + * Get JSON data + * + * @return string + */ + public function getJsonData() + { + return $this->data; + } + + /** + * Set createdAt + * + * @param \DateTime $createdAt + * @return $this + */ + public function setCreatedAt($createdAt) + { + $this->createdAt = $createdAt; + + return $this; + } + + /** + * Get createdAt + * + * @return \DateTime + */ + public function getCreatedAt() + { + return $this->createdAt; + } + + /** + * Set updatedAt + * + * @param \DateTime $updatedAt + * @return $this + */ + public function setUpdatedAt($updatedAt) + { + $this->updatedAt = $updatedAt; + + return $this; + } + + /** + * Get updatedAt + * + * @return \DateTime + */ + public function getUpdatedAt() + { + return $this->updatedAt; + } + + /** + * Pre persist event handler + * + * @ORM\PrePersist + */ + public function doPrePersist() + { + $this->createdAt = new \DateTime('now', new \DateTimeZone('UTC')); + $this->updatedAt = $this->createdAt; + } + + /** + * Pre update event handler + * + * @ORM\PreUpdate + */ + public function doPreUpdate() + { + $this->updatedAt = new \DateTime('now', new \DateTimeZone('UTC')); + } + + /** + * Returns flag that window was rendered successfully + * + * @return bool + */ + public function isRenderedSuccessfully() + { + return $this->renderedSuccessfully; + } + + /** + * Sets flag that window was rendered successfully + * + * @param bool $renderedSuccessfully + */ + public function setRenderedSuccessfully($renderedSuccessfully) + { + $this->renderedSuccessfully = (bool)$renderedSuccessfully; + } + + /** + * Set user + * + * @param UserInterface $user + * @return $this + */ + abstract public function setUser(UserInterface $user); + + /** + * Get user + * + * @return UserInterface + */ + abstract public function getUser(); +} diff --git a/src/Oro/Bundle/WindowsBundle/Entity/Repository/AbstractWindowsStateRepository.php b/src/Oro/Bundle/WindowsBundle/Entity/Repository/AbstractWindowsStateRepository.php new file mode 100644 index 00000000000..0c8bab91649 --- /dev/null +++ b/src/Oro/Bundle/WindowsBundle/Entity/Repository/AbstractWindowsStateRepository.php @@ -0,0 +1,67 @@ +createQueryBuilder('w'); + + return $qb + ->delete() + ->where( + $qb->expr()->andX( + $qb->expr()->eq('w.user', ':user'), + $qb->expr()->eq('w.id', ':id') + ) + ) + ->setParameter('id', $windowId) + ->setParameter('user', $user) + ->getQuery() + ->execute(); + } + + /** + * @param UserInterface $user + * @param int $windowId + * @param array $data + * @return mixed + */ + public function update(UserInterface $user, $windowId, array $data) + { + $connection = $this->getEntityManager()->getConnection(); + + $qb = $this->createQueryBuilder('w'); + + return $qb + ->update() + ->set('w.data', ':data') + ->set('w.updatedAt', ':updatedAt') + ->where( + $qb->expr()->andX( + $qb->expr()->eq('w.user', ':user'), + $qb->expr()->eq('w.id', ':id') + ) + ) + ->setParameter('data', $connection->convertToDatabaseValue($data, Type::JSON_ARRAY)) + ->setParameter( + 'updatedAt', + $connection->convertToDatabaseValue(new \DateTime('now', new \DateTimeZone('UTC')), Type::DATETIME) + ) + ->setParameter('id', $windowId) + ->setParameter('user', $user) + ->getQuery() + ->execute(); + } +} diff --git a/src/Oro/Bundle/WindowsBundle/Entity/Repository/WindowsStateRepository.php b/src/Oro/Bundle/WindowsBundle/Entity/Repository/WindowsStateRepository.php new file mode 100644 index 00000000000..7d2c2281fe2 --- /dev/null +++ b/src/Oro/Bundle/WindowsBundle/Entity/Repository/WindowsStateRepository.php @@ -0,0 +1,7 @@ +id; - } - - /** - * Set data - * - * @param mixed $data - * @return \Oro\Bundle\WindowsBundle\Entity\WindowsState - */ - public function setData($data) - { - $this->data = json_encode($data); - - return $this; - } - - /** - * Get data - * - * @return mixed - */ - public function getData() - { - return json_decode($this->data, true); - } - - /** - * Get JSON data - * - * @return string - */ - public function getJsonData() - { - return $this->data; - } - - /** - * Set createdAt - * - * @param \DateTime $createdAt - * @return \Oro\Bundle\WindowsBundle\Entity\WindowsState - */ - public function setCreatedAt($createdAt) - { - $this->createdAt = $createdAt; - - return $this; - } - - /** - * Get createdAt - * - * @return \DateTime - */ - public function getCreatedAt() - { - return $this->createdAt; - } - - /** - * Set updatedAt - * - * @param \DateTime $updatedAt - * @return \Oro\Bundle\WindowsBundle\Entity\WindowsState - */ - public function setUpdatedAt($updatedAt) - { - $this->updatedAt = $updatedAt; - - return $this; - } - - /** - * Get updatedAt - * - * @return \DateTime - */ - public function getUpdatedAt() - { - return $this->updatedAt; - } - - /** - * Set user - * - * @param UserInterface $user - * @return \Oro\Bundle\WindowsBundle\Entity\WindowsState + * {@inheritdoc} */ - public function setUser(UserInterface $user = null) + public function setUser(UserInterface $user) { $this->user = $user; @@ -161,53 +34,10 @@ public function setUser(UserInterface $user = null) } /** - * Get user - * - * @return UserInterface + * {@inheritdoc} */ public function getUser() { return $this->user; } - - /** - * Pre persist event handler - * - * @ORM\PrePersist - */ - public function doPrePersist() - { - $this->createdAt = new \DateTime('now', new \DateTimeZone('UTC')); - $this->updatedAt = $this->createdAt; - } - - /** - * Pre update event handler - * - * @ORM\PreUpdate - */ - public function doPreUpdate() - { - $this->updatedAt = new \DateTime('now', new \DateTimeZone('UTC')); - } - - /** - * Returns flag that window was rendered successfully - * - * @return bool - */ - public function isRenderedSuccessfully() - { - return $this->renderedSuccessfully; - } - - /** - * Sets flag that window was rendered successfully - * - * @param bool $renderedSuccessfully - */ - public function setRenderedSuccessfully($renderedSuccessfully) - { - $this->renderedSuccessfully = (bool)$renderedSuccessfully; - } } diff --git a/src/Oro/Bundle/WindowsBundle/Manager/WindowsStateManager.php b/src/Oro/Bundle/WindowsBundle/Manager/WindowsStateManager.php new file mode 100644 index 00000000000..ecbe93923c6 --- /dev/null +++ b/src/Oro/Bundle/WindowsBundle/Manager/WindowsStateManager.php @@ -0,0 +1,121 @@ +tokenStorage = $tokenStorage; + $this->doctrineHelper = $doctrineHelper; + $this->className = $className; + $this->requestStateManager = $requestStateManager; + } + + /** + * @return int + */ + public function createWindowsState() + { + /** @var AbstractWindowsState $state */ + $state = $this->doctrineHelper->createEntityInstance($this->className); + $state->setData($this->requestStateManager->getData()); + $state->setUser($this->getUser()); + + $em = $this->doctrineHelper->getEntityManagerForClass($this->className); + $em->persist($state); + $em->flush($state); + + return $state->getId(); + } + + /** + * @param int $windowId + * @return bool + */ + public function updateWindowsState($windowId) + { + return (bool)$this->getRepository()->update($this->getUser(), $windowId, $this->requestStateManager->getData()); + } + + /** + * @param int $windowId + * @return bool + */ + public function deleteWindowsState($windowId) + { + return (bool)$this->getRepository()->delete($this->getUser(), $windowId); + } + + /** + * @return AbstractWindowsState[] + */ + public function getWindowsStates() + { + return $this->getRepository()->findBy(['user' => $this->getUser()]); + } + + /** + * @param int $windowId + * @return AbstractWindowsState + */ + public function getWindowsState($windowId) + { + return $this->getRepository()->findBy(['user' => $this->getUser(), 'id' => (int)$windowId]); + } + + /** + * @return AbstractWindowsStateRepository + */ + protected function getRepository() + { + return $this->doctrineHelper->getEntityRepository($this->className); + } + + /** + * @return UserInterface + * + * @see TokenInterface::getUser() + */ + public function getUser() + { + if (null === $token = $this->tokenStorage->getToken()) { + return null; + } + + if (!is_object($user = $token->getUser())) { + return null; + } + + return $user; + } +} diff --git a/src/Oro/Bundle/WindowsBundle/Manager/WindowsStateRequestManager.php b/src/Oro/Bundle/WindowsBundle/Manager/WindowsStateRequestManager.php new file mode 100644 index 00000000000..bf77f329504 --- /dev/null +++ b/src/Oro/Bundle/WindowsBundle/Manager/WindowsStateRequestManager.php @@ -0,0 +1,99 @@ +requestStack = $requestStack; + } + + /** + * @return array + */ + public function getData() + { + $request = $this->requestStack->getCurrentRequest(); + $data = $request->request->all(); + + if (!array_key_exists('data', $data)) { + throw new \InvalidArgumentException(); + } + + if (!array_key_exists('url', $data['data'])) { + throw new \InvalidArgumentException(); + } + + $cleanUrl = str_replace($request->server->get('SCRIPT_NAME'), '', $data['data']['url']); + if (!$cleanUrl) { + throw new \InvalidArgumentException(); + } + + $data['data']['cleanUrl'] = $cleanUrl; + + return $data['data']; + } + + /** + * @param array $data + * @return string + */ + public function getUri(array $data) + { + if (isset($data['cleanUrl'])) { + if (isset($data['type'])) { + $wid = isset($data['wid']) ? $data['wid'] : $this->getUniqueIdentifier(); + $uri = $this->getUrlWithContainer($data['cleanUrl'], $data['type'], $wid); + } else { + $uri = $data['cleanUrl']; + } + } + + if (!$uri) { + throw new \InvalidArgumentException(); + } + + return $uri; + } + + /** + * @param string $url + * @param string $container + * @param string $wid + * + * @return string + */ + protected function getUrlWithContainer($url, $container, $wid) + { + if (strpos($url, '_widgetContainer=') === false) { + $parts = parse_url($url); + $widgetPart = '_widgetContainer=' . $container . '&_wid=' . $wid; + if (array_key_exists('query', $parts)) { + $separator = $parts['query'] ? '&' : ''; + $newQuery = $parts['query'] . $separator . $widgetPart; + $url = str_replace($parts['query'], $newQuery, $url); + } else { + $url .= '?' . $widgetPart; + } + } + + return $url; + } + + /** + * @return string + */ + protected function getUniqueIdentifier() + { + return str_replace('.', '-', uniqid('windows_state', true)); + } +} diff --git a/src/Oro/Bundle/WindowsBundle/Migrations/Schema/OroWindowsBundleInstaller.php b/src/Oro/Bundle/WindowsBundle/Migrations/Schema/OroWindowsBundleInstaller.php index 86338116977..7e7548e4a46 100644 --- a/src/Oro/Bundle/WindowsBundle/Migrations/Schema/OroWindowsBundleInstaller.php +++ b/src/Oro/Bundle/WindowsBundle/Migrations/Schema/OroWindowsBundleInstaller.php @@ -3,6 +3,7 @@ namespace Oro\Bundle\WindowsBundle\Migrations\Schema; use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Types\Type; use Oro\Bundle\MigrationBundle\Migration\Installation; use Oro\Bundle\MigrationBundle\Migration\QueryBag; @@ -14,7 +15,7 @@ class OroWindowsBundleInstaller implements Installation */ public function getMigrationVersion() { - return 'v1_0'; + return 'v1_1'; } /** @@ -39,7 +40,7 @@ protected function createOroWindowsStateTable(Schema $schema) $table = $schema->createTable('oro_windows_state'); $table->addColumn('id', 'integer', ['autoincrement' => true]); $table->addColumn('user_id', 'integer', []); - $table->addColumn('data', 'text', []); + $table->addColumn('data', Type::JSON_ARRAY, []); $table->addColumn('created_at', 'datetime', []); $table->addColumn('updated_at', 'datetime', []); $table->setPrimaryKey(['id']); diff --git a/src/Oro/Bundle/WindowsBundle/Migrations/Schema/v1_1/OroWindowsBundle.php b/src/Oro/Bundle/WindowsBundle/Migrations/Schema/v1_1/OroWindowsBundle.php new file mode 100644 index 00000000000..914b4fb51af --- /dev/null +++ b/src/Oro/Bundle/WindowsBundle/Migrations/Schema/v1_1/OroWindowsBundle.php @@ -0,0 +1,21 @@ +getTable('oro_windows_state'); + $table->getColumn('data')->setType(Type::getType(Type::JSON_ARRAY)); + } +} diff --git a/src/Oro/Bundle/WindowsBundle/Resources/config/services.yml b/src/Oro/Bundle/WindowsBundle/Resources/config/services.yml index b0c1452c3d0..7017b40a8a7 100644 --- a/src/Oro/Bundle/WindowsBundle/Resources/config/services.yml +++ b/src/Oro/Bundle/WindowsBundle/Resources/config/services.yml @@ -1,11 +1,33 @@ parameters: + oro_windows.entity.windows_state.class: Oro\Bundle\WindowsBundle\Entity\WindowsState oro_windows.twig.extension.class: Oro\Bundle\WindowsBundle\Twig\WindowsExtension + oro_windows.manager.windows_state.class: Oro\Bundle\WindowsBundle\Manager\WindowsStateManager + oro_windows.manager.windows_state_request.class: Oro\Bundle\WindowsBundle\Manager\WindowsStateRequestManager services: oro_windows.twig.extension: class: %oro_windows.twig.extension.class% arguments: - - @security.context - - @doctrine.orm.entity_manager + - @oro_windows.manager.windows_state + - @oro_windows.manager.windows_state_request tags: - - { name: twig.extension } \ No newline at end of file + - { name: twig.extension } + + oro_windows.manager.windows_state.abstract: + class: %oro_windows.manager.windows_state.class% + abstract: true + private: true + arguments: + - @security.token_storage + - @oro_entity.doctrine_helper + - @oro_windows.manager.windows_state_request + + oro_windows.manager.windows_state: + parent: oro_windows.manager.windows_state.abstract + arguments: + - %oro_windows.entity.windows_state.class% + + oro_windows.manager.windows_state_request: + class: %oro_windows.manager.windows_state_request.class% + arguments: + - @request_stack diff --git a/src/Oro/Bundle/WindowsBundle/Twig/WindowsExtension.php b/src/Oro/Bundle/WindowsBundle/Twig/WindowsExtension.php index 470c67e0b5e..b4a66818b62 100644 --- a/src/Oro/Bundle/WindowsBundle/Twig/WindowsExtension.php +++ b/src/Oro/Bundle/WindowsBundle/Twig/WindowsExtension.php @@ -2,29 +2,17 @@ namespace Oro\Bundle\WindowsBundle\Twig; -use Doctrine\ORM\EntityManager; - use Symfony\Bridge\Twig\Extension\HttpKernelExtension; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Symfony\Component\Security\Core\SecurityContextInterface; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Oro\Bundle\WindowsBundle\Entity\WindowsState; +use Oro\Bundle\WindowsBundle\Entity\AbstractWindowsState; +use Oro\Bundle\WindowsBundle\Manager\WindowsStateManager; +use Oro\Bundle\WindowsBundle\Manager\WindowsStateRequestManager; class WindowsExtension extends \Twig_Extension { const EXTENSION_NAME = 'oro_windows'; - /** - * @var SecurityContextInterface - */ - protected $securityContext; - - /** - * @var EntityManager - */ - protected $em; - /** * Protect extension from infinite loop * @@ -32,16 +20,22 @@ class WindowsExtension extends \Twig_Extension */ protected $rendered = false; + /** @var WindowsStateManager */ + protected $windowsStateManager; + + /** @var WindowsStateRequestManager */ + protected $windowsStateRequestManager; + /** - * @param SecurityContextInterface $securityContext - * @param EntityManager $em + * @param WindowsStateManager $windowsStateManager + * @param WindowsStateRequestManager $windowsStateRequestManager */ public function __construct( - SecurityContextInterface $securityContext, - EntityManager $em + WindowsStateManager $windowsStateManager, + WindowsStateRequestManager $windowsStateRequestManager ) { - $this->securityContext = $securityContext; - $this->em = $em; + $this->windowsStateManager = $windowsStateManager; + $this->windowsStateRequestManager = $windowsStateRequestManager; } /** @@ -51,24 +45,24 @@ public function __construct( */ public function getFunctions() { - return array( + return [ 'oro_windows_restore' => new \Twig_Function_Method( $this, 'render', - array( - 'is_safe' => array('html'), - 'needs_environment' => true - ) + [ + 'is_safe' => ['html'], + 'needs_environment' => true, + ] ), 'oro_window_render_fragment' => new \Twig_Function_Method( $this, 'renderFragment', - array( - 'is_safe' => array('html'), - 'needs_environment' => true - ) - ) - ); + [ + 'is_safe' => ['html'], + 'needs_environment' => true, + ] + ), + ]; } /** @@ -80,32 +74,15 @@ public function getFunctions() */ public function render(\Twig_Environment $environment) { - if (!($user = $this->getUser()) || $this->rendered) { + if ($this->rendered) { return ''; } - $this->rendered = true; - - $windowStates = array(); - $removeWindowStates = array(); - $userWindowStates = $this->em->getRepository('OroWindowsBundle:WindowsState')->findBy(array('user' => $user)); - /** @var WindowsState $windowState */ - foreach ($userWindowStates as $windowState) { - $data = $windowState->getData(); - if (empty($data) || !isset($data['cleanUrl'])) { - $this->em->remove($windowState); - $removeWindowStates[] = $windowState; - } else { - $windowStates[] = $windowState; - } - } - if ($removeWindowStates) { - $this->em->flush($removeWindowStates); - } + $this->rendered = true; return $environment->render( 'OroWindowsBundle::states.html.twig', - array('windowStates' => $windowStates) + ['windowStates' => $this->windowsStateManager->getWindowsStates()] ); } @@ -113,83 +90,29 @@ public function render(\Twig_Environment $environment) * Renders fragment by window state. * * @param \Twig_Environment $environment - * @param WindowsState $windowState + * @param AbstractWindowsState $windowState * * @return string */ - public function renderFragment(\Twig_Environment $environment, WindowsState $windowState) + public function renderFragment(\Twig_Environment $environment, AbstractWindowsState $windowState) { - $result = ''; $windowState->setRenderedSuccessfully(false); - $data = $windowState->getData(); - - if (isset($data['cleanUrl'])) { - if (isset($data['type'])) { - $wid = isset($data['wid']) ? $data['wid'] : $this->getUniqueIdentifier(); - $uri = $this->getUrlWithContainer($data['cleanUrl'], $data['type'], $wid); - } else { - $uri = $data['cleanUrl']; - } - } else { - return $result; - } try { + $uri = $this->windowsStateRequestManager->getUri($windowState->getData()); + /** @var HttpKernelExtension $httpKernelExtension */ $httpKernelExtension = $environment->getExtension('http_kernel'); - $result = $httpKernelExtension->renderFragment($uri); $windowState->setRenderedSuccessfully(true); - return $result; - } catch (NotFoundHttpException $e) { - $this->em->remove($windowState); - $this->em->flush($windowState); - } - - return $result; - } - /** - * Get a user from the Security Context - * - * @return null|mixed - * @throws \LogicException If SecurityBundle is not available - * @see Symfony\Component\Security\Core\Authentication\Token\TokenInterface::getUser() - */ - public function getUser() - { - /** @var TokenInterface $token */ - if (null === $token = $this->securityContext->getToken()) { - return null; - } - - if (!is_object($user = $token->getUser())) { - return null; + return $httpKernelExtension->renderFragment($uri); + } catch (NotFoundHttpException $e) { + $this->windowsStateManager->deleteWindowsState($windowState->getId()); + } catch (\InvalidArgumentException $e) { + $this->windowsStateManager->deleteWindowsState($windowState->getId()); } - return $user; - } - - /** - * @param string $url - * @param string $container - * @param string $wid - * - * @return string - */ - protected function getUrlWithContainer($url, $container, $wid) - { - if (strpos($url, '_widgetContainer=') === false) { - $parts = parse_url($url); - $widgetPart = '_widgetContainer=' . $container. '&_wid=' . $wid; - if (array_key_exists('query', $parts)) { - $separator = $parts['query'] ? '&' : ''; - $newQuery = $parts['query'] . $separator . $widgetPart; - $url = str_replace($parts['query'], $newQuery, $url); - } else { - $url .= '?' . $widgetPart; - } - } - return $url; + return ''; } /** @@ -201,12 +124,4 @@ public function getName() { return self::EXTENSION_NAME; } - - /** - * @return string - */ - protected function getUniqueIdentifier() - { - return str_replace('.', '-', uniqid('', true)); - } } From f409656585946e0c47ce56c06f23848cb56af2da Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Mon, 14 Dec 2015 11:51:02 +0100 Subject: [PATCH 118/471] CRM-3997: Make all mailboxes available under System organization - fix tests --- .../Tests/Unit/Provider/EmailActivityListProviderTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Oro/Bundle/EmailBundle/Tests/Unit/Provider/EmailActivityListProviderTest.php b/src/Oro/Bundle/EmailBundle/Tests/Unit/Provider/EmailActivityListProviderTest.php index 00ed47953f5..07a03c39c92 100644 --- a/src/Oro/Bundle/EmailBundle/Tests/Unit/Provider/EmailActivityListProviderTest.php +++ b/src/Oro/Bundle/EmailBundle/Tests/Unit/Provider/EmailActivityListProviderTest.php @@ -27,7 +27,7 @@ class EmailActivityListProviderTest extends \PHPUnit_Framework_TestCase protected $doctrineRegistryLink; /** @var \PHPUnit_Framework_MockObject_MockObject */ - protected $mailboxProcessStorage; + protected $mailboxProcessStorageLink; protected function setUp() { @@ -65,8 +65,8 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); - $this->mailboxProcessStorage = $this->getMockBuilder( - 'Oro\Bundle\EmailBundle\Mailbox\MailboxProcessStorage' + $this->mailboxProcessStorageLink = $this->getMockBuilder( + 'Oro\Bundle\EntityConfigBundle\DependencyInjection\Utils\ServiceLink' ) ->disableOriginalConstructor() ->getMock(); @@ -80,7 +80,7 @@ protected function setUp() $emailThreadProvider, $htmlTagHelper, $this->securityFacadeLink, - $this->mailboxProcessStorage + $this->mailboxProcessStorageLink ); $this->emailActivityListProvider->setSecurityContextLink($this->securityContextLink); } From f27d931ebd4ed0e68561c2e0696fc06b0e0a45e9 Mon Sep 17 00:00:00 2001 From: Yurii Muratov Date: Mon, 14 Dec 2015 15:03:44 +0200 Subject: [PATCH 119/471] OEE-720: Fix save role with custom fields --- .../Resources/public/js/views/role-view.js | 48 +++++++++++++------ .../Resources/views/Role/update.html.twig | 9 +++- 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/src/Oro/Bundle/UserBundle/Resources/public/js/views/role-view.js b/src/Oro/Bundle/UserBundle/Resources/public/js/views/role-view.js index f28cb06a1d8..5c3cc374b85 100644 --- a/src/Oro/Bundle/UserBundle/Resources/public/js/views/role-view.js +++ b/src/Oro/Bundle/UserBundle/Resources/public/js/views/role-view.js @@ -16,20 +16,18 @@ define([ elSelector: '', formName: '', formSelector: '', - labelSelector: '', privilegesSelector: '', appendUsersSelector: '', removeUsersSelector: '', - tokenSelector: '' + fields: '' }, privileges: null, $form: null, - $label: null, $privileges: null, $appendUsers: null, $removeUsers: null, - $token: null, + $fields: null, events: { 'click': 'onSubmit' @@ -48,12 +46,19 @@ define([ this.options = _.defaults(options || {}, this.options); this.$el = $(this.options.elSelector); this.$form = $(this.options.formSelector); - this.$label = $(this.options.labelSelector); this.$privileges = $(this.options.privilegesSelector); this.$appendUsers = $(this.options.appendUsersSelector); this.$removeUsers = $(this.options.removeUsersSelector); - this.$token = $(this.options.tokenSelector); this.privileges = JSON.parse(this.$privileges.val()); + + var fields = {}; + _.each( + this.options.fields, + function(selector, name) { + fields[name] = $(selector); + } + ); + this.$fields = fields; }, /** @@ -65,12 +70,11 @@ define([ } delete this.$form; - delete this.$label; delete this.$privileges; delete this.$appendUsers; delete this.$removeUsers; - delete this.$token; delete this.privileges; + delete this.$fields; RoleView.__super__.dispose.call(this); }, @@ -79,7 +83,16 @@ define([ * onSubmit event listener */ onSubmit: function(event) { - if (this.$label.hasClass('error')) { + var hasErrors = false; + _.each( + this.$fields, + function(element) { + if (element.hasClass('error')) { + hasErrors = true; + } + } + ); + if (hasErrors) { return; } var $form = this.$form; @@ -122,11 +135,18 @@ define([ getData: function() { var data = {}; - data[this.options.formName + '[label]'] = this.$label.val(); - data[this.options.formName + '[privileges]'] = JSON.stringify(this.privileges); - data[this.options.formName + '[appendUsers]'] = this.$appendUsers.val(); - data[this.options.formName + '[removeUsers]'] = this.$removeUsers.val(); - data[this.options.formName + '[_token]'] = this.$token.val(); + var formName = this.options.formName; + _.each( + this.$fields, + function(element, name) { + debugger; + data[formName+ '[' + name +']'] = element.val(); + } + ); + + data[formName + '[privileges]'] = JSON.stringify(this.privileges); + data[formName + '[appendUsers]'] = this.$appendUsers.val(); + data[formName + '[removeUsers]'] = this.$removeUsers.val(); return data; }, diff --git a/src/Oro/Bundle/UserBundle/Resources/views/Role/update.html.twig b/src/Oro/Bundle/UserBundle/Resources/views/Role/update.html.twig index 1130106d760..db2de6e7e9b 100644 --- a/src/Oro/Bundle/UserBundle/Resources/views/Role/update.html.twig +++ b/src/Oro/Bundle/UserBundle/Resources/views/Role/update.html.twig @@ -34,15 +34,20 @@ }) %} {% endif %} {{ UI.dropdownSaveButton({'html': html}) }} + {% set fields = [] %} + {% for name, child in form %} + {% if name not in ['appendUsers', 'removeUsers', 'entity', 'privileges', 'action'] %} + {% set fields = fields|merge({(name): '#' ~ child.vars.id}) %} + {% endif %} + {% endfor %} {% set options = { elSelector: '.btn-success.role-submit', formName: form.vars.name, formSelector: '#' ~ form.vars.id, - labelSelector: '#' ~ form.label.vars.id, privilegesSelector: '#' ~ form.privileges.vars.id, appendUsersSelector: '#roleAppendUsers', removeUsersSelector: '#roleRemoveUsers', - tokenSelector: '#' ~ form._token.vars.id + fields : fields } %}
            From d5c8adeba72df96c8fa9cf204b5a932692254d3c Mon Sep 17 00:00:00 2001 From: Sergey Zhuravel Date: Mon, 14 Dec 2015 18:39:00 +0200 Subject: [PATCH 120/471] BB-1539: 500 error on frontend api/rest/latest/windows request - add tests --- .../Entity/AbstractWindowsState.php | 5 +- .../Manager/WindowsStateManager.php | 29 ++- .../Manager/WindowsStateRequestManager.php | 19 +- .../WindowsStateController.php} | 69 +++--- .../DataFixtures/LoadWindowsStateData.php | 31 +++ .../Repository/WindowsStateRepositoryTest.php | 91 +++++++ .../Unit/Manager/WindowsStateManagerTest.php | 223 +++++++++++++++++ .../WindowsStateRequestManagerTest.php | 118 +++++++++ .../Tests/Unit/Twig/WindowsExtensionTest.php | 224 ++++++------------ .../WindowsBundle/Twig/WindowsExtension.php | 36 ++- 10 files changed, 634 insertions(+), 211 deletions(-) rename src/Oro/Bundle/WindowsBundle/Tests/Functional/{API/RestApiTest.php => Api/WindowsStateController.php} (76%) create mode 100644 src/Oro/Bundle/WindowsBundle/Tests/Functional/DataFixtures/LoadWindowsStateData.php create mode 100644 src/Oro/Bundle/WindowsBundle/Tests/Functional/Entity/Repository/WindowsStateRepositoryTest.php create mode 100644 src/Oro/Bundle/WindowsBundle/Tests/Unit/Manager/WindowsStateManagerTest.php create mode 100644 src/Oro/Bundle/WindowsBundle/Tests/Unit/Manager/WindowsStateRequestManagerTest.php diff --git a/src/Oro/Bundle/WindowsBundle/Entity/AbstractWindowsState.php b/src/Oro/Bundle/WindowsBundle/Entity/AbstractWindowsState.php index 1ce3d84abec..1eff9acbf44 100644 --- a/src/Oro/Bundle/WindowsBundle/Entity/AbstractWindowsState.php +++ b/src/Oro/Bundle/WindowsBundle/Entity/AbstractWindowsState.php @@ -79,17 +79,18 @@ public function setData($data) */ public function getData() { - return json_decode($this->data, true); + return $this->data; } /** * Get JSON data * * @return string + * @deprecated use getData method instead */ public function getJsonData() { - return $this->data; + return json_encode($this->data); } /** diff --git a/src/Oro/Bundle/WindowsBundle/Manager/WindowsStateManager.php b/src/Oro/Bundle/WindowsBundle/Manager/WindowsStateManager.php index ecbe93923c6..a10e67d58ad 100644 --- a/src/Oro/Bundle/WindowsBundle/Manager/WindowsStateManager.php +++ b/src/Oro/Bundle/WindowsBundle/Manager/WindowsStateManager.php @@ -3,6 +3,7 @@ namespace Oro\Bundle\WindowsBundle\Manager; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; +use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Symfony\Component\Security\Core\User\UserInterface; use Oro\Bundle\EntityBundle\ORM\DoctrineHelper; @@ -64,7 +65,11 @@ public function createWindowsState() */ public function updateWindowsState($windowId) { - return (bool)$this->getRepository()->update($this->getUser(), $windowId, $this->requestStateManager->getData()); + return (bool)$this->getRepository()->update( + $this->getUser(), + $this->filterId($windowId), + $this->requestStateManager->getData() + ); } /** @@ -73,7 +78,7 @@ public function updateWindowsState($windowId) */ public function deleteWindowsState($windowId) { - return (bool)$this->getRepository()->delete($this->getUser(), $windowId); + return (bool)$this->getRepository()->delete($this->getUser(), $this->filterId($windowId)); } /** @@ -90,7 +95,7 @@ public function getWindowsStates() */ public function getWindowsState($windowId) { - return $this->getRepository()->findBy(['user' => $this->getUser(), 'id' => (int)$windowId]); + return $this->getRepository()->findBy(['user' => $this->getUser(), 'id' => $this->filterId($windowId)]); } /** @@ -101,6 +106,20 @@ protected function getRepository() return $this->doctrineHelper->getEntityRepository($this->className); } + /** + * @param mixed $windowId + * @return int + */ + protected function filterId($windowId) + { + $windowId = filter_var($windowId, FILTER_VALIDATE_INT); + if (false === $windowId) { + throw new \InvalidArgumentException('Wrong $windowId type'); + } + + return $windowId; + } + /** * @return UserInterface * @@ -109,11 +128,11 @@ protected function getRepository() public function getUser() { if (null === $token = $this->tokenStorage->getToken()) { - return null; + throw new AccessDeniedException(); } if (!is_object($user = $token->getUser())) { - return null; + throw new AccessDeniedException(); } return $user; diff --git a/src/Oro/Bundle/WindowsBundle/Manager/WindowsStateRequestManager.php b/src/Oro/Bundle/WindowsBundle/Manager/WindowsStateRequestManager.php index bf77f329504..2b6d8dcb265 100644 --- a/src/Oro/Bundle/WindowsBundle/Manager/WindowsStateRequestManager.php +++ b/src/Oro/Bundle/WindowsBundle/Manager/WindowsStateRequestManager.php @@ -23,19 +23,23 @@ public function __construct(RequestStack $requestStack) public function getData() { $request = $this->requestStack->getCurrentRequest(); + if (!$request) { + throw new \InvalidArgumentException('Missing $request'); + } + $data = $request->request->all(); if (!array_key_exists('data', $data)) { - throw new \InvalidArgumentException(); + throw new \InvalidArgumentException('Missing data in $request'); } if (!array_key_exists('url', $data['data'])) { - throw new \InvalidArgumentException(); + throw new \InvalidArgumentException('Missing url in $data'); } - $cleanUrl = str_replace($request->server->get('SCRIPT_NAME'), '', $data['data']['url']); + $cleanUrl = str_replace($request->getScriptName(), '', $data['data']['url']); if (!$cleanUrl) { - throw new \InvalidArgumentException(); + throw new \InvalidArgumentException('$cleanUrl is empty'); } $data['data']['cleanUrl'] = $cleanUrl; @@ -49,6 +53,7 @@ public function getData() */ public function getUri(array $data) { + $uri = null; if (isset($data['cleanUrl'])) { if (isset($data['type'])) { $wid = isset($data['wid']) ? $data['wid'] : $this->getUniqueIdentifier(); @@ -58,8 +63,8 @@ public function getUri(array $data) } } - if (!$uri) { - throw new \InvalidArgumentException(); + if (null === $uri) { + throw new \InvalidArgumentException('cleanUrl is missing'); } return $uri; @@ -94,6 +99,6 @@ protected function getUrlWithContainer($url, $container, $wid) */ protected function getUniqueIdentifier() { - return str_replace('.', '-', uniqid('windows_state', true)); + return str_replace('.', '-', uniqid('', true)); } } diff --git a/src/Oro/Bundle/WindowsBundle/Tests/Functional/API/RestApiTest.php b/src/Oro/Bundle/WindowsBundle/Tests/Functional/Api/WindowsStateController.php similarity index 76% rename from src/Oro/Bundle/WindowsBundle/Tests/Functional/API/RestApiTest.php rename to src/Oro/Bundle/WindowsBundle/Tests/Functional/Api/WindowsStateController.php index 34239b9f714..860d7e89b76 100644 --- a/src/Oro/Bundle/WindowsBundle/Tests/Functional/API/RestApiTest.php +++ b/src/Oro/Bundle/WindowsBundle/Tests/Functional/Api/WindowsStateController.php @@ -1,6 +1,6 @@ array( + self::$entity = [ + 'data' => [ 'position' => '0', - 'title' => 'Some title' - ) - ); + 'title' => 'Some title', + 'url' => '/path' + ], + ]; $this->client->request( 'POST', $this->getUrl('oro_api_post_windows'), self::$entity, - array(), + [], $this->generateWsseAuthHeader() ); @@ -48,10 +49,10 @@ public function testPost() $resultJson = json_decode($result->getContent(), true); - $this->assertArrayHasKey("id", $resultJson); - $this->assertGreaterThan(0, $resultJson["id"]); + $this->assertArrayHasKey('id', $resultJson); + $this->assertGreaterThan(0, $resultJson['id']); - self::$entity['id'] = $resultJson["id"]; + self::$entity['id'] = $resultJson['id']; } /** @@ -67,9 +68,9 @@ public function testPut() $this->client->request( 'PUT', - $this->getUrl('oro_api_put_windows', array('windowId' => self::$entity['id'])), + $this->getUrl('oro_api_put_windows', ['windowId' => self::$entity['id']]), self::$entity, - array(), + [], $this->generateWsseAuthHeader() ); @@ -95,8 +96,8 @@ public function testGet() $this->client->request( 'GET', $this->getUrl('oro_api_get_windows'), - array(), - array(), + [], + [], $this->generateWsseAuthHeader() ); @@ -122,9 +123,9 @@ public function testDelete($itemType) $this->client->request( 'DELETE', - $this->getUrl('oro_api_delete_windows', array('windowId' => self::$entity['id'])), - array(), - array(), + $this->getUrl('oro_api_delete_windows', ['windowId' => self::$entity['id']]), + [], + [], $this->generateWsseAuthHeader() ); @@ -145,9 +146,9 @@ public function testNotFound() $this->client->request( 'PUT', - $this->getUrl('oro_api_put_windows', array('windowId' => self::$entity['id'])), + $this->getUrl('oro_api_put_windows', ['windowId' => self::$entity['id']]), self::$entity, - array(), + [], $this->generateWsseAuthHeader() ); @@ -159,9 +160,9 @@ public function testNotFound() $this->client->request( 'DELETE', - $this->getUrl('oro_api_delete_windows', array('windowId' => self::$entity['id'])), - array(), - array(), + $this->getUrl('oro_api_delete_windows', ['windowId' => self::$entity['id']]), + [], + [], $this->generateWsseAuthHeader() ); @@ -180,12 +181,12 @@ public function testUnauthorized() { $this->assertNotEmpty(self::$entity); - $requests = array( - 'GET' => $this->getUrl('oro_api_get_windows'), - 'POST' => $this->getUrl('oro_api_post_windows'), - 'PUT' => $this->getUrl('oro_api_put_windows', array('windowId' => self::$entity['id'])), - 'DELETE' => $this->getUrl('oro_api_delete_windows', array('windowId' => self::$entity['id'])), - ); + $requests = [ + 'GET' => $this->getUrl('oro_api_get_windows'), + 'POST' => $this->getUrl('oro_api_post_windows'), + 'PUT' => $this->getUrl('oro_api_put_windows', ['windowId' => self::$entity['id']]), + 'DELETE' => $this->getUrl('oro_api_delete_windows', ['windowId' => self::$entity['id']]), + ]; foreach ($requests as $requestType => $url) { $this->client->request($requestType, $url); @@ -208,17 +209,17 @@ public function testEmptyBody() { $this->assertNotEmpty(self::$entity); - $requests = array( + $requests = [ 'POST' => $this->getUrl('oro_api_post_windows'), - 'PUT' => $this->getUrl('oro_api_put_windows', array('windowId' => self::$entity['id'])), - ); + 'PUT' => $this->getUrl('oro_api_put_windows', ['windowId' => self::$entity['id']]), + ]; foreach ($requests as $requestType => $url) { $this->client->request( $requestType, $url, - array(), - array(), + [], + [], $this->generateWsseAuthHeader() ); diff --git a/src/Oro/Bundle/WindowsBundle/Tests/Functional/DataFixtures/LoadWindowsStateData.php b/src/Oro/Bundle/WindowsBundle/Tests/Functional/DataFixtures/LoadWindowsStateData.php new file mode 100644 index 00000000000..83b472da28b --- /dev/null +++ b/src/Oro/Bundle/WindowsBundle/Tests/Functional/DataFixtures/LoadWindowsStateData.php @@ -0,0 +1,31 @@ +getRepository('OroUserBundle:User')->findOneBy(['username' => 'admin']); + + $state = new WindowsState(); + $state + ->setUser($user) + ->setData( + [ + 'cleanUrl' => '/path', + ] + ); + + $manager->persist($state); + $manager->flush(); + + $this->setReference('windows_state.admin', $state); + } +} diff --git a/src/Oro/Bundle/WindowsBundle/Tests/Functional/Entity/Repository/WindowsStateRepositoryTest.php b/src/Oro/Bundle/WindowsBundle/Tests/Functional/Entity/Repository/WindowsStateRepositoryTest.php new file mode 100644 index 00000000000..789555e9dce --- /dev/null +++ b/src/Oro/Bundle/WindowsBundle/Tests/Functional/Entity/Repository/WindowsStateRepositoryTest.php @@ -0,0 +1,91 @@ +initClient(); + + $this->loadFixtures( + [ + 'Oro\Bundle\WindowsBundle\Tests\Functional\DataFixtures\LoadWindowsStateData', + 'Oro\Bundle\UserBundle\Tests\Functional\DataFixtures\LoadUserData', + ] + ); + } + + public function testUpdate() + { + /** @var WindowsState $state */ + $state = $this->getReference('windows_state.admin'); + + $registry = $this->getContainer()->get('doctrine'); + + /** @var WindowsStateRepository $repo */ + $repo = $registry->getRepository('OroWindowsBundle:WindowsState'); + + $this->assertEquals(true, $repo->update($state->getUser(), $state->getId(), ['cleanUrl' => '/path?a=1'])); + + $registry->getManager()->clear(); + + /** @var WindowsState $updatedState */ + $updatedState = $repo->find($state->getId()); + + $this->assertNotEquals($state->getData(), $updatedState->getData()); + } + + public function testUpdateAnotherUser() + { + /** @var WindowsState $state */ + $state = $this->getReference('windows_state.admin'); + /** @var User $user */ + $user = $this->getReference('simple_user'); + $registry = $this->getContainer()->get('doctrine'); + + /** @var WindowsStateRepository $repo */ + $repo = $registry->getRepository('OroWindowsBundle:WindowsState'); + + $this->assertEquals(false, $repo->update($user, $state->getId(), ['cleanUrl' => '/path?a=1'])); + } + + public function testDelete() + { + /** @var WindowsState $state */ + $state = $this->getReference('windows_state.admin'); + + $registry = $this->getContainer()->get('doctrine'); + + /** @var WindowsStateRepository $repo */ + $repo = $registry->getRepository('OroWindowsBundle:WindowsState'); + + $this->assertEquals(true, $repo->delete($state->getUser(), $state->getId())); + + $registry->getManager()->clear(); + + $this->assertNull($repo->find($state->getId())); + } + + public function testDeleteAnotherUser() + { + /** @var WindowsState $state */ + $state = $this->getReference('windows_state.admin'); + /** @var User $user */ + $user = $this->getReference('simple_user'); + $registry = $this->getContainer()->get('doctrine'); + + /** @var WindowsStateRepository $repo */ + $repo = $registry->getRepository('OroWindowsBundle:WindowsState'); + + $this->assertEquals(false, $repo->delete($user, $state->getId())); + } +} diff --git a/src/Oro/Bundle/WindowsBundle/Tests/Unit/Manager/WindowsStateManagerTest.php b/src/Oro/Bundle/WindowsBundle/Tests/Unit/Manager/WindowsStateManagerTest.php new file mode 100644 index 00000000000..b89ba105a55 --- /dev/null +++ b/src/Oro/Bundle/WindowsBundle/Tests/Unit/Manager/WindowsStateManagerTest.php @@ -0,0 +1,223 @@ +tokenStorage = $this + ->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface'); + + $this->doctrineHelper = $this->getMockBuilder('Oro\Bundle\EntityBundle\ORM\DoctrineHelper') + ->disableOriginalConstructor() + ->getMock(); + + $this->requestStateManager = $this + ->getMockBuilder('Oro\Bundle\WindowsBundle\Manager\WindowsStateRequestManager') + ->disableOriginalConstructor() + ->getMock(); + + $this->manager = new WindowsStateManager( + $this->tokenStorage, + $this->doctrineHelper, + $this->requestStateManager, + 'Oro\Bundle\WindowsBundle\Entity\WindowsState' + ); + } + + public function testCreateWindowsState() + { + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $token->expects($this->once())->method('getUser')->willReturn(new User()); + $this->tokenStorage->expects($this->once())->method('getToken')->willReturn($token); + + $em = $this->getMock('Doctrine\ORM\EntityManagerInterface'); + + $state = new WindowsState(); + $this->doctrineHelper->expects($this->once())->method('createEntityInstance')->willReturn($state); + $this->doctrineHelper->expects($this->once())->method('getEntityManagerForClass')->willReturn($em); + + $em->expects($this->once())->method('persist')->with($state); + $em->expects($this->once())->method('flush')->with($state); + + $this->requestStateManager->expects($this->once())->method('getData')->willReturn([]); + $this->manager->createWindowsState(); + } + + public function testUpdateWindowState() + { + $windowId = 42; + $user = new User(); + $data = ['url' => '/test']; + + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $token->expects($this->once())->method('getUser')->willReturn($user); + $this->tokenStorage->expects($this->once())->method('getToken')->willReturn($token); + + $repo = $this->getMockBuilder('Oro\Bundle\WindowsBundle\Entity\Repository\WindowsStateRepository') + ->disableOriginalConstructor() + ->getMock(); + + $this->doctrineHelper->expects($this->once())->method('getEntityRepository')->willReturn($repo); + $this->requestStateManager->expects($this->once())->method('getData')->willReturn($data); + + $repo->expects($this->once())->method('update')->with($user, $windowId, $data); + + $this->manager->updateWindowsState($windowId); + } + + public function testDeleteWindowsState() + { + $user = new User(); + $windowId = 42; + + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $token->expects($this->once())->method('getUser')->willReturn($user); + $this->tokenStorage->expects($this->once())->method('getToken')->willReturn($token); + + $repo = $this->getMockBuilder('Oro\Bundle\WindowsBundle\Entity\Repository\WindowsStateRepository') + ->disableOriginalConstructor() + ->getMock(); + + $this->doctrineHelper->expects($this->once())->method('getEntityRepository')->willReturn($repo); + + $repo->expects($this->once())->method('delete')->with($user, $windowId); + + $this->requestStateManager->expects($this->never())->method($this->anything()); + $this->manager->deleteWindowsState($windowId); + } + + public function testGetWindowsStates() + { + $user = new User(); + + $windowStateFoo = $this->createWindowState(['cleanUrl' => 'foo']); + $windowStateBar = $this->createWindowState(['cleanUrl' => 'foo']); + + $windowStates = [$windowStateFoo, $windowStateBar]; + + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $token->expects($this->once())->method('getUser')->willReturn($user); + $this->tokenStorage->expects($this->once())->method('getToken')->willReturn($token); + + $repo = $this->getMockBuilder('Oro\Bundle\WindowsBundle\Entity\Repository\WindowsStateRepository') + ->disableOriginalConstructor() + ->getMock(); + + $this->doctrineHelper->expects($this->once())->method('getEntityRepository')->willReturn($repo); + + $repo->expects($this->once())->method('findBy')->with(['user' => $user])->willReturn($windowStates); + + $this->requestStateManager->expects($this->never())->method($this->anything()); + $this->assertSame($windowStates, $this->manager->getWindowsStates()); + } + + public function testGetWindowsState() + { + $user = new User(); + $windowStateId = 42; + + $windowState = $this->createWindowState(['cleanUrl' => 'foo']); + + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $token->expects($this->once())->method('getUser')->willReturn($user); + $this->tokenStorage->expects($this->once())->method('getToken')->willReturn($token); + + $repo = $this->getMockBuilder('Oro\Bundle\WindowsBundle\Entity\Repository\WindowsStateRepository') + ->disableOriginalConstructor() + ->getMock(); + + $this->doctrineHelper->expects($this->once())->method('getEntityRepository')->willReturn($repo); + + $repo->expects($this->once())->method('findBy')->with(['user' => $user, 'id' => $windowStateId]) + ->willReturn($windowState); + + $this->requestStateManager->expects($this->never())->method($this->anything()); + $this->assertSame($windowState, $this->manager->getWindowsState($windowStateId)); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Wrong $windowId type + */ + public function testFilterId() + { + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $token->expects($this->once())->method('getUser')->willReturn(new User()); + $this->tokenStorage->expects($this->once())->method('getToken')->willReturn($token); + + $repo = $this->getMockBuilder('Oro\Bundle\WindowsBundle\Entity\Repository\WindowsStateRepository') + ->disableOriginalConstructor() + ->getMock(); + + $this->doctrineHelper->expects($this->once())->method('getEntityRepository')->willReturn($repo); + + $this->manager->getWindowsState('bbb'); + } + + /** + * @expectedException \Symfony\Component\Security\Core\Exception\AccessDeniedException + */ + public function testUserEmptyToken() + { + $this->tokenStorage->expects($this->once())->method('getToken')->willReturn(null); + + $repo = $this->getMockBuilder('Oro\Bundle\WindowsBundle\Entity\Repository\WindowsStateRepository') + ->disableOriginalConstructor() + ->getMock(); + + $this->doctrineHelper->expects($this->once())->method('getEntityRepository')->willReturn($repo); + + $this->manager->getWindowsState(42); + } + + /** + * @expectedException \Symfony\Component\Security\Core\Exception\AccessDeniedException + */ + public function testUserEmptyUser() + { + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $token->expects($this->once())->method('getUser')->willReturn(null); + $this->tokenStorage->expects($this->once())->method('getToken')->willReturn($token); + + $repo = $this->getMockBuilder('Oro\Bundle\WindowsBundle\Entity\Repository\WindowsStateRepository') + ->disableOriginalConstructor() + ->getMock(); + + $this->doctrineHelper->expects($this->once())->method('getEntityRepository')->willReturn($repo); + + $this->manager->getWindowsState(42); + } + + /** + * @param array $data + * @return WindowsState + */ + protected function createWindowState(array $data = []) + { + $state = new WindowsState(); + $state->setData($data); + + return $state; + } +} diff --git a/src/Oro/Bundle/WindowsBundle/Tests/Unit/Manager/WindowsStateRequestManagerTest.php b/src/Oro/Bundle/WindowsBundle/Tests/Unit/Manager/WindowsStateRequestManagerTest.php new file mode 100644 index 00000000000..3088edb3882 --- /dev/null +++ b/src/Oro/Bundle/WindowsBundle/Tests/Unit/Manager/WindowsStateRequestManagerTest.php @@ -0,0 +1,118 @@ +requestStack = $this->getMockBuilder('Symfony\Component\HttpFoundation\RequestStack') + ->disableOriginalConstructor() + ->getMock(); + + $this->manager = new WindowsStateRequestManager($this->requestStack); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Missing $request + */ + public function testGetDataMissingRequest() + { + $this->requestStack->expects($this->once())->method('getCurrentRequest')->willReturn(null); + $this->manager->getData(); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Missing data in $request + */ + public function testGetDataMissingData() + { + $request = new Request(); + $this->requestStack->expects($this->once())->method('getCurrentRequest')->willReturn($request); + $this->manager->getData(); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Missing url in $data + */ + public function testGetDataMissingUrl() + { + $request = new Request([], ['data' => []]); + $this->requestStack->expects($this->once())->method('getCurrentRequest')->willReturn($request); + $this->manager->getData(); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage $cleanUrl is empty + */ + public function testCleanUrlIsEmpty() + { + $request = new Request([], ['data' => ['url' => 'localhost']], [], [], [], ['SCRIPT_NAME' => 'localhost']); + $this->requestStack->expects($this->once())->method('getCurrentRequest')->willReturn($request); + $this->manager->getData(); + } + + public function testGetData() + { + $request = new Request([], ['data' => ['url' => 'localhost/path']], [], [], [], ['SCRIPT_NAME' => 'localhost']); + $this->requestStack->expects($this->once())->method('getCurrentRequest')->willReturn($request); + $this->assertEquals(['url' => 'localhost/path', 'cleanUrl' => '/path'], $this->manager->getData()); + } + + /** + * @dataProvider uriDataProvider + * @param string $expected + * @param array $data + */ + public function testGetUri($expected, array $data) + { + $this->assertStringStartsWith($expected, $this->manager->getUri($data)); + } + + /** + * @return array + */ + public function uriDataProvider() + { + return [ + 'simple path' => ['/path', ['cleanUrl' => '/path']], + 'autogenerated wid' => ['/path?_widgetContainer=form&_wid=', ['cleanUrl' => '/path', 'type' => 'form']], + 'predefined wid' => [ + '/path?_widgetContainer=form&_wid=123456', + ['cleanUrl' => '/path', 'type' => 'form', 'wid' => '123456'], + ], + 'url contains container' => [ + '/path?_widgetContainer=container', + ['cleanUrl' => '/path?_widgetContainer=container', 'type' => 'form', 'wid' => '123456'], + ], + 'query' => [ + '/path?param=value&_widgetContainer=form&_wid=123456', + ['cleanUrl' => '/path?param=value', 'type' => 'form', 'wid' => '123456'], + ], + ]; + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage cleanUrl is missing + */ + public function testDataEmpty() + { + $this->manager->getUri([]); + } +} diff --git a/src/Oro/Bundle/WindowsBundle/Tests/Unit/Twig/WindowsExtensionTest.php b/src/Oro/Bundle/WindowsBundle/Tests/Unit/Twig/WindowsExtensionTest.php index 176ae1f1a24..54fc69817ca 100644 --- a/src/Oro/Bundle/WindowsBundle/Tests/Unit/Twig/WindowsExtensionTest.php +++ b/src/Oro/Bundle/WindowsBundle/Tests/Unit/Twig/WindowsExtensionTest.php @@ -2,8 +2,12 @@ namespace Oro\Bundle\WindowsBundle\Tests\Twig; +use Symfony\Bridge\Twig\Extension\HttpKernelExtension; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\Security\Core\Exception\AccessDeniedException; +use Oro\Bundle\WindowsBundle\Manager\WindowsStateManager; +use Oro\Bundle\WindowsBundle\Manager\WindowsStateRequestManager; use Oro\Bundle\WindowsBundle\Entity\WindowsState; use Oro\Bundle\WindowsBundle\Twig\WindowsExtension; @@ -15,31 +19,34 @@ class WindowsExtensionTest extends \PHPUnit_Framework_TestCase protected $extension; /** - * @var \PHPUnit_Framework_MockObject_MockObject $environment + * @var \PHPUnit_Framework_MockObject_MockObject|\Twig_Environment */ protected $environment; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit_Framework_MockObject_MockObject|WindowsStateManager */ - protected $securityContext; + protected $stateManager; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit_Framework_MockObject_MockObject|WindowsStateRequestManager */ - protected $entityManager; + protected $requestStateManager; protected function setUp() { $this->environment = $this->getMockBuilder('Twig_Environment') ->disableOriginalConstructor() ->getMock(); - $this->securityContext = $this->getMockBuilder('Symfony\Component\Security\Core\SecurityContextInterface') + $this->stateManager = $this->getMockBuilder('Oro\Bundle\WindowsBundle\Manager\WindowsStateManager') + ->disableOriginalConstructor() ->getMock(); - $this->entityManager = $this->getMockBuilder('Doctrine\ORM\EntityManager') + $this->requestStateManager = $this + ->getMockBuilder('Oro\Bundle\WindowsBundle\Manager\WindowsStateRequestManager') + ->setMethods(['getData']) ->disableOriginalConstructor() ->getMock(); - $this->extension = new WindowsExtension($this->securityContext, $this->entityManager); + $this->extension = new WindowsExtension($this->stateManager, $this->requestStateManager); } public function testGetFunctions() @@ -57,136 +64,35 @@ public function testGetName() public function testRenderNoUser() { + $this->stateManager->expects($this->once())->method('getWindowsStates') + ->willThrowException(new AccessDeniedException()); + $this->assertEmpty($this->extension->render($this->environment)); } public function testRender() { - $token = $this->getMockBuilder('stdClass') - ->setMethods(array('getUser')) - ->getMock(); - - $this->securityContext->expects($this->once()) - ->method('getToken') - ->will($this->returnValue($token)); - - $user = $this->getMock('stdClass'); - - $token->expects($this->once()) - ->method('getUser') - ->will($this->returnValue($user)); - - $windowStateFoo = $this->createWindowState(array('cleanUrl' => 'foo')); - $windowStateBar = $this->createWindowState(array('cleanUrl' => 'foo')); - - $userWindowStates = array($windowStateFoo, $windowStateBar); - - $repository = $this->getMock('Doctrine\Common\Persistence\ObjectRepository'); - $repository->expects($this->once()) - ->method('findBy') - ->with(array('user' => $user)) - ->will($this->returnValue($userWindowStates)); - - $this->entityManager->expects($this->once()) - ->method('getRepository') - ->with('OroWindowsBundle:WindowsState') - ->will($this->returnValue($repository)); - - $this->entityManager->expects($this->never())->method('remove'); - $this->entityManager->expects($this->never())->method('flush'); - - $expectedOutput = 'RENDERED'; - $this->environment->expects($this->once()) - ->method('render') - ->with( - 'OroWindowsBundle::states.html.twig', - array('windowStates' => array($windowStateFoo, $windowStateBar)) - ) - ->will($this->returnValue($expectedOutput)); - - $this->assertEquals($expectedOutput, $this->extension->render($this->environment)); - } - - public function testRenderWithRemoveInvalidStates() - { - $token = $this->getMockBuilder('stdClass') - ->setMethods(array('getUser')) - ->getMock(); - - $this->securityContext->expects($this->once()) - ->method('getToken') - ->will($this->returnValue($token)); - - $user = $this->getMock('stdClass'); - - $token->expects($this->once()) - ->method('getUser') - ->will($this->returnValue($user)); - - $normalWindowState = $this->createWindowState(array('cleanUrl' => 'foo')); - $badWindowState = $this->createWindowState(array('url' => 'foo')); - $emptyWindowState = $this->createWindowState(); - - $userWindowStates = array($normalWindowState, $badWindowState, $emptyWindowState); - - $repository = $this->getMock('Doctrine\Common\Persistence\ObjectRepository'); - $repository->expects($this->once()) - ->method('findBy') - ->with(array('user' => $user)) - ->will($this->returnValue($userWindowStates)); + $windowStateFoo = $this->createWindowState(['cleanUrl' => 'foo']); + $windowStateBar = $this->createWindowState(['cleanUrl' => 'foo']); - $this->entityManager->expects($this->at(0)) - ->method('getRepository') - ->with('OroWindowsBundle:WindowsState') - ->will($this->returnValue($repository)); + $windowStates = [$windowStateFoo, $windowStateBar]; - $this->entityManager->expects($this->at(1)) - ->method('remove') - ->with($badWindowState); - - $this->entityManager->expects($this->at(2)) - ->method('remove') - ->with($emptyWindowState); - - $this->entityManager->expects($this->at(3)) - ->method('flush') - ->with(array($badWindowState, $emptyWindowState)); + $this->stateManager->expects($this->once()) + ->method('getWindowsStates') + ->will($this->returnValue($windowStates)); $expectedOutput = 'RENDERED'; $this->environment->expects($this->once()) ->method('render') ->with( 'OroWindowsBundle::states.html.twig', - array('windowStates' => array($normalWindowState)) + ['windowStates' => [$windowStateFoo, $windowStateBar]] ) ->will($this->returnValue($expectedOutput)); $this->assertEquals($expectedOutput, $this->extension->render($this->environment)); - } - - public function testRenderWithoutUser() - { - $token = $this->getMockBuilder('stdClass') - ->setMethods(array('getUser')) - ->getMock(); - - $this->securityContext->expects($this->once()) - ->method('getToken') - ->will($this->returnValue($token)); - - $this->entityManager->expects($this->never())->method($this->anything()); - - $this->assertEquals('', $this->extension->render($this->environment)); - } - - public function testRenderWithoutToken() - { - $this->securityContext->expects($this->once()) - ->method('getToken') - ->will($this->returnValue(null)); - - $this->entityManager->expects($this->never())->method($this->anything()); + // no need to render twice $this->assertEquals('', $this->extension->render($this->environment)); } @@ -198,7 +104,7 @@ public function testRenderWithoutToken() */ public function testRenderFragment($cleanUrl, $type, $expectedUrl) { - $windowState = $this->createWindowState(array('cleanUrl' => $cleanUrl, 'type' => $type)); + $windowState = $this->createWindowState(['cleanUrl' => $cleanUrl, 'type' => $type]); $httpKernelExtension = $this->getHttpKernelExtensionMock(); @@ -216,14 +122,12 @@ function ($url) use ($expectedUrl) { $count = 0; $cleanUrl = preg_replace('/&_wid=([a-z0-9]*)-([a-z0-9]*)/', '', $url, -1, $count); - return ($count === 1 && $cleanUrl == $expectedUrl); + return ($count === 1 && $cleanUrl === $expectedUrl); } ) ) ->will($this->returnValue($expectedOutput)); - $this->entityManager->expects($this->never())->method($this->anything()); - $this->assertEquals($expectedOutput, $this->extension->renderFragment($this->environment, $windowState)); $this->assertTrue($windowState->isRenderedSuccessfully()); } @@ -233,29 +137,29 @@ function ($url) use ($expectedUrl) { */ public function renderFragmentDataProvider() { - return array( - 'url_without_parameters' => array( - 'widgetUrl' => '/user/create', - 'widgetType' => 'test', - 'expectedWidgetUrl' => '/user/create?_widgetContainer=test' - ), - 'url_with_parameters' => array( - 'widgetUrl' => '/user/create?id=1', - 'widgetType' => 'test', - 'expectedWidgetUrl' => '/user/create?id=1&_widgetContainer=test' - ), - 'url_with_parameters_and_fragment' => array( - 'widgetUrl' => '/user/create?id=1#group=date', - 'widgetType' => 'test', - 'expectedWidgetUrl' => '/user/create?id=1&_widgetContainer=test#group=date' - ), - ); + return [ + 'url_without_parameters' => [ + 'widgetUrl' => '/user/create', + 'widgetType' => 'test', + 'expectedWidgetUrl' => '/user/create?_widgetContainer=test', + ], + 'url_with_parameters' => [ + 'widgetUrl' => '/user/create?id=1', + 'widgetType' => 'test', + 'expectedWidgetUrl' => '/user/create?id=1&_widgetContainer=test', + ], + 'url_with_parameters_and_fragment' => [ + 'widgetUrl' => '/user/create?id=1#group=date', + 'widgetType' => 'test', + 'expectedWidgetUrl' => '/user/create?id=1&_widgetContainer=test#group=date', + ], + ]; } public function testRenderFragmentWithNotFoundHttpException() { $cleanUrl = '/foo/bar'; - $windowState = $this->createWindowState(array('cleanUrl' => $cleanUrl)); + $windowState = $this->createWindowState(['cleanUrl' => $cleanUrl]); $httpKernelExtension = $this->getHttpKernelExtensionMock(); @@ -269,13 +173,9 @@ public function testRenderFragmentWithNotFoundHttpException() ->with($cleanUrl) ->will($this->throwException(new NotFoundHttpException())); - $this->entityManager->expects($this->at(0)) - ->method('remove') - ->with($windowState); - - $this->entityManager->expects($this->at(1)) - ->method('flush') - ->with($windowState); + $this->stateManager->expects($this->once()) + ->method('deleteWindowsState') + ->with($windowState->getId()); $this->assertEquals('', $this->extension->renderFragment($this->environment, $windowState)); $this->assertFalse($windowState->isRenderedSuccessfully()); @@ -288,7 +188,7 @@ public function testRenderFragmentWithNotFoundHttpException() public function testRenderFragmentWithGenericException() { $cleanUrl = '/foo/bar'; - $windowState = $this->createWindowState(array('cleanUrl' => $cleanUrl)); + $windowState = $this->createWindowState(['cleanUrl' => $cleanUrl]); $httpKernelExtension = $this->getHttpKernelExtensionMock(); @@ -310,19 +210,39 @@ public function testRenderFragmentWithEmptyCleanUrl() $windowState = $this->createWindowState(); $this->environment->expects($this->never())->method($this->anything()); - $this->entityManager->expects($this->never())->method($this->anything()); $this->assertEquals('', $this->extension->renderFragment($this->environment, $windowState)); $this->assertFalse($windowState->isRenderedSuccessfully()); } - protected function createWindowState($data = null) + public function testRenderFragmentWithoutUser() + { + $windowState = $this->createWindowState(); + + $this->environment->expects($this->never())->method($this->anything()); + + $this->stateManager->expects($this->once())->method('deleteWindowsState') + ->willThrowException(new AccessDeniedException()); + + $this->assertEquals('', $this->extension->renderFragment($this->environment, $windowState)); + $this->assertFalse($windowState->isRenderedSuccessfully()); + } + + /** + * @param array $data + * @return WindowsState + */ + protected function createWindowState(array $data = []) { $state = new WindowsState(); $state->setData($data); + return $state; } + /** + * @return \PHPUnit_Framework_MockObject_MockObject|HttpKernelExtension + */ protected function getHttpKernelExtensionMock() { return $this->getMockBuilder('Symfony\Bridge\Twig\Extension\HttpKernelExtension') diff --git a/src/Oro/Bundle/WindowsBundle/Twig/WindowsExtension.php b/src/Oro/Bundle/WindowsBundle/Twig/WindowsExtension.php index b4a66818b62..f3abe0babd1 100644 --- a/src/Oro/Bundle/WindowsBundle/Twig/WindowsExtension.php +++ b/src/Oro/Bundle/WindowsBundle/Twig/WindowsExtension.php @@ -4,6 +4,7 @@ use Symfony\Bridge\Twig\Extension\HttpKernelExtension; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Oro\Bundle\WindowsBundle\Entity\AbstractWindowsState; use Oro\Bundle\WindowsBundle\Manager\WindowsStateManager; @@ -39,9 +40,7 @@ public function __construct( } /** - * Returns a list of functions to add to the existing list. - * - * @return array An array of functions + * {@inheritdoc} */ public function getFunctions() { @@ -80,9 +79,15 @@ public function render(\Twig_Environment $environment) $this->rendered = true; + try { + $windowsStates = $this->windowsStateManager->getWindowsStates(); + } catch (AccessDeniedException $e) { + $windowsStates = []; + } + return $environment->render( 'OroWindowsBundle::states.html.twig', - ['windowStates' => $this->windowsStateManager->getWindowsStates()] + ['windowStates' => $windowsStates] ); } @@ -96,6 +101,8 @@ public function render(\Twig_Environment $environment) */ public function renderFragment(\Twig_Environment $environment, AbstractWindowsState $windowState) { + $result = ''; + $scheduleDelete = false; $windowState->setRenderedSuccessfully(false); try { @@ -103,22 +110,29 @@ public function renderFragment(\Twig_Environment $environment, AbstractWindowsSt /** @var HttpKernelExtension $httpKernelExtension */ $httpKernelExtension = $environment->getExtension('http_kernel'); + $result = $httpKernelExtension->renderFragment($uri); $windowState->setRenderedSuccessfully(true); - return $httpKernelExtension->renderFragment($uri); + return $result; } catch (NotFoundHttpException $e) { - $this->windowsStateManager->deleteWindowsState($windowState->getId()); + $scheduleDelete = true; } catch (\InvalidArgumentException $e) { - $this->windowsStateManager->deleteWindowsState($windowState->getId()); + $scheduleDelete = true; } - return ''; + if ($scheduleDelete) { + try { + $this->windowsStateManager->deleteWindowsState($windowState->getId()); + } catch (AccessDeniedException $e) { + return $result; + } + } + + return $result; } /** - * Returns the name of the extension. - * - * @return string The extension name + * {@inheritdoc} */ public function getName() { From c81897d279273267a551b152de0c3a9e12c2dae0 Mon Sep 17 00:00:00 2001 From: Sergey Zhuravel Date: Mon, 14 Dec 2015 19:37:18 +0200 Subject: [PATCH 121/471] BB-1539: 500 error on frontend api/rest/latest/windows request - fix migrations --- .../Schema/OroWindowsBundleInstaller.php | 65 ------------------- .../Schema/v1_1/OroWindowsBundle.php | 26 +++++++- 2 files changed, 24 insertions(+), 67 deletions(-) delete mode 100644 src/Oro/Bundle/WindowsBundle/Migrations/Schema/OroWindowsBundleInstaller.php diff --git a/src/Oro/Bundle/WindowsBundle/Migrations/Schema/OroWindowsBundleInstaller.php b/src/Oro/Bundle/WindowsBundle/Migrations/Schema/OroWindowsBundleInstaller.php deleted file mode 100644 index 7e7548e4a46..00000000000 --- a/src/Oro/Bundle/WindowsBundle/Migrations/Schema/OroWindowsBundleInstaller.php +++ /dev/null @@ -1,65 +0,0 @@ -createOroWindowsStateTable($schema); - - /** Foreign keys generation **/ - $this->addOroWindowsStateForeignKeys($schema); - } - - /** - * Create oro_windows_state table - * - * @param Schema $schema - */ - protected function createOroWindowsStateTable(Schema $schema) - { - $table = $schema->createTable('oro_windows_state'); - $table->addColumn('id', 'integer', ['autoincrement' => true]); - $table->addColumn('user_id', 'integer', []); - $table->addColumn('data', Type::JSON_ARRAY, []); - $table->addColumn('created_at', 'datetime', []); - $table->addColumn('updated_at', 'datetime', []); - $table->setPrimaryKey(['id']); - $table->addIndex(['user_id'], 'IDX_8B134CF6A76ED395', []); - } - - /** - * Add oro_windows_state foreign keys. - * - * @param Schema $schema - */ - protected function addOroWindowsStateForeignKeys(Schema $schema) - { - $table = $schema->getTable('oro_windows_state'); - $table->addForeignKeyConstraint( - $schema->getTable('oro_user'), - ['user_id'], - ['id'], - ['onDelete' => 'CASCADE', 'onUpdate' => null] - ); - } -} diff --git a/src/Oro/Bundle/WindowsBundle/Migrations/Schema/v1_1/OroWindowsBundle.php b/src/Oro/Bundle/WindowsBundle/Migrations/Schema/v1_1/OroWindowsBundle.php index 914b4fb51af..4d9554caaf3 100644 --- a/src/Oro/Bundle/WindowsBundle/Migrations/Schema/v1_1/OroWindowsBundle.php +++ b/src/Oro/Bundle/WindowsBundle/Migrations/Schema/v1_1/OroWindowsBundle.php @@ -2,20 +2,42 @@ namespace Oro\Bundle\WindowsBundle\Migrations\Schema\v1_1; +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Platforms\PostgreSqlPlatform; use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Types\Type; +use Oro\Bundle\MigrationBundle\Migration\Extension\DatabasePlatformAwareInterface; use Oro\Bundle\MigrationBundle\Migration\Migration; use Oro\Bundle\MigrationBundle\Migration\QueryBag; -class OroWindowsBundle implements Migration +class OroWindowsBundle implements Migration, DatabasePlatformAwareInterface { + /** @var AbstractPlatform */ + protected $platform; + + /** {@inheritdoc} */ + public function setDatabasePlatform(AbstractPlatform $platform) + { + $this->platform = $platform; + } + /** * @inheritdoc */ public function up(Schema $schema, QueryBag $queries) { $table = $schema->getTable('oro_windows_state'); - $table->getColumn('data')->setType(Type::getType(Type::JSON_ARRAY)); + $column = $table->getColumn('data'); + + if ($this->platform instanceof PostgreSqlPlatform) { + $queries->addPreQuery( + 'ALTER TABLE oro_windows_state ALTER COLUMN data TYPE JSON USING data::JSON' + ); + } else { + $column->setType(Type::getType(Type::JSON_ARRAY)); + } + + $column->setOptions(['comment' => '(DC2Type:json_array)']); } } From 6de2f2cd7d08cbcb508ae030a54ce02e07c919f1 Mon Sep 17 00:00:00 2001 From: AlexandrDmitriev Date: Tue, 15 Dec 2015 16:07:17 +0200 Subject: [PATCH 122/471] CBORO-225: Dotmailer watchdog import status break export - fix tag extension exception during marketing list segment build: add disable param for extension --- .../Bundle/TagBundle/Grid/TagsExtension.php | 37 +++++++++++++++---- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/src/Oro/Bundle/TagBundle/Grid/TagsExtension.php b/src/Oro/Bundle/TagBundle/Grid/TagsExtension.php index 3a9b488a0b7..be1eae3b1a4 100644 --- a/src/Oro/Bundle/TagBundle/Grid/TagsExtension.php +++ b/src/Oro/Bundle/TagBundle/Grid/TagsExtension.php @@ -12,6 +12,9 @@ class TagsExtension extends AbstractTagsExtension { + const ROOT_PARAM = '_tags'; + const DISABLED_PARAM = '_disabled'; + /** @var TaggableHelper */ protected $taggableHelper; @@ -47,15 +50,33 @@ public function __construct( */ public function isApplicable(DatagridConfiguration $config) { - $className = $this->getEntityClassName($config); + $tagParameters = $this->getParameters()->get(self::ROOT_PARAM); + $isDisabled = $tagParameters && !empty($tagParameters[self::DISABLED_PARAM]); + + return !$isDisabled + && !$this->isReportOrSegmentGrid($config) + && $this->isGridRootEntityTaggable($config) + && null !== $config->offsetGetByPath(self::PROPERTY_ID_PATH) + && $this->isAccessGranted(); + } - return - !$this->isReportOrSegmentGrid($config) && - $className && - $this->taggableHelper->isTaggable($className) && - null !== $config->offsetGetByPath(self::PROPERTY_ID_PATH) && - null !== $this->securityFacade->getToken() && - $this->securityFacade->isGranted('oro_tag_view'); + /** + * @param DatagridConfiguration $configuration + * + * @return bool + */ + protected function isGridRootEntityTaggable(DatagridConfiguration $configuration) + { + $className = $this->getEntityClassName($configuration); + return $className && $this->taggableHelper->isTaggable($className); + } + + /** + * @return bool + */ + protected function isAccessGranted() + { + return null !== $this->securityFacade->getToken() && $this->securityFacade->isGranted('oro_tag_view'); } /** From e95fcece8c8b3383c6306649bcd51e9c21060605 Mon Sep 17 00:00:00 2001 From: Denis Voronin Date: Tue, 15 Dec 2015 17:01:12 +0200 Subject: [PATCH 123/471] CBORO-225: Dotmailer watchdog import status break export - fix tag extension exception during marketing list segment build: add disable param for extension --- .../Bundle/TagBundle/Grid/TagsExtension.php | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/Oro/Bundle/TagBundle/Grid/TagsExtension.php b/src/Oro/Bundle/TagBundle/Grid/TagsExtension.php index be1eae3b1a4..741cd60c784 100644 --- a/src/Oro/Bundle/TagBundle/Grid/TagsExtension.php +++ b/src/Oro/Bundle/TagBundle/Grid/TagsExtension.php @@ -12,8 +12,8 @@ class TagsExtension extends AbstractTagsExtension { - const ROOT_PARAM = '_tags'; - const DISABLED_PARAM = '_disabled'; + const TAGS_ROOT_PARAM = '_tags'; + const DISABLED_PARAM = '_disabled'; /** @var TaggableHelper */ protected $taggableHelper; @@ -50,14 +50,24 @@ public function __construct( */ public function isApplicable(DatagridConfiguration $config) { - $tagParameters = $this->getParameters()->get(self::ROOT_PARAM); - $isDisabled = $tagParameters && !empty($tagParameters[self::DISABLED_PARAM]); - - return !$isDisabled - && !$this->isReportOrSegmentGrid($config) - && $this->isGridRootEntityTaggable($config) - && null !== $config->offsetGetByPath(self::PROPERTY_ID_PATH) - && $this->isAccessGranted(); + return + !$this->isDisabled() && + !$this->isReportOrSegmentGrid($config) && + $this->isGridRootEntityTaggable($config) && + null !== $config->offsetGetByPath(self::PROPERTY_ID_PATH) && + $this->isAccessGranted(); + } + + /** + * @return bool + */ + protected function isDisabled() + { + $tagParameters = $this->getParameters()->get(self::TAGS_ROOT_PARAM); + + return + $tagParameters && + !empty($tagParameters[self::DISABLED_PARAM]); } /** @@ -68,6 +78,7 @@ public function isApplicable(DatagridConfiguration $config) protected function isGridRootEntityTaggable(DatagridConfiguration $configuration) { $className = $this->getEntityClassName($configuration); + return $className && $this->taggableHelper->isTaggable($className); } @@ -76,7 +87,9 @@ protected function isGridRootEntityTaggable(DatagridConfiguration $configuration */ protected function isAccessGranted() { - return null !== $this->securityFacade->getToken() && $this->securityFacade->isGranted('oro_tag_view'); + return + null !== $this->securityFacade->getToken() && + $this->securityFacade->isGranted('oro_tag_view'); } /** From 571837ab9071d9890ae0f2b007ebf27ba6478126 Mon Sep 17 00:00:00 2001 From: Miloslav Nenadal Date: Tue, 15 Dec 2015 16:52:41 +0100 Subject: [PATCH 124/471] BAP-9424: LDAP sync of two different users with the same fields error during import job --- .../ImportExportBundle/Event/StrategyEvent.php | 18 +++++++++++++++++- .../Strategy/Import/AbstractImportStrategy.php | 4 ++-- .../Tests/Unit/Event/StrategyEventTest.php | 3 ++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/Oro/Bundle/ImportExportBundle/Event/StrategyEvent.php b/src/Oro/Bundle/ImportExportBundle/Event/StrategyEvent.php index 4c0652c8d18..c7a141db527 100644 --- a/src/Oro/Bundle/ImportExportBundle/Event/StrategyEvent.php +++ b/src/Oro/Bundle/ImportExportBundle/Event/StrategyEvent.php @@ -4,6 +4,7 @@ use Symfony\Component\EventDispatcher\Event; +use Oro\Bundle\ImportExportBundle\Context\ContextInterface; use Oro\Bundle\ImportExportBundle\Strategy\StrategyInterface; class StrategyEvent extends Event @@ -21,14 +22,21 @@ class StrategyEvent extends Event */ protected $entity; + /** + * @var ContextInterface + */ + protected $context; + /** * @param StrategyInterface $strategy * @param $entity + * @param ContextInterface $context */ - public function __construct(StrategyInterface $strategy, $entity) + public function __construct(StrategyInterface $strategy, $entity, ContextInterface $context) { $this->entity = $entity; $this->strategy = $strategy; + $this->context = $context; } /** @@ -54,4 +62,12 @@ public function setEntity($entity) { $this->entity = $entity; } + + /** + * @return ContextInterface + */ + public function getContext() + { + return $this->context; + } } diff --git a/src/Oro/Bundle/ImportExportBundle/Strategy/Import/AbstractImportStrategy.php b/src/Oro/Bundle/ImportExportBundle/Strategy/Import/AbstractImportStrategy.php index c30b8e0bcce..d75d2a4c2a2 100644 --- a/src/Oro/Bundle/ImportExportBundle/Strategy/Import/AbstractImportStrategy.php +++ b/src/Oro/Bundle/ImportExportBundle/Strategy/Import/AbstractImportStrategy.php @@ -88,7 +88,7 @@ public function setImportExportContext(ContextInterface $context) */ protected function beforeProcessEntity($entity) { - $event = new StrategyEvent($this, $entity); + $event = new StrategyEvent($this, $entity, $this->context); $this->eventDispatcher->dispatch(StrategyEvent::PROCESS_BEFORE, $event); return $event->getEntity(); } @@ -99,7 +99,7 @@ protected function beforeProcessEntity($entity) */ protected function afterProcessEntity($entity) { - $event = new StrategyEvent($this, $entity); + $event = new StrategyEvent($this, $entity, $this->context); $this->eventDispatcher->dispatch(StrategyEvent::PROCESS_AFTER, $event); return $event->getEntity(); } diff --git a/src/Oro/Bundle/ImportExportBundle/Tests/Unit/Event/StrategyEventTest.php b/src/Oro/Bundle/ImportExportBundle/Tests/Unit/Event/StrategyEventTest.php index 2f5d6e50a7b..41151c98493 100644 --- a/src/Oro/Bundle/ImportExportBundle/Tests/Unit/Event/StrategyEventTest.php +++ b/src/Oro/Bundle/ImportExportBundle/Tests/Unit/Event/StrategyEventTest.php @@ -25,11 +25,12 @@ class StrategyEventTest extends \PHPUnit_Framework_TestCase protected function setUp() { $this->strategy = $this->getMock('Oro\Bundle\ImportExportBundle\Strategy\StrategyInterface'); + $context = $this->getMock('Oro\Bundle\ImportExportBundle\Context\ContextInterface'); $this->entity = new \stdClass(); $this->entity->id = 1; - $this->event = new StrategyEvent($this->strategy, $this->entity); + $this->event = new StrategyEvent($this->strategy, $this->entity, $context); } public function testGetStrategy() From 6d17a02e868848a2f45ffd4ea3bf1d903f596c33 Mon Sep 17 00:00:00 2001 From: Denis Voronin Date: Tue, 15 Dec 2015 21:35:24 +0200 Subject: [PATCH 125/471] BAP-9503: Tags links are broken after editing --- .../Api/Rest/TaggableController.php | 28 +++++++++++++++++-- .../Api/Rest/TaggableController.php | 5 +++- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/Oro/Bundle/TagBundle/Controller/Api/Rest/TaggableController.php b/src/Oro/Bundle/TagBundle/Controller/Api/Rest/TaggableController.php index 86bf165b8e9..0295e199886 100644 --- a/src/Oro/Bundle/TagBundle/Controller/Api/Rest/TaggableController.php +++ b/src/Oro/Bundle/TagBundle/Controller/Api/Rest/TaggableController.php @@ -9,6 +9,7 @@ use FOS\RestBundle\Controller\Annotations\NamePrefix; use FOS\RestBundle\Controller\Annotations\RouteResource; use FOS\RestBundle\Controller\Annotations\Post; +use FOS\RestBundle\Util\Codes; use Oro\Bundle\SoapBundle\Controller\Api\Rest\RestController; @@ -19,7 +20,7 @@ class TaggableController extends RestController { /** - * Sets tags to the target entity. + * Sets tags to the target entity and return them. * * @param string $entity The type of the target entity. * @param int $entityId The id of the target entity. @@ -27,7 +28,7 @@ class TaggableController extends RestController * @Post("/tags/{entity}/{entityId}") * * @ApiDoc( - * description="Sets tags to the target entity", + * description="Sets tags to the target entity and return them", * resource=true * ) * @@ -38,7 +39,28 @@ public function postAction($entity, $entityId) $manager = $this->getManager(); $manager->setClass($manager->resolveEntityClass($entity)); - return $this->handleUpdateRequest($entityId); + $entity = $this->getManager()->find($entityId); + + if ($entity) { + $entity = $this->processForm($entity); + if ($entity) { + $result = $this->get('oro_tag.tag.manager')->getPreparedArray($entity); + + // Returns tags for the updated entity. + return $this->buildResponse( + $result, + self::ACTION_READ, + ['result' => $result], + Codes::HTTP_OK + ); + } else { + $view = $this->view($this->getForm(), Codes::HTTP_BAD_REQUEST); + } + } else { + $view = $this->view(null, Codes::HTTP_NOT_FOUND); + } + + return $this->buildResponse($view, self::ACTION_UPDATE, ['id' => $entityId, 'entity' => $entity]); } /** diff --git a/src/Oro/Bundle/TagBundle/Tests/Functional/Controller/Api/Rest/TaggableController.php b/src/Oro/Bundle/TagBundle/Tests/Functional/Controller/Api/Rest/TaggableController.php index f9ca38b1f82..4009f86ab7b 100644 --- a/src/Oro/Bundle/TagBundle/Tests/Functional/Controller/Api/Rest/TaggableController.php +++ b/src/Oro/Bundle/TagBundle/Tests/Functional/Controller/Api/Rest/TaggableController.php @@ -30,6 +30,9 @@ public function testPostAction() ] ] ); - $this->assertEmptyResponseStatusCodeEquals($this->client->getResponse(), 204); + + $result = $this->getJsonResponseContent($this->client->getResponse(), 200); + $this->assertNotEmpty($result); + $this->assertCount(2, $result, 'Result should contains information about 2 tags.'); } } From 86e4902b509305626bf67d099d93ffe23e0e9fe6 Mon Sep 17 00:00:00 2001 From: Denis Voronin Date: Tue, 15 Dec 2015 21:37:13 +0200 Subject: [PATCH 126/471] BAP-9503: Tags links are broken after editing --- .../Rest/{TaggableController.php => TaggableControllerTest.php} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/Oro/Bundle/TagBundle/Tests/Functional/Controller/Api/Rest/{TaggableController.php => TaggableControllerTest.php} (100%) diff --git a/src/Oro/Bundle/TagBundle/Tests/Functional/Controller/Api/Rest/TaggableController.php b/src/Oro/Bundle/TagBundle/Tests/Functional/Controller/Api/Rest/TaggableControllerTest.php similarity index 100% rename from src/Oro/Bundle/TagBundle/Tests/Functional/Controller/Api/Rest/TaggableController.php rename to src/Oro/Bundle/TagBundle/Tests/Functional/Controller/Api/Rest/TaggableControllerTest.php From eeff54e7192edc7e8f54458aa780e5ba122dd392 Mon Sep 17 00:00:00 2001 From: Olga Syzova Date: Tue, 15 Dec 2015 22:10:20 +0200 Subject: [PATCH 127/471] BAP-9515: Oro:platform:update migration fails with not nullable value in OroDataGridBundle --- src/Oro/Bundle/DataGridBundle/Entity/GridView.php | 8 ++++++-- .../DataGridBundle/Extension/GridViews/View.php | 4 ++++ .../Migrations/Schema/OroDataGridBundleInstaller.php | 2 +- .../Migrations/Schema/v1_1/OroDataGridBundle.php | 12 +++++++++++- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/Oro/Bundle/DataGridBundle/Entity/GridView.php b/src/Oro/Bundle/DataGridBundle/Entity/GridView.php index 3d90134dd3e..a7c23ac2649 100644 --- a/src/Oro/Bundle/DataGridBundle/Entity/GridView.php +++ b/src/Oro/Bundle/DataGridBundle/Entity/GridView.php @@ -90,7 +90,7 @@ class GridView /** * @var array * - * @ORM\Column(type="array") + * @ORM\Column(type="array", nullable=true) */ protected $columnsData = []; @@ -240,6 +240,10 @@ public function setSortersData(array $sortersData = []) */ public function getColumnsData() { + if ($this->columnsData === null) { + $this->columnsData = []; + } + return $this->columnsData; } @@ -280,7 +284,7 @@ public function setOwner(User $owner = null) */ public function createView() { - $view = new View($this->id, $this->filtersData, $this->sortersData, $this->type, $this->columnsData); + $view = new View($this->id, $this->filtersData, $this->sortersData, $this->type, $this->getColumnsData()); $view->setLabel($this->name); return $view; diff --git a/src/Oro/Bundle/DataGridBundle/Extension/GridViews/View.php b/src/Oro/Bundle/DataGridBundle/Extension/GridViews/View.php index ccd78efe42f..292a5c750e6 100644 --- a/src/Oro/Bundle/DataGridBundle/Extension/GridViews/View.php +++ b/src/Oro/Bundle/DataGridBundle/Extension/GridViews/View.php @@ -179,6 +179,10 @@ public function setDeletable($deletable = true) */ public function getColumnsData() { + if ($this->columnsData === null) { + $this->columnsData = []; + } + return $this->columnsData; } diff --git a/src/Oro/Bundle/DataGridBundle/Migrations/Schema/OroDataGridBundleInstaller.php b/src/Oro/Bundle/DataGridBundle/Migrations/Schema/OroDataGridBundleInstaller.php index 1c82bb136df..354bec81c06 100644 --- a/src/Oro/Bundle/DataGridBundle/Migrations/Schema/OroDataGridBundleInstaller.php +++ b/src/Oro/Bundle/DataGridBundle/Migrations/Schema/OroDataGridBundleInstaller.php @@ -38,7 +38,7 @@ protected function createOroGridViewTable(Schema $schema) $table->addColumn('type', 'string', ['length' => 255]); $table->addColumn('filtersData', 'array', ['comment' => '(DC2Type:array)']); $table->addColumn('sortersData', 'array', ['comment' => '(DC2Type:array)']); - $table->addColumn('columnsData', 'array', ['comment' => '(DC2Type:array)']); + $table->addColumn('columnsData', 'array', ['comment' => '(DC2Type:array)', 'notnull' => false]); $table->addColumn('gridName', 'string', ['length' => 255]); $table->setPrimaryKey(['id']); $table->addIndex(['user_owner_id'], 'IDX_5B73FBCB9EB185F9', []); diff --git a/src/Oro/Bundle/DataGridBundle/Migrations/Schema/v1_1/OroDataGridBundle.php b/src/Oro/Bundle/DataGridBundle/Migrations/Schema/v1_1/OroDataGridBundle.php index 39ba41f404c..be132815da8 100644 --- a/src/Oro/Bundle/DataGridBundle/Migrations/Schema/v1_1/OroDataGridBundle.php +++ b/src/Oro/Bundle/DataGridBundle/Migrations/Schema/v1_1/OroDataGridBundle.php @@ -6,6 +6,7 @@ use Oro\Bundle\MigrationBundle\Migration\Migration; use Oro\Bundle\MigrationBundle\Migration\QueryBag; +use Oro\Bundle\MigrationBundle\Migration\ParametrizedSqlMigrationQuery; class OroDataGridBundle implements Migration { @@ -16,6 +17,15 @@ public function up(Schema $schema, QueryBag $queries) { $table = $schema->getTable('oro_grid_view'); - $table->addColumn('columnsData', 'array', ['comment' => '(DC2Type:array)']); + $table->addColumn('columnsData', 'array', ['comment' => '(DC2Type:array)', 'notnull' => false]); + + if ($table->hasColumn('columnsData')) { + $queries->addPostQuery( + new ParametrizedSqlMigrationQuery( + 'UPDATE oro_grid_view SET columnsData = :columnsData WHERE columnsData is NULL; ', + ['columnsData' => serialize([])] + ) + ); + } } } From 95128d110002bf6314891122529f803db37c07f1 Mon Sep 17 00:00:00 2001 From: Ilya Antipenko Date: Wed, 16 Dec 2015 12:36:55 +0200 Subject: [PATCH 128/471] BB-1738: Use collection for Zip Codes (in Tax Jurisdiction) instead of text field - Region not always required --- .../AddressBundle/Resources/public/js/region/view.js | 9 +++++++-- .../Resources/views/Include/fields.html.twig | 11 +++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/Oro/Bundle/AddressBundle/Resources/public/js/region/view.js b/src/Oro/Bundle/AddressBundle/Resources/public/js/region/view.js index 9ff0c7ec1cb..d082c0afa3f 100644 --- a/src/Oro/Bundle/AddressBundle/Resources/public/js/region/view.js +++ b/src/Oro/Bundle/AddressBundle/Resources/public/js/region/view.js @@ -32,6 +32,7 @@ define([ this.$simpleEl.attr('type', 'text'); this.showSelect = options.showSelect; + this.regionRequired = options.regionRequired; this.template = _.template($('#region-chooser-template').html()); @@ -50,11 +51,15 @@ define([ */ displaySelect2: function(display) { if (display) { - this.addRequiredFlag(this.$simpleEl); + if (this.regionRequired) { + this.addRequiredFlag(this.$simpleEl); + } this.target.select2('container').show(); } else { this.target.select2('container').hide(); - this.removeRequiredFlag(this.$simpleEl); + if (this.regionRequired) { + this.removeRequiredFlag(this.$simpleEl); + } this.target.validate().hideElementErrors(this.target); } }, diff --git a/src/Oro/Bundle/AddressBundle/Resources/views/Include/fields.html.twig b/src/Oro/Bundle/AddressBundle/Resources/views/Include/fields.html.twig index 61512ac33a2..805b50c8926 100644 --- a/src/Oro/Bundle/AddressBundle/Resources/views/Include/fields.html.twig +++ b/src/Oro/Bundle/AddressBundle/Resources/views/Include/fields.html.twig @@ -16,7 +16,13 @@ {% endif %} {% set region_text_field = form.parent[region_text_field] %} - {{ form_widget(form, {'attr': {'data-validation': { NotBlank: null}|json_encode} }) }} + {% set attr = {} %} + + {% if required %} + {% set attr = attr|merge({'attr': {'data-validation': { NotBlank: null}|json_encode} }) %} + {% endif %} + + {{ form_widget(form, attr) }} {% set showSelect = (choices is not empty and region_text_field.vars.value is empty) %} - + diff --git a/src/Oro/Component/Layout/Block/Type/BaseType.php b/src/Oro/Component/Layout/Block/Type/BaseType.php index 12795a9f947..9d21d5ee478 100644 --- a/src/Oro/Component/Layout/Block/Type/BaseType.php +++ b/src/Oro/Component/Layout/Block/Type/BaseType.php @@ -19,7 +19,6 @@ public function setDefaultOptions(OptionsResolverInterface $resolver) $resolver->setOptional( [ 'vars', - 'tag', 'attr', 'label', 'label_attr', @@ -30,7 +29,6 @@ public function setDefaultOptions(OptionsResolverInterface $resolver) $resolver->setAllowedTypes( [ 'vars' => 'array', - 'tag' => 'string', 'attr' => 'array', 'label_attr' => 'array', 'class_prefix' => 'string' @@ -51,7 +49,6 @@ public function buildView(BlockView $view, BlockInterface $block, array $options // add the view to itself vars to allow get it using 'block' variable in a rendered, for example TWIG $view->vars['block'] = $view; - $view->vars['tag'] = isset($options['tag']) ? $options['tag'] : null; $view->vars['class_prefix'] = null; if (isset($options['class_prefix'])) { $view->vars['class_prefix'] = $options['class_prefix']; diff --git a/src/Oro/Component/Layout/BlockTypeExtensionInterface.php b/src/Oro/Component/Layout/BlockTypeExtensionInterface.php index 84b6b4e6a15..37455af07dd 100644 --- a/src/Oro/Component/Layout/BlockTypeExtensionInterface.php +++ b/src/Oro/Component/Layout/BlockTypeExtensionInterface.php @@ -56,6 +56,7 @@ public function setDefaultOptions(OptionsResolverInterface $resolver); /** * Normalize options after they was resolved. + * You can implement this function to manipulate block options before build. * * @param array $options * @param ContextInterface $context diff --git a/src/Oro/Component/Layout/Tests/Unit/LayoutTestCase.php b/src/Oro/Component/Layout/Tests/Unit/LayoutTestCase.php index cca934ff430..c67e7a01d83 100644 --- a/src/Oro/Component/Layout/Tests/Unit/LayoutTestCase.php +++ b/src/Oro/Component/Layout/Tests/Unit/LayoutTestCase.php @@ -45,9 +45,6 @@ protected function completeView(array &$view) if (!isset($view['children'])) { $view['children'] = []; } - if (!isset($view['vars']['tag'])) { - $view['vars']['tag'] = null; - } if (!isset($view['vars']['class_prefix'])) { $view['vars']['class_prefix'] = null; } From 4ee80d87ef5dd020a264d59aad6e03a897ea22a9 Mon Sep 17 00:00:00 2001 From: Mykhailo Sulyma Date: Thu, 24 Dec 2015 19:25:20 +0200 Subject: [PATCH 254/471] BB-1777: Action Improvements - update documentations --- src/Oro/Component/Config/README.md | 2 +- .../Resources/doc/configuration_merger.md | 279 ++++++++++++++++++ 2 files changed, 280 insertions(+), 1 deletion(-) create mode 100644 src/Oro/Component/Config/Resources/doc/configuration_merger.md diff --git a/src/Oro/Component/Config/README.md b/src/Oro/Component/Config/README.md index 616c053a861..a662464ac3b 100644 --- a/src/Oro/Component/Config/README.md +++ b/src/Oro/Component/Config/README.md @@ -11,7 +11,7 @@ Resource Types Resource Merge -------------- - - [Configuration Merger](./Resources/doc/configuration_merger.md) provides a way to merge configurations of some resource both from one or many bundles. Supports two strategy: replace and append config. + - [Configuration Merger](./Resources/doc/configuration_merger.md) provides a way to merge configurations of some resource both from one or many bundles. Supports two strategy: replace and append. System Aware Resolver diff --git a/src/Oro/Component/Config/Resources/doc/configuration_merger.md b/src/Oro/Component/Config/Resources/doc/configuration_merger.md new file mode 100644 index 00000000000..f251b58c4b2 --- /dev/null +++ b/src/Oro/Component/Config/Resources/doc/configuration_merger.md @@ -0,0 +1,279 @@ +Configuration Merger +==================== + +This class provides a way to merge configurations with equal name from any configuration groups and way to extend one +configuration from other configuration. Also exists mechanism to replace some nodes of original configuration by nodes +of configuration which will be extended from this config. For this case you need to set node `replace` with list of +nodes, which you want to replace, on the same level of this nodes. + +Initialization +-------------- + +For creating new instance of merger you need list of some keys. It will use as sorting order for merging all +configuration from groups which have equal name. + +``` php +container->getParameter('kernel.bundles'); + + $merger = new ConfigurationMerger($bundles); + ... + } +} +... +``` + +Using example +------------- + +Please imagine that you need to load configurations from `Resources\config\acme.yml` file located in any bundle in your +application and merge them to final configurations. For example one bundle, which will be loaded last, override some +part of configuration from other bundle. All process to load this configurations shows below. + +``` php +load($container); + $configs = []; + + foreach ($resources as $resource) { + $configs[$resource->bundleClass] = $resource->data; + } + + $bundles = $this->container->getParameter('kernel.bundles'); + + $merger = new ConfigurationMerger($bundles); + $configs = $merger->mergeConfiguration($configs); + ... + } +} +``` + +Examples +-------- + +### Merge configurations with the same name from two bundles (use append strategy) + +Order of bundles loading: `FirstBundle`, `SecondBundle`. + +``` yaml +# Acme\Bundle\FirstBundle\Resources\config\acme.yml + +acme_config: + param: value + array_param: + sub_array_param1: value1 + sub_array_param2: value2 +``` + +``` yaml +# Acme\Bundle\SecondBundle\Resources\config\acme.yml + +acme_config: + param: replaced_value + array_param: + sub_array_param3: value3 +``` + +Result: +``` yaml +acme_config: + param: replaced_value + array_param: + sub_array_param1: value1 + sub_array_param2: value2 + sub_array_param3: value3 +``` + +### Extends one configuration from other configuration (use append strategy) +``` yaml +# Acme\Bundle\DemoBundle\Resources\config\acme.yml + +acme_config_base: + param: value + array_param: + sub_array_param1: value1 + sub_array_param2: value2 + +acme_config: + extends: acme_config_base + new_param: new_value + array_param: + sub_array_param3: value3 +``` + +Result: +``` yaml +acme_config_base: + param: value + array_param: + sub_array_param1: value1 + sub_array_param2: value2 + +acme_config: + param: value + array_param: + sub_array_param1: value1 + sub_array_param2: value2 + sub_array_param3: value3 + new_param: new_value +``` + +### Merge configurations with the same name from two bundles and extends one configuration from other configuration (use append strategy) + +Order of bundles loading: `FirstBundle`, `SecondBundle`. + +``` yaml +# Acme\Bundle\FirstBundle\Resources\config\acme.yml + +acme_config_base: + param: value + array_param: + sub_array_param1: value1 + sub_array_param2: value2 + +acme_config: + extends: acme_config_base + new_param: new_value + array_param: + sub_array_param4: value4 +``` + +``` yaml +# Acme\Bundle\SecondBundle\Resources\config\acme.yml + +acme_config_base: + param: replaced_value + array_param: + sub_array_param3: value3 + +``` + +Result: +``` yaml +acme_config_base: + param: replaced_value + array_param: + sub_array_param1: value1 + sub_array_param2: value2 + sub_array_param3: value3 + +acme_config: + param: replaced_value + array_param: + sub_array_param1: value1 + sub_array_param2: value2 + sub_array_param3: value3 + sub_array_param4: value4 + new_param: new_value +``` + +### Extends one configuration from other configuration (use append and replace strategies) +``` yaml +# Acme\Bundle\DemoBundle\Resources\config\acme.yml + +acme_config_base: + param: value + array_param: + sub_array_param1: value1 + sub_array_param2: value2 + +acme_config: + extends: acme_config_base + replace: [array_param] + new_param: new_value + array_param: + sub_array_param3: value3 +``` + +Result: +``` yaml +acme_config_base: + param: value + array_param: + sub_array_param1: value1 + sub_array_param2: value2 + +acme_config: + param: value + array_param: + sub_array_param3: value3 + new_param: new_value +``` + +### Merge configurations with the same name from two bundles and extends one configuration from other configuration (use append and replace strategy) + +Order of bundles loading: `FirstBundle`, `SecondBundle`. + +``` yaml +# Acme\Bundle\FirstBundle\Resources\config\acme.yml + +acme_config_base: + param: value + array_param: + sub_array_param1: value1 + sub_array_param2: value2 + +acme_config: + extends: acme_config_base + replace: [array_param] + new_param: new_value + array_param: + sub_array_param4: value4 +``` + +``` yaml +# Acme\Bundle\SecondBundle\Resources\config\acme.yml + +acme_config_base: + param: replaced_value + array_param: + sub_array_param3: value3 + +``` + +Result: +``` yaml +acme_config_base: + param: replaced_value + array_param: + sub_array_param1: value1 + sub_array_param2: value2 + sub_array_param3: value3 + +acme_config: + param: replaced_value + array_param: + sub_array_param4: value4 + new_param: new_value +``` From 26f11e2a03c8efcc57f86f71fb6f3f9bab1e4ec9 Mon Sep 17 00:00:00 2001 From: zebimax Date: Thu, 24 Dec 2015 19:52:53 +0200 Subject: [PATCH 255/471] BAP-9576: Add field 'default' to GridView - rename field --- .../Bundle/DataGridBundle/Entity/GridView.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Oro/Bundle/DataGridBundle/Entity/GridView.php b/src/Oro/Bundle/DataGridBundle/Entity/GridView.php index a56e7fcf521..e16a9f38c6d 100644 --- a/src/Oro/Bundle/DataGridBundle/Entity/GridView.php +++ b/src/Oro/Bundle/DataGridBundle/Entity/GridView.php @@ -133,14 +133,14 @@ class GridView * inverseJoinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id", onDelete="CASCADE")}, * ) */ - protected $chosenDefaultUsers; + protected $users; /** * GridView constructor. */ public function __construct() { - $this->chosenDefaultUsers = new ArrayCollection(); + $this->users = new ArrayCollection(); } /** @@ -349,9 +349,9 @@ public function getOrganization() /** * @return ArrayCollection|User[] */ - public function getChosenDefaultUsers() + public function getUsers() { - return $this->chosenDefaultUsers; + return $this->users; } /** @@ -359,10 +359,10 @@ public function getChosenDefaultUsers() * * @return $this */ - public function addChosenDefaultUser(User $user) + public function addUser(User $user) { - if (!$this->chosenDefaultUsers->contains($user)) { - $this->chosenDefaultUsers->add($user); + if (!$this->users->contains($user)) { + $this->users->add($user); } return $this; @@ -371,8 +371,8 @@ public function addChosenDefaultUser(User $user) /** * @param User $user */ - public function removeChosenDefaultUser(User $user) + public function removeUser(User $user) { - $this->chosenDefaultUsers->removeElement($user); + $this->users->removeElement($user); } } From da5259e27c765869d4b81bd27cd0f564b776d703 Mon Sep 17 00:00:00 2001 From: dmitrosh Date: Thu, 24 Dec 2015 20:12:06 +0200 Subject: [PATCH 256/471] CRM-4829: remove flex styles because IE incorrect rendering of popup --- .../Resources/public/css/less/oro.less | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/src/Oro/Bundle/UIBundle/Resources/public/css/less/oro.less b/src/Oro/Bundle/UIBundle/Resources/public/css/less/oro.less index c53bf83e964..747902fdb2a 100644 --- a/src/Oro/Bundle/UIBundle/Resources/public/css/less/oro.less +++ b/src/Oro/Bundle/UIBundle/Resources/public/css/less/oro.less @@ -878,13 +878,8 @@ footer { } } .ui-dialog.ui-resizable:not(.ui-dialog-buttons) { - &:after{ - display: block; - height: 15px; - content: " "; - flex: none; - } .ui-dialog-content{ + height: calc(~"100% - 50px") !important; border-radius: 0 0 3px 3px; border-bottom: 1px solid #e5e5e5; } @@ -1654,19 +1649,6 @@ footer { } } &.ui-dialog-normal { - display: flex !important; - flex-direction: column; - .ui-dialog-titlebar { - min-height: 34px; - flex: none; - } - .ui-dialog-content { - flex: 1; - } - .ui-dialog-buttonpane { - height: 49px; - flex: none; - } [class*='ui-dialog-titlebar-'] { &:hover:before { #gradient > .vertical(darken(#838d98,5%), darken(#46515d,5%)); From ca0c67c621bbda0436693e8772d9a88aa6e6a90d Mon Sep 17 00:00:00 2001 From: Dmitry Naydenov Date: Thu, 24 Dec 2015 20:56:49 +0200 Subject: [PATCH 257/471] BAP-9301 Wrong List of BU in the filter on the grid --- .../public/js/filter/choice-tree-filter.js | 33 ++- .../Filter/BusinessUnitChoiceFilter.php | 14 +- .../Filter/ChoiceTreeBusinessUnitProvider.php | 79 ++++-- .../Resources/config/services.yml | 1 + .../ChoiceTreeBusinessUnitProviderTest.php | 248 ++++++++++++------ 5 files changed, 261 insertions(+), 114 deletions(-) diff --git a/src/Oro/Bundle/FilterBundle/Resources/public/js/filter/choice-tree-filter.js b/src/Oro/Bundle/FilterBundle/Resources/public/js/filter/choice-tree-filter.js index df43b8a974e..d271c102568 100644 --- a/src/Oro/Bundle/FilterBundle/Resources/public/js/filter/choice-tree-filter.js +++ b/src/Oro/Bundle/FilterBundle/Resources/public/js/filter/choice-tree-filter.js @@ -248,20 +248,27 @@ define(function(require) { return rootArray; }, - _convertToTree: function(data) { - var self = this; - var response = []; - _.each(data, function(value) { - if (!value.owner_id) { - response.push({ - value: value, - children: [] - }); - } - }); + _convertToTree: function (data) { + var response = [], + idToNodeMap = {}; - _.each(response, function(value, key) { - response[key].children = self.searchEngine.findChild(value, data); + _.each(data, function (value) { + var datum = {}; + datum.value = value; + datum.children = []; + + idToNodeMap[datum.value.id] = datum; + + if (!datum.value.owner_id) { + response.push(datum); + } else { + var parentNode = idToNodeMap[datum.value.owner_id]; + if (parentNode) { + parentNode.children.push(datum); + } else { + response.push(datum); + } + } }); return response; diff --git a/src/Oro/Bundle/OrganizationBundle/Filter/BusinessUnitChoiceFilter.php b/src/Oro/Bundle/OrganizationBundle/Filter/BusinessUnitChoiceFilter.php index 51a7127831d..8f0721375fe 100644 --- a/src/Oro/Bundle/OrganizationBundle/Filter/BusinessUnitChoiceFilter.php +++ b/src/Oro/Bundle/OrganizationBundle/Filter/BusinessUnitChoiceFilter.php @@ -18,7 +18,7 @@ public function apply(FilterDatasourceAdapterInterface $ds, $data) return false; } - $type = $data['type']; + $type = $data['type']; if (count($data['value']) > 1 || (isset($data['value'][0]) && $data['value'][0] != "")) { $parameterName = $ds->generateParameterName($this->getName()); @@ -26,30 +26,30 @@ public function apply(FilterDatasourceAdapterInterface $ds, $data) ->createQueryBuilder('u') ->select('u.id') ->leftJoin('u.businessUnits', 'bu') - ->where('bu.id in (:'.$parameterName.')') + ->where('bu.id in (:' . $parameterName . ')') ->getQuery() - ->getDQL(); + ->getDQL(); $this->applyFilterToClause( $ds, - $this->get(FilterUtility::DATA_NAME_KEY) . ' in ('.$qb2.')' + $this->get(FilterUtility::DATA_NAME_KEY) . ' in (' . $qb2 . ')' ); if (!in_array($type, [FilterUtility::TYPE_EMPTY, FilterUtility::TYPE_NOT_EMPTY], true)) { $ds->setParameter($parameterName, $data['value']); } } + return true; } /** - * @param $data - * - * @return mixed + * {@inheritDoc} */ public function parseData($data) { $data['value'] = explode(',', $data['value']); + return $data; } } diff --git a/src/Oro/Bundle/OrganizationBundle/Provider/Filter/ChoiceTreeBusinessUnitProvider.php b/src/Oro/Bundle/OrganizationBundle/Provider/Filter/ChoiceTreeBusinessUnitProvider.php index 650b51d2f2a..ee42169e2ed 100644 --- a/src/Oro/Bundle/OrganizationBundle/Provider/Filter/ChoiceTreeBusinessUnitProvider.php +++ b/src/Oro/Bundle/OrganizationBundle/Provider/Filter/ChoiceTreeBusinessUnitProvider.php @@ -5,10 +5,12 @@ use Doctrine\Bundle\DoctrineBundle\Registry; use Oro\Bundle\SecurityBundle\ORM\Walker\AclHelper; +use Oro\Bundle\SecurityBundle\Owner\ChainOwnerTreeProvider; +use Oro\Bundle\SecurityBundle\Owner\OwnerTree; use Oro\Bundle\SecurityBundle\SecurityFacade; -use Oro\Bundle\OrganizationBundle\Entity\Organization; use Oro\Bundle\OrganizationBundle\Entity\Repository\BusinessUnitRepository; use Oro\Bundle\OrganizationBundle\Entity\BusinessUnit; +use Oro\Bundle\UserBundle\Entity\User; class ChoiceTreeBusinessUnitProvider { @@ -21,44 +23,56 @@ class ChoiceTreeBusinessUnitProvider /** @var SecurityFacade */ protected $securityFacade; + /** @var ChainOwnerTreeProvider */ + protected $treeProvider; + /** - * @param Registry $registry - * @param SecurityFacade $securityFacade - * @param AclHelper $aclHelper + * @param Registry $registry + * @param SecurityFacade $securityFacade + * @param AclHelper $aclHelper + * @param ChainOwnerTreeProvider $treeProvider */ public function __construct( Registry $registry, SecurityFacade $securityFacade, - AclHelper $aclHelper + AclHelper $aclHelper, + ChainOwnerTreeProvider $treeProvider ) { - $this->registry = $registry; + $this->registry = $registry; $this->securityFacade = $securityFacade; - $this->aclHelper = $aclHelper; + $this->aclHelper = $aclHelper; + $this->treeProvider = $treeProvider; } /** - * @param Organization $currentOrganization - * * @return array */ public function getList() { - $businessUnitRepository = $this->getBusinessUnitRepo(); + $businessUnitRepo = $this->getBusinessUnitRepo(); + $response = []; - $qb = $businessUnitRepository->getQueryBuilder(); + $qb = $businessUnitRepo->getQueryBuilder(); + + $qb->andWhere( + $qb->expr()->in('businessUnit.id', ':ids') + ); + + $qb->setParameter('ids', $this->getBusinessUnitIds()); + $businessUnits = $this->aclHelper->apply($qb)->getResult(); /** @var BusinessUnit $businessUnit */ foreach ($businessUnits as $businessUnit) { if ($businessUnit->getOwner()) { - $name =$businessUnit->getName(); + $name = $businessUnit->getName(); } else { $name = $this->getBusinessUnitName($businessUnit); } $response[] = [ - 'id' => $businessUnit->getId(), - 'name' => $name, + 'id' => $businessUnit->getId(), + 'name' => $name, 'owner_id' => $businessUnit->getOwner() ? $businessUnit->getOwner()->getId() : null ]; } @@ -81,6 +95,41 @@ protected function getBusinessUnitRepo() */ protected function getBusinessUnitName(BusinessUnit $businessUnit) { - return $businessUnit->getName(); + return $businessUnit->getName(); + } + + /** + * @return User + */ + protected function getUser() + { + return $this->securityFacade->getToken()->getUser(); + } + + /** + * @return array + */ + protected function getBusinessUnitIds() + { + $user = $this->getUser(); + $organization = $user->getOrganization(); + + /** @var OwnerTree $tree */ + $tree = $this->treeProvider->getTree(); + + $userBUIds = $tree->getUserBusinessUnitIds( + $user->getId(), + $organization->getId() + ); + + $result = []; + foreach ($userBUIds as $businessUnitId) { + $subordinateBUIds = $tree->getSubordinateBusinessUnitIds($businessUnitId); + $result = array_merge($subordinateBUIds, $result); + } + + return array_unique( + array_merge($userBUIds, $result) + ); } } diff --git a/src/Oro/Bundle/OrganizationBundle/Resources/config/services.yml b/src/Oro/Bundle/OrganizationBundle/Resources/config/services.yml index 6fc011d8f57..98d0ec35c6d 100644 --- a/src/Oro/Bundle/OrganizationBundle/Resources/config/services.yml +++ b/src/Oro/Bundle/OrganizationBundle/Resources/config/services.yml @@ -200,6 +200,7 @@ services: - @doctrine - @oro_security.security_facade - @oro_security.acl_helper + - @oro_security.ownership_tree_provider.chain oro_organization.autocomplete.business_unit.search_handler: class: %oro_organization.autocomplete.business_unit.search_handler.class% diff --git a/src/Oro/Bundle/OrganizationBundle/Tests/Unit/Provider/Filter/ChoiceTreeBusinessUnitProviderTest.php b/src/Oro/Bundle/OrganizationBundle/Tests/Unit/Provider/Filter/ChoiceTreeBusinessUnitProviderTest.php index 0ea4d9a949f..61a0c69bfe8 100644 --- a/src/Oro/Bundle/OrganizationBundle/Tests/Unit/Provider/Filter/ChoiceTreeBusinessUnitProviderTest.php +++ b/src/Oro/Bundle/OrganizationBundle/Tests/Unit/Provider/Filter/ChoiceTreeBusinessUnitProviderTest.php @@ -7,7 +7,7 @@ class ChoiceTreeBusinessUnitProviderTest extends \PHPUnit_Framework_TestCase { /** @var ChoiceTreeBusinessUnitProvider */ - protected $choiceTreeBusinessUnitProvider; + protected $choiceTreeBUProvider; /** @var \PHPUnit_Framework_MockObject_MockObject */ protected $registry; @@ -18,133 +18,223 @@ class ChoiceTreeBusinessUnitProviderTest extends \PHPUnit_Framework_TestCase /** @var \PHPUnit_Framework_MockObject_MockObject */ protected $aclHelper; + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $treeProvider; + public function setUp() { - $this->registry = $this->getMockBuilder('Doctrine\Bundle\DoctrineBundle\Registry') + $this->registry = $this->getMockBuilder('Doctrine\Bundle\DoctrineBundle\Registry') ->disableOriginalConstructor() ->getMock(); $this->securityFacade = $this->getMockBuilder('Oro\Bundle\SecurityBundle\SecurityFacade') ->disableOriginalConstructor() ->getMock(); - $this->aclHelper = $this->getMockBuilder('Oro\Bundle\SecurityBundle\ORM\Walker\AclHelper') + $this->aclHelper = $this->getMockBuilder('Oro\Bundle\SecurityBundle\ORM\Walker\AclHelper') + ->disableOriginalConstructor() + ->getMock(); + + $this->treeProvider = $this->getMockBuilder('Oro\Bundle\SecurityBundle\Owner\ChainOwnerTreeProvider') + ->setMethods(['getTree']) ->disableOriginalConstructor() ->getMock(); - $this->choiceTreeBusinessUnitProvider = new ChoiceTreeBusinessUnitProvider( + $this->choiceTreeBUProvider = new ChoiceTreeBusinessUnitProvider( $this->registry, $this->securityFacade, - $this->aclHelper + $this->aclHelper, + $this->treeProvider ); } - public function testGetList() + /** + * @dataProvider getListDataProvider + */ + public function testGetList($userBUIds, $subordinateBUIds, $times, $queryResult, $result) { - $businessUnitRepository = $this->getMockBuilder('BusinessUnitRepository') + $businessUnitRepos = $this->getMockBuilder('BusinessUnitRepository') ->disableOriginalConstructor() ->setMethods(['getQueryBuilder']) ->getMock(); $qb = $this->getMockBuilder('Doctrine\ORM\QueryBuilder') - ->setMethods(['getResult']) + ->setMethods(['getResult', 'expr', 'setParameter']) ->disableOriginalConstructor() ->getMock(); - $this->aclHelper->expects($this->any())->method('apply')->willReturn($qb); - $businessUnitRepository->expects($this->any())->method('getQueryBuilder')->willReturn($qb); - $qb->expects($this->any())->method('getResult')->willReturn($this->getTestBusinessUnits()); - - $this->registry->expects($this->once())->method('getRepository')->with('OroOrganizationBundle:BusinessUnit') - ->willReturn($businessUnitRepository); + $treeOwner = $this->getMockBuilder('Oro\Bundle\SecurityBundle\Owner\OwnerTree') + ->setMethods(['getUserBusinessUnitIds', 'getSubordinateBusinessUnitIds']) + ->disableOriginalConstructor() + ->getMock(); - $result = $this->choiceTreeBusinessUnitProvider->getList(); + $this->treeProvider->expects(self::once())->method('getTree')->willReturn($treeOwner); + $treeOwner->expects(self::once())->method('getUserBusinessUnitIds')->willReturn($userBUIds); + $treeOwner + ->expects(self::exactly($times)) + ->method('getSubordinateBusinessUnitIds') + ->willReturn($subordinateBUIds); - $this->assertEquals($this->getExpectedData(), $result); - } + $this->aclHelper->expects(self::any())->method('apply')->willReturn($qb); + $businessUnitRepos->expects(self::any())->method('getQueryBuilder')->willReturn($qb); - public function testGetEmptyList() - { - $businessUnitRepository = $this->getMockBuilder('BusinessUnitRepository') + $expression = $this->getMockBuilder('Doctrine\ORM\Query\Expr') + ->setMethods(['in']) ->disableOriginalConstructor() - ->setMethods(['getQueryBuilder']) ->getMock(); - $qb = $this->getMockBuilder('Doctrine\ORM\QueryBuilder') - ->setMethods(['getResult']) + $qb->expects(self::once())->method('getResult')->willReturn($queryResult); + $qb->expects(self::once())->method('expr')->willReturn($expression); + $qb->expects(self::once())->method('setParameter'); + + $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + + $user = $this->getMockBuilder('Oro\Bundle\UserBundle\Entity\User') + ->setMethods(['getId', 'getOrganization']) ->disableOriginalConstructor() ->getMock(); - $this->aclHelper->expects($this->any())->method('apply')->willReturn($qb); - $businessUnitRepository->expects($this->any())->method('getQueryBuilder')->willReturn($qb); - $qb->expects($this->any())->method('getResult')->willReturn([]); + $organization = $this->getMock('Oro\Bundle\OrganizationBundle\Entity\OrganizationInterface'); + $organization->expects(self::once())->method('getId')->willReturn(1); + $user->expects(self::once())->method('getOrganization')->willReturn($organization); + $this->securityFacade->expects(self::once())->method('getToken')->willReturn($tokenStorage); + $tokenStorage->expects(self::once())->method('getUser')->willReturn($user); - $this->registry->expects($this->once())->method('getRepository')->with('OroOrganizationBundle:BusinessUnit') - ->willReturn($businessUnitRepository); + $this->registry + ->expects(self::once()) + ->method('getRepository') + ->with('OroOrganizationBundle:BusinessUnit') + ->willReturn($businessUnitRepos); - $result = $this->choiceTreeBusinessUnitProvider->getList(); + $resultedUserBUids = $this->choiceTreeBUProvider->getList(); - $this->assertEquals([], $result); + self::assertEquals($result, $resultedUserBUids); } - protected function getTestBusinessUnits() + /** + * @return array + */ + public function getListDataProvider() { - $data = [ - [ - 'name' => 'Main Business Unit', - 'id' => 1, - 'owner_id' => null + return [ + 'Three elements in the list' => [ + 'userBUIds' => [1], + 'subordinateBUIds' => [2, 3], + 'times' => 1, + 'queryResult' => $this->getBusinessUnits('one'), + 'result' => [ + [ + 'name' => 'Main Business Unit 1', + 'id' => 1, + 'owner_id' => null + ], + [ + 'name' => 'Business Unit 1', + 'id' => 2, + 'owner_id' => 1 + ], + [ + 'name' => 'Business Unit 2', + 'id' => 3, + 'owner_id' => 1 + ], + ] + ], + 'Six elements in the list' => [ + 'userBUIds' => [1, 2], + 'subordinateBUIds' => [3, 4, 5, 6], + 'times' => 2, + 'queryResult' => $this->getBusinessUnits('two'), + 'result' => [ + [ + 'name' => 'Main Business Unit 1', + 'id' => 1, + 'owner_id' => null + ], + [ + 'name' => 'Main Business Unit 2', + 'id' => 2, + 'owner_id' => null + ], + [ + 'name' => 'Business Unit 1', + 'id' => 3, + 'owner_id' => 1 + ], + [ + 'name' => 'Business Unit 2', + 'id' => 4, + 'owner_id' => 1 + ], + [ + 'name' => 'Business Unit 3', + 'id' => 5, + 'owner_id' => 2 + ], + [ + 'name' => 'Business Unit 4', + 'id' => 6, + 'owner_id' => 2 + ], + [ + 'name' => 'Business Unit 5', + 'id' => 7, + 'owner_id' => 4 + ] + ] + ], + 'empty list' => [ + 'userBUIds' => [], + 'subordinateBUIds' => [], + 'times' => 0, + 'queryResult' => [], + 'result' => [] ], - [ - 'name' => 'Business Uit 1', - 'id' => 2, - 'owner_id' => $this->getTestDataRootBusinessUnit() - ] ]; - - return $this->convertTestDataToBusinessUnitEntity($data); } - protected function convertTestDataToBusinessUnitEntity($data) + /** + * @param string $name + * + * @return array + */ + protected function getBusinessUnits($name) { - $response = []; - foreach ($data as $item) { - $businessUnit = $this->getMockBuilder('Oro\Bundle\OrganizationBundle\Entity\BusinessUnit') + $scheme = [ + 'one' => [ + ['name' => 'Main Business Unit 1', 'owner' => null, 'id' => 1], + ['name' => 'Business Unit 1', 'owner' => 1, 'id' => 2], + ['name' => 'Business Unit 2', 'owner' => 1, 'id' => 3] + ], + 'two' => [ + ['name' => 'Main Business Unit 1', 'owner' => null, 'id' => 1], + ['name' => 'Main Business Unit 2', 'owner' => null, 'id' => 2], + ['name' => 'Business Unit 1', 'owner' => 1, 'id' => 3], + ['name' => 'Business Unit 2', 'owner' => 1, 'id' => 4], + ['name' => 'Business Unit 3', 'owner' => 2, 'id' => 5], + ['name' => 'Business Unit 4', 'owner' => 2, 'id' => 6], + ['name' => 'Business Unit 5', 'owner' => 4, 'id' => 7], + ], + ]; + + $result = []; + $schemeSet = $scheme[$name]; + $schemeSetCount = count($schemeSet); + + for ($i = 0; $i < $schemeSetCount; $i++) { + $element = $this->getMockBuilder('Oro\Bundle\OrganizationBundle\Entity\BusinessUnit') ->disableOriginalConstructor() ->getMock(); - $businessUnit->expects($this->any())->method('getId')->willReturn($item['id']); - $businessUnit->expects($this->any())->method('getOwner')->willReturn($item['owner_id']); - $businessUnit->expects($this->any())->method('getName')->willReturn($item['name']); - $response[] = $businessUnit; - } + $owner = (null === $schemeSet[$i]['owner']) + ? $schemeSet[$i]['owner'] + : $result[$schemeSet[$i]['owner'] - 1]; - return $response; - } + $element->expects(self::any())->method('getOwner')->willReturn($owner); + $element->expects(self::any())->method('getName')->willReturn($schemeSet[$i]['name']); + $element->expects(self::any())->method('getId')->willReturn($schemeSet[$i]['id']); - protected function getTestDataRootBusinessUnit() - { - $rootBusinessUnit = $this->getMockBuilder('Oro\Bundle\OrganizationBundle\Entity\BusinessUnit') - ->disableOriginalConstructor() - ->getMock(); - $rootBusinessUnit->expects($this->any())->method('getId')->willReturn('1'); - $rootBusinessUnit->expects($this->any())->method('getOwner')->willReturn(null); - $rootBusinessUnit->expects($this->any())->method('getName')->willReturn('Main Business Unit'); - - return $rootBusinessUnit; - } + $result[] = $element; + } - protected function getExpectedData() - { - return [ - [ - 'name' => 'Main Business Unit', - 'id' => 1, - 'owner_id' => null - ], - [ - 'name' => 'Business Uit 1', - 'id' => 2, - 'owner_id' => 1 - ] - ]; + return $result; } } From a4d5cfa29f1eed754832926536a77baa94895409 Mon Sep 17 00:00:00 2001 From: Denis Voronin Date: Thu, 24 Dec 2015 21:24:34 +0200 Subject: [PATCH 258/471] BAP-9580: Disable grid views for reports and segments --- .../GridViews/GridViewsExtension.php | 19 ++++++++++++++++--- .../views/Report/table/view.html.twig | 12 +++++++++++- .../Resources/views/Segment/view.html.twig | 12 +++++++++++- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/Oro/Bundle/DataGridBundle/Extension/GridViews/GridViewsExtension.php b/src/Oro/Bundle/DataGridBundle/Extension/GridViews/GridViewsExtension.php index 560112ff2f9..90ed2a3a772 100644 --- a/src/Oro/Bundle/DataGridBundle/Extension/GridViews/GridViewsExtension.php +++ b/src/Oro/Bundle/DataGridBundle/Extension/GridViews/GridViewsExtension.php @@ -15,6 +15,9 @@ class GridViewsExtension extends AbstractExtension { + const GRID_VIEW_ROOT_PARAM = '_grid_view'; + const DISABLED_PARAM = '_disabled'; + const VIEWS_LIST_KEY = 'views_list'; const VIEWS_PARAM_KEY = 'view'; const MINIFIED_VIEWS_PARAM_KEY = 'v'; @@ -45,15 +48,25 @@ public function __construct( } /** - * {@inheritDoc} + * {@inheritdoc} */ public function isApplicable(DatagridConfiguration $config) { - return true; + return !$this->isDisabled(); + } + + /** + * @return bool + */ + protected function isDisabled() + { + $parameters = $this->getParameters()->get(self::GRID_VIEW_ROOT_PARAM, []); + + return !empty($parameters[self::DISABLED_PARAM]); } /** - * {@inheritDoc} + * {@inheritdoc} */ public function visitMetadata(DatagridConfiguration $config, MetadataObject $data) { diff --git a/src/Oro/Bundle/ReportBundle/Resources/views/Report/table/view.html.twig b/src/Oro/Bundle/ReportBundle/Resources/views/Report/table/view.html.twig index 32880ffa91d..22166fbaf91 100644 --- a/src/Oro/Bundle/ReportBundle/Resources/views/Report/table/view.html.twig +++ b/src/Oro/Bundle/ReportBundle/Resources/views/Report/table/view.html.twig @@ -41,7 +41,17 @@
            {% endif %} {% set renderParams = renderParams|default({})|merge({enableFullScreenLayout: true}) %} - {{ dataGrid.renderGrid(gridName, params|default({}), renderParams) }} + {% set params = params|default({})|merge({ + '_grid_view': { + '_disabled': true + }, + '_tags': { + '_disabled': true + } + }) + %} + + {{ dataGrid.renderGrid(gridName, params, renderParams) }} {% else %}
            diff --git a/src/Oro/Bundle/SegmentBundle/Resources/views/Segment/view.html.twig b/src/Oro/Bundle/SegmentBundle/Resources/views/Segment/view.html.twig index 3cd25950712..1bea9b8bbd9 100644 --- a/src/Oro/Bundle/SegmentBundle/Resources/views/Segment/view.html.twig +++ b/src/Oro/Bundle/SegmentBundle/Resources/views/Segment/view.html.twig @@ -38,7 +38,17 @@ {% block content_data %} {% if gridName is defined and gridName %} {% set renderParams = renderParams|default({})|merge({enableFullScreenLayout: true}) %} - {{ dataGrid.renderGrid(gridName, params|default({}), renderParams) }} + {% set params = params|default({})|merge({ + '_grid_view': { + '_disabled': true + }, + '_tags': { + '_disabled': true + } + }) + %} + + {{ dataGrid.renderGrid(gridName, params, renderParams) }} {% else %}
            From bc8c932aca085c73f493ffd496f8a314c15296f7 Mon Sep 17 00:00:00 2001 From: Dmitry Naydenov Date: Thu, 24 Dec 2015 22:34:59 +0200 Subject: [PATCH 259/471] BAP-9301 Wrong List of BU in the filter on the grid - style fix --- .../public/js/filter/choice-tree-filter.js | 47 ++++++++++--------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/Oro/Bundle/FilterBundle/Resources/public/js/filter/choice-tree-filter.js b/src/Oro/Bundle/FilterBundle/Resources/public/js/filter/choice-tree-filter.js index d271c102568..ef4fa58a90c 100644 --- a/src/Oro/Bundle/FilterBundle/Resources/public/js/filter/choice-tree-filter.js +++ b/src/Oro/Bundle/FilterBundle/Resources/public/js/filter/choice-tree-filter.js @@ -192,7 +192,7 @@ define(function(require) { items = this._getSelectedItems(items); var temp = []; _.each(items, function(value) { - temp .push({ + temp.push({ value: value, children: [] }); @@ -248,25 +248,26 @@ define(function(require) { return rootArray; }, - _convertToTree: function (data) { + _convertToTree: function(data) { var response = [], - idToNodeMap = {}; + idToNodeMap = {}, + element = {}; - _.each(data, function (value) { - var datum = {}; - datum.value = value; - datum.children = []; + _.each(data, function(value) { + element = {}; + element.value = value; + element.children = []; - idToNodeMap[datum.value.id] = datum; + idToNodeMap[element.value.id] = element; - if (!datum.value.owner_id) { - response.push(datum); + if (!element.value.owner_id) { + response.push(element); } else { - var parentNode = idToNodeMap[datum.value.owner_id]; + var parentNode = idToNodeMap[element.value.owner_id]; if (parentNode) { - parentNode.children.push(datum); + parentNode.children.push(element); } else { - response.push(datum); + response.push(element); } } }); @@ -305,12 +306,12 @@ define(function(require) { var id = self.name + '-' + value.value.id; template += '
          • ' + - ''; if (value.children.length > 0) { template += self.getListTemplate(value.children); } @@ -340,12 +341,12 @@ define(function(require) { }, /** - * Set raw value to filter - * - * @param value - * @param skipRefresh - * @return {*} - */ + * Set raw value to filter + * + * @param value + * @param skipRefresh + * @return {*} + */ setValue: function(value, skipRefresh) { if (!tools.isEqualsLoosely(this.value, value)) { var oldValue = this.value; From d0f5a7324da01ebf1bdaadee714fd72c1ecb1063 Mon Sep 17 00:00:00 2001 From: Tom Holland Date: Wed, 23 Dec 2015 13:59:15 -0800 Subject: [PATCH 260/471] Update transition-actions.md Added quotes and a comment on @create_date and @create_datetime actions because without the quotes the value is interpreted as integer and the object creation fails because it requires a string as input. --- .../workflow/workflow-entities/transition-actions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Oro/Bundle/WorkflowBundle/Resources/doc/reference/workflow/workflow-entities/transition-actions.md b/src/Oro/Bundle/WorkflowBundle/Resources/doc/reference/workflow/workflow-entities/transition-actions.md index 6fcca9aaef8..b5617618c42 100644 --- a/src/Oro/Bundle/WorkflowBundle/Resources/doc/reference/workflow/workflow-entities/transition-actions.md +++ b/src/Oro/Bundle/WorkflowBundle/Resources/doc/reference/workflow/workflow-entities/transition-actions.md @@ -434,7 +434,7 @@ OR # optional condition configuration parameters: attribute: $sales_funnel_start_date - date: 2014-04-01 + date: '2014-04-01' # must use quotes because date parameter requires string value ``` Create Date Time @@ -463,7 +463,7 @@ OR # optional condition configuration parameters: attribute: $sales_funnel_start_date - time: 2014-04-01 12:12:00 + time: '2014-04-01 12:12:00' # must use quotes because date parameter requires string value timezone: Europe/Kiev ``` From 09da89b91816294c6f189260ba5fa53fe4daafde Mon Sep 17 00:00:00 2001 From: Tom Holland Date: Wed, 23 Dec 2015 16:26:24 -0800 Subject: [PATCH 261/471] Fixed typo in transition-actions.md --- .../reference/workflow/workflow-entities/transition-actions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Oro/Bundle/WorkflowBundle/Resources/doc/reference/workflow/workflow-entities/transition-actions.md b/src/Oro/Bundle/WorkflowBundle/Resources/doc/reference/workflow/workflow-entities/transition-actions.md index b5617618c42..9c1101667df 100644 --- a/src/Oro/Bundle/WorkflowBundle/Resources/doc/reference/workflow/workflow-entities/transition-actions.md +++ b/src/Oro/Bundle/WorkflowBundle/Resources/doc/reference/workflow/workflow-entities/transition-actions.md @@ -463,7 +463,7 @@ OR # optional condition configuration parameters: attribute: $sales_funnel_start_date - time: '2014-04-01 12:12:00' # must use quotes because date parameter requires string value + time: '2014-04-01 12:12:00' # must use quotes because time parameter requires string value timezone: Europe/Kiev ``` From 84ed17a5a9cf0118fe68705194a08daeea93d541 Mon Sep 17 00:00:00 2001 From: Tom Holland Date: Thu, 24 Dec 2015 11:34:57 -0800 Subject: [PATCH 262/471] Added attribute parameter to call_method action This appears to have been left out not sure if it's on purpose or not but the attribute parameter is used on the call_method action in this file: https://github.com/orocrm/crm/blob/d3d34ac52808bd2778cd4b5e490618ee19c165f8/src/OroCRM/Bundle/MagentoBundle/Resources/config/process.yml When I was reading this doc and debugging my process config, I assumed the omission of the attribute parameter meant that it was not supported in the call_method action. After looking at the magento_customer_export action (and in oro/platform/src/Oro/Bundle/WorkflowBundle/Model/Action/CallMethod.php) , it appears that this is incorrect and that attribute parameter is supported in the call_method action. --- .../workflow/workflow-entities/transition-actions.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Oro/Bundle/WorkflowBundle/Resources/doc/reference/workflow/workflow-entities/transition-actions.md b/src/Oro/Bundle/WorkflowBundle/Resources/doc/reference/workflow/workflow-entities/transition-actions.md index 9c1101667df..0da585c398e 100644 --- a/src/Oro/Bundle/WorkflowBundle/Resources/doc/reference/workflow/workflow-entities/transition-actions.md +++ b/src/Oro/Bundle/WorkflowBundle/Resources/doc/reference/workflow/workflow-entities/transition-actions.md @@ -386,9 +386,11 @@ Call Method **Description:** Triggers call of object method with parameters. **Parameters:** + - attribute - (optional) target path where result of action will be saved - object - path to callee object - - method - name of method to call - - method_parameters - list of parameters that will be passed to method call. + - method - method name of callee object + - method_parameters - (optional) list of parameters that will be passed to method call + **Configuration Example** ``` @@ -396,6 +398,7 @@ Call Method conditions: # optional condition configuration parameters: + attribute: $.leadContactAddAddress object: $lead.contact method: addAddress method_parameters: [$.result.address] @@ -403,6 +406,7 @@ Call Method OR - @call_method: # add Address to Contact + attribute: $.leadContactAddAddress object: $lead.contact method: addAddress method_parameters: [$.result.address] From dddfb30bd0305516bc863e26357e4527114b1c27 Mon Sep 17 00:00:00 2001 From: Tom Holland Date: Thu, 24 Dec 2015 12:37:35 -0800 Subject: [PATCH 263/471] Enforced conventions & added (optional) notes Enforced convention of all parameter descriptions ending in ; Also enforced convention of indicating if a parameter is optional by moving all (optional) statements to directly after the dash following the parameter name. A few other misc convention/grammar changes. --- .../workflow-entities/transition-actions.md | 66 +++++++++---------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/src/Oro/Bundle/WorkflowBundle/Resources/doc/reference/workflow/workflow-entities/transition-actions.md b/src/Oro/Bundle/WorkflowBundle/Resources/doc/reference/workflow/workflow-entities/transition-actions.md index 0da585c398e..d652e3e7eb0 100644 --- a/src/Oro/Bundle/WorkflowBundle/Resources/doc/reference/workflow/workflow-entities/transition-actions.md +++ b/src/Oro/Bundle/WorkflowBundle/Resources/doc/reference/workflow/workflow-entities/transition-actions.md @@ -85,7 +85,7 @@ Assign Value **Parameters:** - attribute / 0 - attribute where value should be set; - - value / 1 - value that should be set. + - value / 1 - value that should be set; **Configuration Example** ``` @@ -167,10 +167,10 @@ Create Object **Description:** Creates object with specified class and data, and sets it as attribute value. **Parameters:** - - class - class name of created object; - - arguments - array of object constructor arguments; - - attribute - attribute that will contain entity instance; - - data - array of data that should be set to entity. + - class - fully qualified class name of object to be created; + - arguments - (optional) array of object constructor arguments; + - attribute - attribute that will contain the created object instance; + - data - (optional) array of data that should be set to object; **Configuration Example** ``` @@ -199,11 +199,11 @@ Create Entity **Description:** Creates entity with specified class and data, and sets it as attribute value. **Parameters:** - - class - fully qualified class name of created entity; - - attribute - attribute that will contain entity instance; - - flush - when flush in DB should be performed. + - class - fully qualified class name of entity to be created; + - attribute - attribute that will contain the created entity instance; + - flush - (optional) when flush in DB should be performed. Immediately after entity creation if ``true`` or later if ``false`` (default value: false); - - data - array of data that should be set to entity. + - data - (optional) array of data that should be set to entity. **Configuration Example** ``` @@ -293,15 +293,15 @@ Find Entity **Alias:** find_entity|request_entity -**Description:** Finds entity by identifier value or "where" condition and saves reference or entity to path. +**Description:** Finds entity by parameter value and saves reference or entity to path. You must define at least one of 3 optional parameters: `identifier`, `where` or `order_by`. **Parameters:** - class - fully qualified class name of requested entity; - attribute - target path where result of action will be saved; - - identifier - value of identifier of entity to find; - - where - array of conditions to find entity, key is field name, value is scalar value or path; - - order_by - array of fields used to sort values, key is field name, value is direction (asc or desc); - - case_insensitive - boolean flag used to find entity using case insensitive search, default value is false. + - identifier - (optional) value of identifier of entity to find; + - where - (optional) array of conditions to find entity, key is field name, value is scalar value or path; + - order_by - (optional) array of fields used to sort values, key is field name, value is direction (asc or desc); + - case_insensitive - (optional) boolean flag used to find entity using case insensitive search, default value is false; **Configuration Example** ``` @@ -386,10 +386,10 @@ Call Method **Description:** Triggers call of object method with parameters. **Parameters:** - - attribute - (optional) target path where result of action will be saved - - object - path to callee object - - method - method name of callee object - - method_parameters - (optional) list of parameters that will be passed to method call + - attribute - (optional) target path where result of action will be saved; + - object - fully qualified class name of object to be referenced; + - method - method name of referenced object to be called; + - method_parameters - (optional) list of parameters that will be passed to method call; **Configuration Example** @@ -481,10 +481,10 @@ Start Workflow **Description:** Triggers start of workflow with configured data. As a result a new WorkflowItem will be produced. **Parameters:** - - name - name of Workflow to start - - attribute - path where result WorkflowItem will be saved - - entity - path to entity that plays role of managed entity in started Workflow (optional) - - transition - name of start transition (optional) + - name - name of Workflow to start; + - attribute - path where result WorkflowItem will be saved; + - entity - (optional) path to entity that plays role of managed entity in started Workflow; + - transition - (optional) name of start transition; **Configuration Example** ``` @@ -517,9 +517,9 @@ Transit Workflow **Description:** Performs transition for workflow on a specific entity. Workflow must be already started. **Parameters:** - - entity (or first parameter) - path to entity used for transition operation - - transition (or second parameter) - name of the transition - - data (or third parameter) - additional data passed to workflow item before transition (optional) + - entity (or first parameter) - path to entity used for transition operation; + - transition (or second parameter) - name of the transition; + - data (or third parameter) - (optional) additional data passed to workflow item before transition; **Configuration Example** ``` @@ -559,9 +559,9 @@ Redirect **Description:** Redirects unset to some route **Parameters:** - - url - URL where user should be redirected - - route - name of the route, if set than url parameter will be ignored - - route_parameters - parameters of route + - url - URL where user should be redirected; + - route - (optional) name of the route, if set than url parameter will be ignored; + - route_parameters - (optional) parameters of route; **Configuration Example** ``` @@ -684,14 +684,14 @@ Flash Message **Alias:** flash_message -**Parameters:** - - message - message itself, will be passed to translator. Required. - - message_parameters - message parameters, that will be passed to translator as second argument. Optional. - - type - message type applicable for Flash Bag. Optional, info by default. - **Description:** Add flash message to session flash bag. Provides ability to show flash messages on frontend. Messages are passed through translator. +**Parameters:** + - message - message itself, will be passed to translator; + - message_parameters - (optional) message parameters, that will be passed to translator as second argument; + - type - (optional) message type applicable for Flash Bag. Set to info by default; + **Configuration Example** ``` @flash_message: From 079fc49dc9ce2664c6f32d65a16929b8616f1cca Mon Sep 17 00:00:00 2001 From: Dmitry Naydenov Date: Fri, 25 Dec 2015 00:43:58 +0200 Subject: [PATCH 264/471] BAP-9301 Wrong List of BU in the filter on the grid - fix logic --- .../Filter/ChoiceTreeBusinessUnitProvider.php | 32 ++++++++----------- .../ChoiceTreeBusinessUnitProviderTest.php | 26 ++++++--------- 2 files changed, 23 insertions(+), 35 deletions(-) diff --git a/src/Oro/Bundle/OrganizationBundle/Provider/Filter/ChoiceTreeBusinessUnitProvider.php b/src/Oro/Bundle/OrganizationBundle/Provider/Filter/ChoiceTreeBusinessUnitProvider.php index ee42169e2ed..e68f05e24ca 100644 --- a/src/Oro/Bundle/OrganizationBundle/Provider/Filter/ChoiceTreeBusinessUnitProvider.php +++ b/src/Oro/Bundle/OrganizationBundle/Provider/Filter/ChoiceTreeBusinessUnitProvider.php @@ -54,10 +54,11 @@ public function getList() $response = []; $qb = $businessUnitRepo->getQueryBuilder(); - - $qb->andWhere( - $qb->expr()->in('businessUnit.id', ':ids') - ); + $qb + ->andWhere( + $qb->expr()->in('businessUnit.id', ':ids') + ) + ->orderBy('businessUnit.id', 'ASC'); $qb->setParameter('ids', $this->getBusinessUnitIds()); @@ -111,25 +112,18 @@ protected function getUser() */ protected function getBusinessUnitIds() { - $user = $this->getUser(); - $organization = $user->getOrganization(); - /** @var OwnerTree $tree */ - $tree = $this->treeProvider->getTree(); + $tree = $this->treeProvider->getTree(); + $user = $this->getUser(); + $result = []; - $userBUIds = $tree->getUserBusinessUnitIds( - $user->getId(), - $organization->getId() - ); + $organizations = $user->getOrganizations(); - $result = []; - foreach ($userBUIds as $businessUnitId) { - $subordinateBUIds = $tree->getSubordinateBusinessUnitIds($businessUnitId); - $result = array_merge($subordinateBUIds, $result); + foreach ($organizations as $organization) { + $subBUIds = $tree->getUserSubordinateBusinessUnitIds($user->getId(), $organization->getId()); + $result = array_merge($result, $subBUIds); } - return array_unique( - array_merge($userBUIds, $result) - ); + return array_unique($result); } } diff --git a/src/Oro/Bundle/OrganizationBundle/Tests/Unit/Provider/Filter/ChoiceTreeBusinessUnitProviderTest.php b/src/Oro/Bundle/OrganizationBundle/Tests/Unit/Provider/Filter/ChoiceTreeBusinessUnitProviderTest.php index 61a0c69bfe8..a8f96c63806 100644 --- a/src/Oro/Bundle/OrganizationBundle/Tests/Unit/Provider/Filter/ChoiceTreeBusinessUnitProviderTest.php +++ b/src/Oro/Bundle/OrganizationBundle/Tests/Unit/Provider/Filter/ChoiceTreeBusinessUnitProviderTest.php @@ -49,7 +49,7 @@ public function setUp() /** * @dataProvider getListDataProvider */ - public function testGetList($userBUIds, $subordinateBUIds, $times, $queryResult, $result) + public function testGetList($userBUIds, $queryResult, $result) { $businessUnitRepos = $this->getMockBuilder('BusinessUnitRepository') ->disableOriginalConstructor() @@ -62,16 +62,16 @@ public function testGetList($userBUIds, $subordinateBUIds, $times, $queryResult, ->getMock(); $treeOwner = $this->getMockBuilder('Oro\Bundle\SecurityBundle\Owner\OwnerTree') - ->setMethods(['getUserBusinessUnitIds', 'getSubordinateBusinessUnitIds']) + ->setMethods(['getUserSubordinateBusinessUnitIds']) ->disableOriginalConstructor() ->getMock(); $this->treeProvider->expects(self::once())->method('getTree')->willReturn($treeOwner); - $treeOwner->expects(self::once())->method('getUserBusinessUnitIds')->willReturn($userBUIds); + $treeOwner - ->expects(self::exactly($times)) - ->method('getSubordinateBusinessUnitIds') - ->willReturn($subordinateBUIds); + ->expects(self::once()) + ->method('getUserSubordinateBusinessUnitIds') + ->willReturn($userBUIds); $this->aclHelper->expects(self::any())->method('apply')->willReturn($qb); $businessUnitRepos->expects(self::any())->method('getQueryBuilder')->willReturn($qb); @@ -88,13 +88,13 @@ public function testGetList($userBUIds, $subordinateBUIds, $times, $queryResult, $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); $user = $this->getMockBuilder('Oro\Bundle\UserBundle\Entity\User') - ->setMethods(['getId', 'getOrganization']) + ->setMethods(['getId', 'getOrganizations']) ->disableOriginalConstructor() ->getMock(); $organization = $this->getMock('Oro\Bundle\OrganizationBundle\Entity\OrganizationInterface'); $organization->expects(self::once())->method('getId')->willReturn(1); - $user->expects(self::once())->method('getOrganization')->willReturn($organization); + $user->expects(self::once())->method('getOrganizations')->willReturn([$organization]); $this->securityFacade->expects(self::once())->method('getToken')->willReturn($tokenStorage); $tokenStorage->expects(self::once())->method('getUser')->willReturn($user); @@ -116,9 +116,7 @@ public function getListDataProvider() { return [ 'Three elements in the list' => [ - 'userBUIds' => [1], - 'subordinateBUIds' => [2, 3], - 'times' => 1, + 'userBUIds' => [1, 2, 3], 'queryResult' => $this->getBusinessUnits('one'), 'result' => [ [ @@ -139,9 +137,7 @@ public function getListDataProvider() ] ], 'Six elements in the list' => [ - 'userBUIds' => [1, 2], - 'subordinateBUIds' => [3, 4, 5, 6], - 'times' => 2, + 'userBUIds' => [1, 2, 3, 4, 5, 6], 'queryResult' => $this->getBusinessUnits('two'), 'result' => [ [ @@ -183,8 +179,6 @@ public function getListDataProvider() ], 'empty list' => [ 'userBUIds' => [], - 'subordinateBUIds' => [], - 'times' => 0, 'queryResult' => [], 'result' => [] ], From d74f7236216fea98d08a8e7182c3bb70c19d8e4c Mon Sep 17 00:00:00 2001 From: Tom Holland Date: Thu, 24 Dec 2015 14:43:23 -0800 Subject: [PATCH 265/471] Remove extra line that was making CI fail --- .../Layout/Block/Extension/ConfigExpressionExtensionTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Block/Extension/ConfigExpressionExtensionTest.php b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Block/Extension/ConfigExpressionExtensionTest.php index 26842db0ed0..4761f937081 100644 --- a/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Block/Extension/ConfigExpressionExtensionTest.php +++ b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Block/Extension/ConfigExpressionExtensionTest.php @@ -110,4 +110,3 @@ protected function getDataAccessorMock() return $this->getMock('Oro\Component\Layout\DataAccessorInterface'); } } - From df56e054987e0d0507d61b4a5c30be0c843747e7 Mon Sep 17 00:00:00 2001 From: Dmitry Naydenov Date: Fri, 25 Dec 2015 01:05:17 +0200 Subject: [PATCH 266/471] BAP-9301 Wrong List of BU in the filter on the grid - js style fix --- .../Resources/public/js/filter/choice-tree-filter.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Oro/Bundle/FilterBundle/Resources/public/js/filter/choice-tree-filter.js b/src/Oro/Bundle/FilterBundle/Resources/public/js/filter/choice-tree-filter.js index ef4fa58a90c..48d95399ca9 100644 --- a/src/Oro/Bundle/FilterBundle/Resources/public/js/filter/choice-tree-filter.js +++ b/src/Oro/Bundle/FilterBundle/Resources/public/js/filter/choice-tree-filter.js @@ -249,9 +249,9 @@ define(function(require) { }, _convertToTree: function(data) { - var response = [], - idToNodeMap = {}, - element = {}; + var response = []; + var idToNodeMap = {}; + var element = {}; _.each(data, function(value) { element = {}; From 4aee038ff716f0cef804894c7a10cf4218320868 Mon Sep 17 00:00:00 2001 From: Ignat Shcheglovskyi Date: Thu, 24 Dec 2015 16:36:17 -0800 Subject: [PATCH 267/471] BAP-9594: Role grid shows organization column incorrectly - fix issue in datagrid "roles-grid" - added acl_resource and set - deprecate constants of datagrid configuration in Builder, move them to DatagridConfiguration - add methods to DatagridConfiguration: getDatasourceType, getAclResource, isDatasourceSkipAclApply - move datagrid option [source][acl_resource] to path [acl_resource] and add backward compatibility - deprecate datagrid option option [option][skip_acl_check], add option [source][skip_acl_apply] instead and add backward compatibility - refactor existing datagrid configurations and client code to use new approach - fix typo in option of datagrid action - "acl_recource" to "acl_resource" - update documentation - update tests --- UPGRADE-1.9.md | 51 ++++++++++++++++ .../Resources/config/datagrid.yml | 22 +++---- .../CronBundle/Resources/config/datagrid.yml | 4 +- .../Resources/config/datagrid.yml | 2 +- .../Resources/config/datagrid.yml | 4 +- .../Controller/GridController.php | 5 +- .../DataGridBundle/Datagrid/Builder.php | 38 ++++++++++-- .../Datagrid/Common/DatagridConfiguration.php | 59 +++++++++++++++++++ .../OrmDatasourceAclListener.php | 3 +- .../GridParams/GridParamsExtension.php | 3 +- .../MassAction/MassActionDispatcher.php | 3 +- .../Extension/Pager/OrmPagerExtension.php | 9 +-- .../Extension/Sorter/OrmSorterExtension.php | 3 +- .../Extension/Totals/OrmTotalsExtension.php | 5 +- .../backend/advanced_grid_configuration.md | 33 ++++++++--- .../Totals/OrmTotalsExtensionTest.php | 9 +-- .../Tests/Unit/Twig/DataGridExtensionTest.php | 20 ++----- .../DataGridBundle/Twig/DataGridExtension.php | 6 +- .../EmailBundle/Resources/config/datagrid.yml | 8 +-- .../Resources/config/datagrid.yml | 2 +- .../Resources/config/datagrid.yml | 4 +- .../Resources/config/datagrid.yml | 8 +-- .../Grid/AbstractFieldsExtension.php | 3 +- .../Datagrid/EntityPaginationExtension.php | 3 +- .../Grid/Extension/OrmFilterExtension.php | 5 +- .../Resources/config/datagrid.yml | 4 +- .../Resources/config/datagrid.yml | 2 +- .../Event/BusinessUnitGridListener.php | 13 ++-- .../Resources/config/datagrid.yml | 7 +-- .../Grid/Extension/OrmDatasourceExtension.php | 3 +- .../Resources/config/datagrid.yml | 2 +- .../Extension/Pager/SearchPagerExtension.php | 3 +- .../Resources/config/datagrid.yml | 4 +- .../TagBundle/Resources/config/datagrid.yml | 4 +- .../Resources/config/datagrid.yml | 4 +- .../EventListener/OwnerUserGridListener.php | 13 ++-- .../UserBundle/Resources/config/datagrid.yml | 19 +++--- .../Resources/config/datagrid.yml | 6 +- 38 files changed, 265 insertions(+), 131 deletions(-) diff --git a/UPGRADE-1.9.md b/UPGRADE-1.9.md index 15368013573..35609e6832d 100644 --- a/UPGRADE-1.9.md +++ b/UPGRADE-1.9.md @@ -34,6 +34,57 @@ UPGRADE FROM 1.8 to 1.9 - `audit-grid` and `audit-history-grid` based on `Oro\Bundle\DataAuditBundle\Entity\AbstractAudit` now. Make join to get your entity on grid ####DataGridBundle +- `Oro\Bundle\DataGridBundle\Datagrid\Builder::DATASOURCE_PATH` marked as deprecated. Use `Oro\Bundle\DataGridBundle\Datagrid\Common\DatagridConfiguration::DATASOURCE_PATH`. +- `Oro\Bundle\DataGridBundle\Datagrid\Builder::DATASOURCE_TYPE_PATH` marked as deprecated. Use `Oro\Bundle\DataGridBundle\Datagrid\Common\DatagridConfiguration::getAclResource`. +- `Oro\Bundle\DataGridBundle\Datagrid\Builder::DATASOURCE_ACL_PATH` marked as deprecated. Use `Oro\Bundle\DataGridBundle\Datagrid\Common\DatagridConfiguration::getAclResource`. +- `Oro\Bundle\DataGridBundle\Datagrid\Builder::BASE_DATAGRID_CLASS_PATH` marked as deprecated. Use `Oro\Bundle\DataGridBundle\Datagrid\Common\DatagridConfiguration::BASE_DATAGRID_CLASS_PATH`. +- `Oro\Bundle\DataGridBundle\Datagrid\Builder::DATASOURCE_SKIP_ACL_CHECK` marked as deprecated. Use `Oro\Bundle\DataGridBundle\Datagrid\Common\DatagridConfiguration::isDatasourceSkipAclApply`. +- `Oro\Bundle\DataGridBundle\Datagrid\Builder::DATASOURCE_SKIP_COUNT_WALKER_PATH` marked as deprecated. Use `Oro\Bundle\DataGridBundle\Datagrid\Common\DatagridConfiguration::DATASOURCE_SKIP_COUNT_WALKER_PATH`. +- Option "acl_resource" moved from option "source" too root node of datagrid configuration: + +Before + +``` +datagrid: + acme-demo-grid: + ... # some configuration + source: + acl_resource: 'acme_demo_entity_view' + ... # some configuration +``` + +Now + +``` +datagrid: + acme-demo-grid: + acl_resource: 'acme_demo_entity_view' + ... # some configuration +``` + +- Option of datagrid "skip_acl_check" is deprecated, use option "skip_acl_apply" instead. Logic of this option was also changed. Before this option caused ignorance of option "acl_resource". Now it is responsible only for indication whether or not ACL should be applied to source query of the grid. See [advanced_grid_configuration.md](.src/Oro/Bundle/DataGridBundle/Resources/doc/backend/advanced_grid_configuration.md) for use cases. + +Before + +``` +datagrid: + acme-demo-grid: + ... # some configuration + options: + skip_acl_check: true +``` + +Now + +``` +datagrid: + acme-demo-grid: + ... # some configuration + source: + skip_acl_apply: true + ... # some configuration +``` + - Services with tag `oro_datagrid.extension.formatter.property` was marked as private - JS collection models format changed to maintain compatibility with Backbone collections: now it is always list of models, and additional parameters are passed through the options - Grid merge uses distinct policy diff --git a/src/Oro/Bundle/CalendarBundle/Resources/config/datagrid.yml b/src/Oro/Bundle/CalendarBundle/Resources/config/datagrid.yml index 5a3738772fc..3e62d83b8c3 100644 --- a/src/Oro/Bundle/CalendarBundle/Resources/config/datagrid.yml +++ b/src/Oro/Bundle/CalendarBundle/Resources/config/datagrid.yml @@ -1,8 +1,8 @@ datagrid: calendar-event-grid: + acl_resource: oro_calendar_event_view source: type: orm - acl_resource: oro_calendar_event_view query: select: - event.id @@ -119,25 +119,25 @@ datagrid: label: Accept link: accept_link icon: ok - acl_recource: oro_calendar_event_view + acl_resource: oro_calendar_event_view tentatively: type: ajax label: Tentative link: tentatively_link icon: question - acl_recource: oro_calendar_event_view + acl_resource: oro_calendar_event_view decline: type: ajax label: Decline link: decline_link icon: remove - acl_recource: oro_calendar_event_view + acl_resource: oro_calendar_event_view view: type: navigate label: View link: view_link icon: eye-open - acl_recource: oro_calendar_event_view + acl_resource: oro_calendar_event_view rowAction: true update: type: navigate @@ -157,9 +157,9 @@ datagrid: entity_pagination: true widget-base-calendar-event-grid: + acl_resource: oro_calendar_event_view source: type: orm - acl_resource: oro_calendar_event_view query: select: - event.id @@ -210,10 +210,10 @@ datagrid: data_name: event.end users-calendar-select-grid-exclude-owner: + acl_resource: oro_user_user_view options: entityHint: calendar source: - acl_resource: oro_user_user_view type: orm query: select: @@ -289,6 +289,7 @@ datagrid: base-system-calendar-event-grid: source: type: orm + skip_acl_apply: true query: select: - event.id @@ -355,7 +356,7 @@ datagrid: label: View link: view_link icon: eye-open - acl_recource: oro_calendar_event_view + acl_resource: oro_calendar_event_view rowAction: true update: type: navigate @@ -371,7 +372,6 @@ datagrid: link: delete_link options: entityHint: calendar_events - skip_acl_check: true entity_pagination: true system-calendar-event-grid: @@ -395,6 +395,7 @@ datagrid: system-calendar-grid: source: type: orm + skip_acl_apply: true query: select: - sc.id @@ -464,5 +465,4 @@ datagrid: icon: trash link: delete_link action_configuration: @oro_calendar.listener.datagrid.system_calendar->getActionConfigurationClosure - options: - skip_acl_check: true + diff --git a/src/Oro/Bundle/CronBundle/Resources/config/datagrid.yml b/src/Oro/Bundle/CronBundle/Resources/config/datagrid.yml index 614db6f52f3..3dc0f4b05b2 100644 --- a/src/Oro/Bundle/CronBundle/Resources/config/datagrid.yml +++ b/src/Oro/Bundle/CronBundle/Resources/config/datagrid.yml @@ -1,8 +1,8 @@ datagrid: jobs-grid: + acl_resource: oro_jobs source: type: orm - acl_resource: oro_jobs query: select: - partial j.{id, command, args, state, runtime, memoryUsageReal, priority, createdAt} @@ -89,7 +89,7 @@ datagrid: label: oro.grid.action.view link: view_link icon: eye-open - acl_recource: oro_jobs + acl_resource: oro_jobs rowAction: true options: entityHint: job diff --git a/src/Oro/Bundle/DashboardBundle/Resources/config/datagrid.yml b/src/Oro/Bundle/DashboardBundle/Resources/config/datagrid.yml index 71e7c80e5bb..3bd2d58292e 100644 --- a/src/Oro/Bundle/DashboardBundle/Resources/config/datagrid.yml +++ b/src/Oro/Bundle/DashboardBundle/Resources/config/datagrid.yml @@ -1,8 +1,8 @@ datagrid: dashboards-grid: + acl_resource: oro_dashboard_view source: type: orm - acl_resource: oro_dashboard_view query: select: - dashboard.id diff --git a/src/Oro/Bundle/DataAuditBundle/Resources/config/datagrid.yml b/src/Oro/Bundle/DataAuditBundle/Resources/config/datagrid.yml index b0730a6904e..7b38ace447b 100644 --- a/src/Oro/Bundle/DataAuditBundle/Resources/config/datagrid.yml +++ b/src/Oro/Bundle/DataAuditBundle/Resources/config/datagrid.yml @@ -1,9 +1,9 @@ datagrid: audit-grid: + acl_resource: oro_dataaudit_history options: entityHint: audit source: - acl_resource: oro_dataaudit_history type: orm query: select: @@ -122,8 +122,8 @@ datagrid: enabled: false audit-history-grid: + acl_resource: oro_dataaudit_history source: - acl_resource: oro_dataaudit_history type: orm query: select: diff --git a/src/Oro/Bundle/DataGridBundle/Controller/GridController.php b/src/Oro/Bundle/DataGridBundle/Controller/GridController.php index 69975475499..15baf3f377a 100644 --- a/src/Oro/Bundle/DataGridBundle/Controller/GridController.php +++ b/src/Oro/Bundle/DataGridBundle/Controller/GridController.php @@ -58,10 +58,9 @@ public function getAction($gridName) { $gridManager = $this->get('oro_datagrid.datagrid.manager'); $gridConfig = $gridManager->getConfigurationForGrid($gridName); - $acl = $gridConfig->offsetGetByPath(Builder::DATASOURCE_ACL_PATH); - $aclSkip = $gridConfig->offsetGetByPath(Builder::DATASOURCE_SKIP_ACL_CHECK, false); + $acl = $gridConfig->getAclResource(); - if (!$aclSkip && $acl && !$this->get('oro_security.security_facade')->isGranted($acl)) { + if ($acl && !$this->get('oro_security.security_facade')->isGranted($acl)) { throw new AccessDeniedException('Access denied.'); } diff --git a/src/Oro/Bundle/DataGridBundle/Datagrid/Builder.php b/src/Oro/Bundle/DataGridBundle/Datagrid/Builder.php index 41c34cb0736..5f207f5bfa7 100644 --- a/src/Oro/Bundle/DataGridBundle/Datagrid/Builder.php +++ b/src/Oro/Bundle/DataGridBundle/Datagrid/Builder.php @@ -15,13 +15,40 @@ class Builder { + /** + * @deprecated Since 1.9, will be removed after 1.11. + * @see DatagridConfiguration::DATASOURCE_PATH + */ const DATASOURCE_PATH = '[source]'; + + /** + * @deprecated Since 1.9, will be removed after 1.11. + * @see DatagridConfiguration::DATASOURCE_TYPE_PATH, DatagridConfiguration::getDatasourceType + */ const DATASOURCE_TYPE_PATH = '[source][type]'; + + /** + * @deprecated Since 1.9, will be removed after 1.11. + * @see DatagridConfiguration::ACL_RESOURCE_PATH, DatagridConfiguration::getAclResource + */ const DATASOURCE_ACL_PATH = '[source][acl_resource]'; + + /** + * @deprecated Since 1.9, will be removed after 1.11. + * @see DatagridConfiguration::BASE_DATAGRID_CLASS_PATH + */ const BASE_DATAGRID_CLASS_PATH = '[options][base_datagrid_class]'; + + /** + * @deprecated Since 1.9, will be removed after 1.11. + * @see DatagridConfiguration::DATASOURCE_SKIP_ACL_APPLY_PATH, DatagridConfiguration::isDatasourceSkipAclApply + */ const DATASOURCE_SKIP_ACL_CHECK = '[options][skip_acl_check]'; - // Use this option as workaround for http://www.doctrine-project.org/jira/browse/DDC-2794 + /** + * @deprecated Since 1.9, will be removed after 1.11. + * @see DatagridConfiguration::DATASOURCE_SKIP_COUNT_WALKER_PATH + */ const DATASOURCE_SKIP_COUNT_WALKER_PATH = '[options][skip_count_walker]'; /** @var string */ @@ -79,7 +106,7 @@ public function build(DatagridConfiguration $config, ParameterBag $parameters) $event = new PreBuild($config, $parameters); $this->eventDispatcher->dispatch(PreBuild::NAME, $event); - $class = $config->offsetGetByPath(self::BASE_DATAGRID_CLASS_PATH, $this->baseDatagridClass); + $class = $config->offsetGetByPath(DatagridConfiguration::BASE_DATAGRID_CLASS_PATH, $this->baseDatagridClass); $name = $config->getName(); /** @var DatagridInterface $datagrid */ @@ -170,7 +197,7 @@ protected function createAcceptor(DatagridConfiguration $config, ParameterBag $p */ protected function buildDataSource(DatagridInterface $grid, DatagridConfiguration $config) { - $sourceType = $config->offsetGetByPath(self::DATASOURCE_TYPE_PATH, false); + $sourceType = $config->offsetGetByPath(DatagridConfiguration::DATASOURCE_TYPE_PATH, false); if (!$sourceType) { throw new RuntimeException('Datagrid source does not configured'); } @@ -179,6 +206,9 @@ protected function buildDataSource(DatagridInterface $grid, DatagridConfiguratio throw new RuntimeException(sprintf('Datagrid source "%s" does not exist', $sourceType)); } - $this->dataSources[$sourceType]->process($grid, $config->offsetGetByPath(self::DATASOURCE_PATH, [])); + $this->dataSources[$sourceType]->process( + $grid, + $config->offsetGetByPath(DatagridConfiguration::DATASOURCE_PATH, []) + ); } } diff --git a/src/Oro/Bundle/DataGridBundle/Datagrid/Common/DatagridConfiguration.php b/src/Oro/Bundle/DataGridBundle/Datagrid/Common/DatagridConfiguration.php index 261ceb16a7d..d21629ee02c 100644 --- a/src/Oro/Bundle/DataGridBundle/Datagrid/Common/DatagridConfiguration.php +++ b/src/Oro/Bundle/DataGridBundle/Datagrid/Common/DatagridConfiguration.php @@ -3,7 +3,66 @@ namespace Oro\Bundle\DataGridBundle\Datagrid\Common; use Oro\Bundle\DataGridBundle\Common\Object; +use Oro\Bundle\DataGridBundle\Datagrid\Builder; class DatagridConfiguration extends Object { + const DATASOURCE_PATH = '[source]'; + const DATASOURCE_TYPE_PATH = '[source][type]'; + const BASE_DATAGRID_CLASS_PATH = '[options][base_datagrid_class]'; + + // Use this option as workaround for http://www.doctrine-project.org/jira/browse/DDC-2794 + const DATASOURCE_SKIP_COUNT_WALKER_PATH = '[options][skip_count_walker]'; + + /** + * This option refers to ACL resource that will be checked before datagrid is loaded. + */ + const ACL_RESOURCE_PATH = '[acl_resource]'; + + /** + * This option makes possible to skip apply of ACL adjustment to source query of datagrid. + */ + const DATASOURCE_SKIP_ACL_APPLY_PATH = '[source][skip_acl_apply]'; + + /** + * @return string + */ + public function getDatasourceType() + { + return $this->offsetGetByPath(self::DATASOURCE_TYPE_PATH); + } + + /** + * Get value of "acl_resource" option from datagrid configuration. + * + * @return string|null + */ + public function getAclResource() + { + $result = $this->offsetGetByPath(self::ACL_RESOURCE_PATH); + + if (!$result) { + // Support backward compatibility until 1.11 to get this option from deprecated path. + $result = $this->offsetGetByPath(Builder::DATASOURCE_ACL_PATH); + } + + return $result; + } + + /** + * Check if ACL apply to source query of datagrid should be skipped + * + * @return bool + */ + public function isDatasourceSkipAclApply() + { + $result = $this->offsetGetByPath(self::DATASOURCE_SKIP_ACL_APPLY_PATH, false); + + if (!$result) { + // Support backward compatibility until 1.11 to get this option from deprecated path. + $result = $this->offsetGetByPath(Builder::DATASOURCE_SKIP_ACL_CHECK, false); + } + + return (bool)$result; + } } diff --git a/src/Oro/Bundle/DataGridBundle/EventListener/OrmDatasourceAclListener.php b/src/Oro/Bundle/DataGridBundle/EventListener/OrmDatasourceAclListener.php index 3fec80548df..5ffc52d86db 100644 --- a/src/Oro/Bundle/DataGridBundle/EventListener/OrmDatasourceAclListener.php +++ b/src/Oro/Bundle/DataGridBundle/EventListener/OrmDatasourceAclListener.php @@ -2,7 +2,6 @@ namespace Oro\Bundle\DataGridBundle\EventListener; -use Oro\Bundle\DataGridBundle\Datagrid\Builder; use Oro\Bundle\DataGridBundle\Event\OrmResultBefore; use Oro\Bundle\SecurityBundle\ORM\Walker\AclHelper; @@ -26,7 +25,7 @@ public function __construct(AclHelper $aclHelper) public function onResultBefore(OrmResultBefore $event) { $config = $event->getDatagrid()->getConfig(); - if (!$config->offsetGetByPath(Builder::DATASOURCE_SKIP_ACL_CHECK, false)) { + if (!$config->isDatasourceSkipAclApply()) { $this->aclHelper->apply($event->getQuery()); } } diff --git a/src/Oro/Bundle/DataGridBundle/Extension/GridParams/GridParamsExtension.php b/src/Oro/Bundle/DataGridBundle/Extension/GridParams/GridParamsExtension.php index 9c841347635..69ff37d3994 100644 --- a/src/Oro/Bundle/DataGridBundle/Extension/GridParams/GridParamsExtension.php +++ b/src/Oro/Bundle/DataGridBundle/Extension/GridParams/GridParamsExtension.php @@ -2,7 +2,6 @@ namespace Oro\Bundle\DataGridBundle\Extension\GridParams; -use Oro\Bundle\DataGridBundle\Datagrid\Builder; use Oro\Bundle\DataGridBundle\Datasource\Orm\OrmDatasource; use Oro\Bundle\DataGridBundle\Extension\AbstractExtension; use Oro\Bundle\DataGridBundle\Datagrid\Common\MetadataObject; @@ -18,7 +17,7 @@ class GridParamsExtension extends AbstractExtension */ public function isApplicable(DatagridConfiguration $config) { - return $config->offsetGetByPath(Builder::DATASOURCE_TYPE_PATH) == OrmDatasource::TYPE; + return $config->getDatasourceType() == OrmDatasource::TYPE; } /** diff --git a/src/Oro/Bundle/DataGridBundle/Extension/MassAction/MassActionDispatcher.php b/src/Oro/Bundle/DataGridBundle/Extension/MassAction/MassActionDispatcher.php index b07c2254204..922deb9950f 100644 --- a/src/Oro/Bundle/DataGridBundle/Extension/MassAction/MassActionDispatcher.php +++ b/src/Oro/Bundle/DataGridBundle/Extension/MassAction/MassActionDispatcher.php @@ -9,7 +9,6 @@ use Symfony\Component\HttpFoundation\File\Exception\UnexpectedTypeException; use Symfony\Component\HttpFoundation\Request; -use Oro\Bundle\DataGridBundle\Datagrid\Builder; use Oro\Bundle\DataGridBundle\Datagrid\Manager; use Oro\Bundle\DataGridBundle\Datagrid\DatagridInterface; use Oro\Bundle\DataGridBundle\Datasource\Orm\IterableResult; @@ -110,7 +109,7 @@ public function dispatch($datagridName, $actionName, array $parameters, array $d //prepare query builder $qb->setMaxResults(null); - if (!$datagrid->getConfig()->offsetGetByPath(Builder::DATASOURCE_SKIP_ACL_CHECK, false)) { + if (!$datagrid->getConfig()->isDatasourceSkipAclApply()) { $qb = $this->aclHelper->apply($qb); } diff --git a/src/Oro/Bundle/DataGridBundle/Extension/Pager/OrmPagerExtension.php b/src/Oro/Bundle/DataGridBundle/Extension/Pager/OrmPagerExtension.php index b3cb16fc04b..6e551f0426d 100644 --- a/src/Oro/Bundle/DataGridBundle/Extension/Pager/OrmPagerExtension.php +++ b/src/Oro/Bundle/DataGridBundle/Extension/Pager/OrmPagerExtension.php @@ -2,7 +2,6 @@ namespace Oro\Bundle\DataGridBundle\Extension\Pager; -use Oro\Bundle\DataGridBundle\Datagrid\Builder; use Oro\Bundle\DataGridBundle\Datagrid\Common\MetadataObject; use Oro\Bundle\DataGridBundle\Datagrid\Common\ResultsObject; use Oro\Bundle\DataGridBundle\Datagrid\Common\DatagridConfiguration; @@ -50,7 +49,7 @@ public function isApplicable(DatagridConfiguration $config) $disabled = $this->getOr(PagerInterface::DISABLED_PARAM, false) || $config->offsetGetByPath(ToolbarExtension::TOOLBAR_PAGINATION_HIDE_OPTION_PATH, false); - return !$disabled && $config->offsetGetByPath(Builder::DATASOURCE_TYPE_PATH) == OrmDatasource::TYPE; + return !$disabled && $config->getDatasourceType() == OrmDatasource::TYPE; } /** @@ -62,11 +61,9 @@ public function visitDatasource(DatagridConfiguration $config, DatasourceInterfa if ($datasource instanceof OrmDatasource) { $this->pager->setQueryBuilder($datasource->getQueryBuilder()); - $this->pager->setSkipAclCheck( - $config->offsetGetByPath(Builder::DATASOURCE_SKIP_ACL_CHECK, false) - ); + $this->pager->setSkipAclCheck($config->isDatasourceSkipAclApply()); $this->pager->setSkipCountWalker( - $config->offsetGetByPath(Builder::DATASOURCE_SKIP_COUNT_WALKER_PATH) + $config->offsetGetByPath(DatagridConfiguration::DATASOURCE_SKIP_COUNT_WALKER_PATH) ); } diff --git a/src/Oro/Bundle/DataGridBundle/Extension/Sorter/OrmSorterExtension.php b/src/Oro/Bundle/DataGridBundle/Extension/Sorter/OrmSorterExtension.php index 4a08cd7ec9e..3580d888c56 100644 --- a/src/Oro/Bundle/DataGridBundle/Extension/Sorter/OrmSorterExtension.php +++ b/src/Oro/Bundle/DataGridBundle/Extension/Sorter/OrmSorterExtension.php @@ -2,7 +2,6 @@ namespace Oro\Bundle\DataGridBundle\Extension\Sorter; -use Oro\Bundle\DataGridBundle\Datagrid\Builder; use Oro\Bundle\DataGridBundle\Datagrid\Common\DatagridConfiguration; use Oro\Bundle\DataGridBundle\Datagrid\Common\MetadataObject; use Oro\Bundle\DataGridBundle\Datagrid\ParameterBag; @@ -36,7 +35,7 @@ class OrmSorterExtension extends AbstractExtension public function isApplicable(DatagridConfiguration $config) { $columns = $config->offsetGetByPath(Configuration::COLUMNS_PATH); - $isApplicable = $config->offsetGetByPath(Builder::DATASOURCE_TYPE_PATH) === OrmDatasource::TYPE + $isApplicable = $config->getDatasourceType() === OrmDatasource::TYPE && is_array($columns); return $isApplicable; diff --git a/src/Oro/Bundle/DataGridBundle/Extension/Totals/OrmTotalsExtension.php b/src/Oro/Bundle/DataGridBundle/Extension/Totals/OrmTotalsExtension.php index d139d5fb719..76e66d1b6bf 100644 --- a/src/Oro/Bundle/DataGridBundle/Extension/Totals/OrmTotalsExtension.php +++ b/src/Oro/Bundle/DataGridBundle/Extension/Totals/OrmTotalsExtension.php @@ -8,7 +8,6 @@ use Symfony\Component\Translation\TranslatorInterface; -use Oro\Bundle\DataGridBundle\Datagrid\Builder; use Oro\Bundle\DataGridBundle\Datagrid\Common\DatagridConfiguration; use Oro\Bundle\DataGridBundle\Datagrid\Common\MetadataObject; use Oro\Bundle\DataGridBundle\Datagrid\Common\ResultsObject; @@ -70,7 +69,7 @@ public function __construct( */ public function isApplicable(DatagridConfiguration $config) { - return $config->offsetGetByPath(Builder::DATASOURCE_TYPE_PATH) === OrmDatasource::TYPE; + return $config->getDatasourceType() === OrmDatasource::TYPE; } /** @@ -123,7 +122,7 @@ public function visitResult(DatagridConfiguration $config, ResultsObject $result $result, $rowConfig['columns'], $rowConfig[Configuration::TOTALS_PER_PAGE_ROW_KEY], - $config->offsetGetByPath(Builder::DATASOURCE_SKIP_ACL_CHECK, false) + $config->isDatasourceSkipAclApply() ) ); } diff --git a/src/Oro/Bundle/DataGridBundle/Resources/doc/backend/advanced_grid_configuration.md b/src/Oro/Bundle/DataGridBundle/Resources/doc/backend/advanced_grid_configuration.md index 958c8f36cab..fa02c01182e 100644 --- a/src/Oro/Bundle/DataGridBundle/Resources/doc/backend/advanced_grid_configuration.md +++ b/src/Oro/Bundle/DataGridBundle/Resources/doc/backend/advanced_grid_configuration.md @@ -238,28 +238,32 @@ datagrid: #### Problem: *I'm developing grid that should not be under ACL control* #### Solution: -- set option 'skip_acl_check' to TRUE +- set option 'skip_acl_apply' to TRUE Example: ``` yml datagrid: acme-demo-grid: ... # some configuration - options: - skip_acl_check: true + source: + skip_acl_apply: true + ... # some configuration of source ``` #### Problem: -*I want to implement some custom security verification/logic without any default acl, even if some "acl_resource" have been defined * +*I want to implement some custom security verification/logic without any default ACL, even if some "acl_resource" have been defined * *e.g. i'm extending some existing grid but with custom acl logic* #### Solution: -- configure grid (set option 'skip_acl_check' to TRUE) +- configure grid (set option 'skip_acl_apply' to TRUE) +- override option 'acl_resource' and to make it false ``` yml datagrid: acme-demo-grid: ... # some configuration - options: - skip_acl_check: true + acl_resource: false + source: + skip_acl_apply: true + ... # some configuration of source ``` - declare own grid listener ``` @@ -273,3 +277,18 @@ my_bundle.event_listener.my_grid_listener: - as an example see: - Oro/Bundle/UserBundle/Resources/config/datagrid.yml (owner-users-select-grid) - Oro/Bundle/UserBundle/EventListener/OwnerUserGridListener.php (service name: "oro_user.event_listener.owner_user_grid_listener") + +#### Problem: +*I want to have a grid secured by ACL resource but skip applying of ACL to DQL query of the grid.* +#### Solution: +- configure grid with option 'skip_acl_apply' set to TRUE, it will ignore applying of ACL to source query of the grid +- configure grid with option 'acl_resource' set to name of some ACL resource, it will check permission to this ACL resouce before datagrid data will be loaded +``` yml +datagrid: + acme-demo-grid: + ... # some configuration + acl_resource: 'acme_demo_entity_view' + source: + skip_acl_apply: true + +``` diff --git a/src/Oro/Bundle/DataGridBundle/Tests/Unit/Extension/Totals/OrmTotalsExtensionTest.php b/src/Oro/Bundle/DataGridBundle/Tests/Unit/Extension/Totals/OrmTotalsExtensionTest.php index 0b1efe0c112..5e435b346df 100644 --- a/src/Oro/Bundle/DataGridBundle/Tests/Unit/Extension/Totals/OrmTotalsExtensionTest.php +++ b/src/Oro/Bundle/DataGridBundle/Tests/Unit/Extension/Totals/OrmTotalsExtensionTest.php @@ -2,19 +2,12 @@ namespace Oro\Bundle\DataGridBundle\Tests\Unit\Extension\Totals; -use Doctrine\Common\Annotations\AnnotationReader; -use Doctrine\ORM\Mapping\Driver\AnnotationDriver; -use Doctrine\ORM\QueryBuilder; - -use Oro\Bundle\DataGridBundle\Datagrid\Builder; use Oro\Bundle\DataGridBundle\Datagrid\Common\DatagridConfiguration; use Oro\Bundle\DataGridBundle\Datagrid\Common\MetadataObject; use Oro\Bundle\DataGridBundle\Datagrid\Common\ResultsObject; -use Oro\Bundle\DataGridBundle\Datasource\Orm\OrmDatasource; use Oro\Bundle\DataGridBundle\Extension\Totals\OrmTotalsExtension; use Oro\Bundle\DataGridBundle\Extension\Totals\Configuration; -use Oro\Bundle\TestFrameworkBundle\Test\Doctrine\ORM\Mocks\EntityManagerMock; use Oro\Bundle\TestFrameworkBundle\Test\Doctrine\ORM\OrmTestCase; class OrmTotalsExtensionTest extends OrmTestCase @@ -77,7 +70,7 @@ protected function setUp() public function testIsApplicable() { $this->assertTrue($this->extension->isApplicable($this->config)); - $this->config->offsetSetByPath(Builder::DATASOURCE_TYPE_PATH, 'non_orm'); + $this->config->offsetSetByPath(DatagridConfiguration::DATASOURCE_TYPE_PATH, 'non_orm'); $this->assertFalse($this->extension->isApplicable($this->config)); } diff --git a/src/Oro/Bundle/DataGridBundle/Tests/Unit/Twig/DataGridExtensionTest.php b/src/Oro/Bundle/DataGridBundle/Tests/Unit/Twig/DataGridExtensionTest.php index 5962ffcadd6..a7fc3528cd9 100644 --- a/src/Oro/Bundle/DataGridBundle/Tests/Unit/Twig/DataGridExtensionTest.php +++ b/src/Oro/Bundle/DataGridBundle/Tests/Unit/Twig/DataGridExtensionTest.php @@ -5,7 +5,6 @@ use Oro\Bundle\DataGridBundle\Datagrid\DatagridInterface; use Symfony\Component\Routing\RouterInterface; -use Oro\Bundle\DataGridBundle\Datagrid\Builder; use Oro\Bundle\DataGridBundle\Datagrid\ManagerInterface; use Oro\Bundle\DataGridBundle\Datagrid\NameStrategyInterface; use Oro\Bundle\DataGridBundle\Twig\DataGridExtension; @@ -85,15 +84,11 @@ public function testGetGridWorks() $configuration = $this->getMockBuilder('Oro\\Bundle\\DataGridBundle\\Datagrid\\Common\\DatagridConfiguration') ->disableOriginalConstructor() + ->setMethods(['getAclResource']) ->getMock(); - $configuration->expects($this->at(0)) - ->method('offsetGetByPath') - ->with(Builder::DATASOURCE_ACL_PATH) - ->will($this->returnValue(null)); - $configuration->expects($this->at(1)) - ->method('offsetGetByPath') - ->with(Builder::DATASOURCE_SKIP_ACL_CHECK) + $configuration->expects($this->once()) + ->method('getAclResource') ->will($this->returnValue(null)); $this->manager->expects($this->once()) @@ -131,14 +126,9 @@ public function testGetGridReturnsNullWhenDontHavePermissions() ->disableOriginalConstructor() ->getMock(); - $configuration->expects($this->at(0)) - ->method('offsetGetByPath') - ->with(Builder::DATASOURCE_ACL_PATH) + $configuration->expects($this->once()) + ->method('getAclResource') ->will($this->returnValue($acl)); - $configuration->expects($this->at(1)) - ->method('offsetGetByPath') - ->with(Builder::DATASOURCE_SKIP_ACL_CHECK) - ->will($this->returnValue(false)); $this->manager->expects($this->once()) ->method('getConfigurationForGrid') diff --git a/src/Oro/Bundle/DataGridBundle/Twig/DataGridExtension.php b/src/Oro/Bundle/DataGridBundle/Twig/DataGridExtension.php index 8bef690d211..5b236cca61a 100644 --- a/src/Oro/Bundle/DataGridBundle/Twig/DataGridExtension.php +++ b/src/Oro/Bundle/DataGridBundle/Twig/DataGridExtension.php @@ -4,7 +4,6 @@ use Symfony\Component\Routing\RouterInterface; -use Oro\Bundle\DataGridBundle\Datagrid\Builder; use Oro\Bundle\DataGridBundle\Datagrid\DatagridInterface; use Oro\Bundle\DataGridBundle\Datagrid\ManagerInterface; use Oro\Bundle\DataGridBundle\Datagrid\NameStrategyInterface; @@ -183,9 +182,8 @@ protected function isAclGrantedForGridName($gridName) $gridConfig = $this->manager->getConfigurationForGrid($gridName); if ($gridConfig) { - $acl = $gridConfig->offsetGetByPath(Builder::DATASOURCE_ACL_PATH); - $aclSKip = $gridConfig->offsetGetByPath(Builder::DATASOURCE_SKIP_ACL_CHECK, false); - if (!$aclSKip && $acl && !$this->securityFacade->isGranted($acl)) { + $aclResource = $gridConfig->getAclResource(); + if ($aclResource && !$this->securityFacade->isGranted($aclResource)) { return false; } else { return true; diff --git a/src/Oro/Bundle/EmailBundle/Resources/config/datagrid.yml b/src/Oro/Bundle/EmailBundle/Resources/config/datagrid.yml index 0b3a8d95a29..d8547ed20ec 100644 --- a/src/Oro/Bundle/EmailBundle/Resources/config/datagrid.yml +++ b/src/Oro/Bundle/EmailBundle/Resources/config/datagrid.yml @@ -1,8 +1,8 @@ datagrid: email-auto-response-rules: + acl_resource: oro_email_autoresponserule_view source: type: orm - acl_resource: oro_email_autoresponserule_view query: select: - r.id @@ -85,6 +85,7 @@ datagrid: base-email-grid: source: type: orm + skip_acl_apply: true query: select: - partial eu.{id, email} @@ -159,7 +160,6 @@ datagrid: default: { sentAt: %oro_datagrid.extension.orm_sorter.class%::DIRECTION_DESC } options: entityHint: email - skip_acl_check: true simplified-email-grid: extends: base-email-grid @@ -412,9 +412,9 @@ datagrid: receivedAt: %oro_datagrid.extension.orm_sorter.class%::DIRECTION_DESC email-templates: + acl_resource: oro_email_emailtemplate_index source: type: orm - acl_resource: oro_email_emailtemplate_index query: select: - t.id @@ -520,6 +520,7 @@ datagrid: base-mailboxes-grid: source: type: orm + skip_acl_apply: true query: select: - m @@ -564,7 +565,6 @@ datagrid: params: id: id options: - skip_acl_check: true toolbarOptions: hide: true actions: diff --git a/src/Oro/Bundle/EmbeddedFormBundle/Resources/config/datagrid.yml b/src/Oro/Bundle/EmbeddedFormBundle/Resources/config/datagrid.yml index 4f82b651f62..e4cd22fddd1 100644 --- a/src/Oro/Bundle/EmbeddedFormBundle/Resources/config/datagrid.yml +++ b/src/Oro/Bundle/EmbeddedFormBundle/Resources/config/datagrid.yml @@ -1,7 +1,7 @@ datagrid: embedded-forms-grid: + acl_resource: oro_embedded_form_view source: - acl_resource: oro_embedded_form_view type: orm query: select: diff --git a/src/Oro/Bundle/EntityBundle/Resources/config/datagrid.yml b/src/Oro/Bundle/EntityBundle/Resources/config/datagrid.yml index 2006ed432bf..b56c95230f5 100644 --- a/src/Oro/Bundle/EntityBundle/Resources/config/datagrid.yml +++ b/src/Oro/Bundle/EntityBundle/Resources/config/datagrid.yml @@ -1,12 +1,12 @@ datagrid: custom-entity-grid: + acl_resource: ~ options: base_datagrid_class: Oro\Bundle\EntityBundle\Grid\CustomEntityDatagrid entityHint: entity export: true entity_pagination: true source: - acl_resource: ~ type: orm query: select: @@ -61,6 +61,7 @@ datagrid: columns: [] entity-relation-grid: + acl_resource: ~ options: entityHint: entity routerEnabled: false @@ -71,7 +72,6 @@ datagrid: included: '#appendRelation' excluded: '#removeRelation' source: - acl_resource: ~ type: orm columns: assigned: diff --git a/src/Oro/Bundle/EntityConfigBundle/Resources/config/datagrid.yml b/src/Oro/Bundle/EntityConfigBundle/Resources/config/datagrid.yml index 9a7a2fa89d7..c23a3d59b07 100644 --- a/src/Oro/Bundle/EntityConfigBundle/Resources/config/datagrid.yml +++ b/src/Oro/Bundle/EntityConfigBundle/Resources/config/datagrid.yml @@ -6,8 +6,8 @@ datagrid: toolbarOptions: pageSize: default_per_page: 100 + acl_resource: oro_entityconfig_manage source: - acl_resource: oro_entityconfig_manage type: orm query: select: @@ -69,9 +69,9 @@ datagrid: toolbarOptions: pageSize: default_per_page: 50 + # TODO: check oro_entityconfig_view acl (403) right now + acl_resource: oro_entityconfig_manage source: - # TODO: check oro_entityconfig_view acl (403) right now - acl_resource: oro_entityconfig_manage type: orm query: select: @@ -126,8 +126,8 @@ datagrid: entity-audit-grid: options: entityHint: history + acl_resource: oro_entityconfig_manage source: - acl_resource: oro_entityconfig_manage type: orm query: select: diff --git a/src/Oro/Bundle/EntityExtendBundle/Grid/AbstractFieldsExtension.php b/src/Oro/Bundle/EntityExtendBundle/Grid/AbstractFieldsExtension.php index d53ddf34a7a..070ccca45db 100644 --- a/src/Oro/Bundle/EntityExtendBundle/Grid/AbstractFieldsExtension.php +++ b/src/Oro/Bundle/EntityExtendBundle/Grid/AbstractFieldsExtension.php @@ -5,7 +5,6 @@ use Doctrine\ORM\Query\Expr\From; use Doctrine\ORM\QueryBuilder; -use Oro\Bundle\DataGridBundle\Datagrid\Builder; use Oro\Bundle\DataGridBundle\Datagrid\DatagridGuesser; use Oro\Bundle\DataGridBundle\Datagrid\Common\DatagridConfiguration; use Oro\Bundle\DataGridBundle\Datasource\DatasourceInterface; @@ -51,7 +50,7 @@ public function __construct( */ public function isApplicable(DatagridConfiguration $config) { - return $config->offsetGetByPath(Builder::DATASOURCE_TYPE_PATH) == OrmDatasource::TYPE; + return $config->getDatasourceType() == OrmDatasource::TYPE; } /** diff --git a/src/Oro/Bundle/EntityPaginationBundle/Datagrid/EntityPaginationExtension.php b/src/Oro/Bundle/EntityPaginationBundle/Datagrid/EntityPaginationExtension.php index 075a86b3371..f724b4ca574 100644 --- a/src/Oro/Bundle/EntityPaginationBundle/Datagrid/EntityPaginationExtension.php +++ b/src/Oro/Bundle/EntityPaginationBundle/Datagrid/EntityPaginationExtension.php @@ -2,7 +2,6 @@ namespace Oro\Bundle\EntityPaginationBundle\Datagrid; -use Oro\Bundle\DataGridBundle\Datagrid\Builder; use Oro\Bundle\DataGridBundle\Extension\AbstractExtension; use Oro\Bundle\DataGridBundle\Datasource\Orm\OrmDatasource; use Oro\Bundle\DataGridBundle\Datagrid\Common\DatagridConfiguration; @@ -22,7 +21,7 @@ public function isApplicable(DatagridConfiguration $config) return false; } - return $config->offsetGetByPath(Builder::DATASOURCE_TYPE_PATH) == OrmDatasource::TYPE; + return $config->getDatasourceType() == OrmDatasource::TYPE; } /** diff --git a/src/Oro/Bundle/FilterBundle/Grid/Extension/OrmFilterExtension.php b/src/Oro/Bundle/FilterBundle/Grid/Extension/OrmFilterExtension.php index d341e3872a1..f4fb11ba76c 100644 --- a/src/Oro/Bundle/FilterBundle/Grid/Extension/OrmFilterExtension.php +++ b/src/Oro/Bundle/FilterBundle/Grid/Extension/OrmFilterExtension.php @@ -2,16 +2,15 @@ namespace Oro\Bundle\FilterBundle\Grid\Extension; -use Oro\Bundle\DataGridBundle\Extension\Formatter\Property\PropertyInterface; use Symfony\Component\Translation\TranslatorInterface; -use Oro\Bundle\DataGridBundle\Datagrid\Builder; use Oro\Bundle\DataGridBundle\Datagrid\Common\MetadataObject; use Oro\Bundle\DataGridBundle\Datagrid\Common\DatagridConfiguration; use Oro\Bundle\DataGridBundle\Datasource\DatasourceInterface; use Oro\Bundle\DataGridBundle\Datasource\Orm\OrmDatasource; use Oro\Bundle\DataGridBundle\Extension\AbstractExtension; use Oro\Bundle\DataGridBundle\Extension\Formatter\Configuration as FormatterConfiguration; +use Oro\Bundle\DataGridBundle\Extension\Formatter\Property\PropertyInterface; use Oro\Bundle\FilterBundle\Filter\FilterUtility; use Oro\Bundle\FilterBundle\Filter\FilterInterface; use Oro\Bundle\FilterBundle\Datasource\Orm\OrmFilterDatasourceAdapter; @@ -52,7 +51,7 @@ public function isApplicable(DatagridConfiguration $config) return false; } - return $config->offsetGetByPath(Builder::DATASOURCE_TYPE_PATH) == OrmDatasource::TYPE; + return $config->getDatasourceType() == OrmDatasource::TYPE; } /** diff --git a/src/Oro/Bundle/IntegrationBundle/Resources/config/datagrid.yml b/src/Oro/Bundle/IntegrationBundle/Resources/config/datagrid.yml index 16a77251609..da4176cf24a 100644 --- a/src/Oro/Bundle/IntegrationBundle/Resources/config/datagrid.yml +++ b/src/Oro/Bundle/IntegrationBundle/Resources/config/datagrid.yml @@ -1,8 +1,8 @@ datagrid: oro-integration-grid: + acl_resource: oro_integration_view source: type: orm - acl_resource: oro_integration_view query: select: - c.id @@ -97,9 +97,9 @@ datagrid: entityHint: channel entity_pagination: true oro-integration-status-grid: + acl_resource: oro_integration_view source: type: orm - acl_resource: oro_integration_view query: select: - s.code diff --git a/src/Oro/Bundle/NotificationBundle/Resources/config/datagrid.yml b/src/Oro/Bundle/NotificationBundle/Resources/config/datagrid.yml index c13647beea4..9cdd78ad18a 100644 --- a/src/Oro/Bundle/NotificationBundle/Resources/config/datagrid.yml +++ b/src/Oro/Bundle/NotificationBundle/Resources/config/datagrid.yml @@ -3,8 +3,8 @@ datagrid: options: entityHint: transactional email entity_pagination: true + acl_resource: oro_notification_emailnotification_view source: - acl_resource: oro_notification_emailnotification_view type: orm query: select: diff --git a/src/Oro/Bundle/OrganizationBundle/Event/BusinessUnitGridListener.php b/src/Oro/Bundle/OrganizationBundle/Event/BusinessUnitGridListener.php index d6e64cd0ba7..c51855e6971 100644 --- a/src/Oro/Bundle/OrganizationBundle/Event/BusinessUnitGridListener.php +++ b/src/Oro/Bundle/OrganizationBundle/Event/BusinessUnitGridListener.php @@ -72,10 +72,15 @@ public function onBuildBefore(BuildBefore $event) $organization->getId() ); } - $where = array_merge( - $where, - ['u.id in (' . implode(', ', $resultBuIds) . ')'] - ); + if ($resultBuIds) { + $where = array_merge( + $where, + ['u.id in (' . implode(', ', $resultBuIds) . ')'] + ); + } else { + // There are no records to show, make query to return empty result + $where = array_merge($where, ['1 = 0']); + } } if (count($where)) { $config->offsetSetByPath('[source][query][where][and]', $where); diff --git a/src/Oro/Bundle/OrganizationBundle/Resources/config/datagrid.yml b/src/Oro/Bundle/OrganizationBundle/Resources/config/datagrid.yml index 59e48e6050f..13787f87a41 100644 --- a/src/Oro/Bundle/OrganizationBundle/Resources/config/datagrid.yml +++ b/src/Oro/Bundle/OrganizationBundle/Resources/config/datagrid.yml @@ -3,11 +3,10 @@ datagrid: extended_entity_name: %oro_organization.business_unit.entity.class% options: entityHint: business unit - skip_acl_check: true entity_pagination: true source: - acl_resource: oro_business_unit_view type: orm + skip_acl_apply: true query: select: - u.id @@ -117,8 +116,8 @@ datagrid: bu-update-users-grid: extends: user-relation-grid + acl_resource: oro_business_unit_update source: - acl_resource: oro_business_unit_update query: select: - > @@ -198,8 +197,8 @@ datagrid: extends: user-relation-grid options: entityHint: user + acl_resource: oro_business_unit_view source: - acl_resource: oro_business_unit_view query: where: and: diff --git a/src/Oro/Bundle/QueryDesignerBundle/Grid/Extension/OrmDatasourceExtension.php b/src/Oro/Bundle/QueryDesignerBundle/Grid/Extension/OrmDatasourceExtension.php index a6b549f60f7..e325deceffb 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Grid/Extension/OrmDatasourceExtension.php +++ b/src/Oro/Bundle/QueryDesignerBundle/Grid/Extension/OrmDatasourceExtension.php @@ -4,7 +4,6 @@ use Doctrine\ORM\QueryBuilder; -use Oro\Bundle\DataGridBundle\Datagrid\Builder; use Oro\Bundle\DataGridBundle\Datagrid\ParameterBag; use Oro\Bundle\DataGridBundle\Extension\AbstractExtension; use Oro\Bundle\DataGridBundle\Datasource\Orm\OrmDatasource; @@ -38,7 +37,7 @@ public function __construct(RestrictionBuilderInterface $restrictionBuilder) */ public function isApplicable(DatagridConfiguration $config) { - return $config->offsetGetByPath(Builder::DATASOURCE_TYPE_PATH) == OrmDatasource::TYPE + return $config->getDatasourceType() == OrmDatasource::TYPE && $config->offsetGetByPath('[source][query_config][filters]'); } diff --git a/src/Oro/Bundle/ReportBundle/Resources/config/datagrid.yml b/src/Oro/Bundle/ReportBundle/Resources/config/datagrid.yml index 48db774d122..ecd26b22792 100644 --- a/src/Oro/Bundle/ReportBundle/Resources/config/datagrid.yml +++ b/src/Oro/Bundle/ReportBundle/Resources/config/datagrid.yml @@ -1,8 +1,8 @@ datagrid: reports-grid: + acl_resource: oro_report_view source: type: orm - acl_resource: oro_report_view query: select: - r.id diff --git a/src/Oro/Bundle/SearchBundle/Extension/Pager/SearchPagerExtension.php b/src/Oro/Bundle/SearchBundle/Extension/Pager/SearchPagerExtension.php index e49a3baa935..b649aa7b44e 100644 --- a/src/Oro/Bundle/SearchBundle/Extension/Pager/SearchPagerExtension.php +++ b/src/Oro/Bundle/SearchBundle/Extension/Pager/SearchPagerExtension.php @@ -2,7 +2,6 @@ namespace Oro\Bundle\SearchBundle\Extension\Pager; -use Oro\Bundle\DataGridBundle\Datagrid\Builder; use Oro\Bundle\DataGridBundle\Datagrid\Common\DatagridConfiguration; use Oro\Bundle\DataGridBundle\Datasource\DatasourceInterface; use Oro\Bundle\DataGridBundle\Extension\Pager\PagerInterface; @@ -29,7 +28,7 @@ public function __construct(IndexerPager $pager) public function isApplicable(DatagridConfiguration $config) { // enabled by default for search datasource - return $config->offsetGetByPath(Builder::DATASOURCE_TYPE_PATH) == SearchDatasource::TYPE; + return $config->getDatasourceType() == SearchDatasource::TYPE; } /** diff --git a/src/Oro/Bundle/SegmentBundle/Resources/config/datagrid.yml b/src/Oro/Bundle/SegmentBundle/Resources/config/datagrid.yml index c65c31635b2..9b46ee6cdb6 100644 --- a/src/Oro/Bundle/SegmentBundle/Resources/config/datagrid.yml +++ b/src/Oro/Bundle/SegmentBundle/Resources/config/datagrid.yml @@ -1,8 +1,8 @@ datagrid: oro_segments-grid: + acl_resource: oro_segment_view source: - type: orm - acl_resource: oro_segment_view + type: orm query: select: - s.id diff --git a/src/Oro/Bundle/TagBundle/Resources/config/datagrid.yml b/src/Oro/Bundle/TagBundle/Resources/config/datagrid.yml index cdbc22ab1be..63a7a8944f2 100644 --- a/src/Oro/Bundle/TagBundle/Resources/config/datagrid.yml +++ b/src/Oro/Bundle/TagBundle/Resources/config/datagrid.yml @@ -3,8 +3,8 @@ datagrid: options: entityHint: tag entity_pagination: true + acl_resource: oro_tag_view source: - acl_resource: oro_tag_view type: orm query: select: @@ -90,8 +90,8 @@ datagrid: tag-results-grid: options: entityHint: result + acl_resource: oro_tag_view source: - acl_resource: oro_tag_view type: orm query: select: diff --git a/src/Oro/Bundle/TrackingBundle/Resources/config/datagrid.yml b/src/Oro/Bundle/TrackingBundle/Resources/config/datagrid.yml index 098806bc058..ae4e173cceb 100644 --- a/src/Oro/Bundle/TrackingBundle/Resources/config/datagrid.yml +++ b/src/Oro/Bundle/TrackingBundle/Resources/config/datagrid.yml @@ -1,9 +1,9 @@ datagrid: website-grid: extended_entity_name: %oro_tracking.tracking_website.class% + acl_resource: oro_tracking_website_view source: type: orm - acl_resource: oro_tracking_website_view query: select: - website.id @@ -124,9 +124,9 @@ datagrid: tracking-events-grid: extended_entity_name: %oro_tracking.tracking_event.class% + acl_resource: oro_tracking_website_view source: type: orm - acl_resource: oro_tracking_website_view query: select: - e.id diff --git a/src/Oro/Bundle/UserBundle/EventListener/OwnerUserGridListener.php b/src/Oro/Bundle/UserBundle/EventListener/OwnerUserGridListener.php index f7156ba4d4f..61e56df4859 100644 --- a/src/Oro/Bundle/UserBundle/EventListener/OwnerUserGridListener.php +++ b/src/Oro/Bundle/UserBundle/EventListener/OwnerUserGridListener.php @@ -137,10 +137,15 @@ protected function applyACL(DatagridConfiguration $config, $accessLevel, User $u $leftJoins[] = ['join' => 'u.businessUnits', 'alias' => 'bu']; $config->offsetSetByPath('[source][query][join][inner]', $leftJoins); - $where = array_merge( - $where, - ['bu.id in (' . implode(', ', $resultBuIds) . ')'] - ); + if ($resultBuIds) { + $where = array_merge( + $where, + ['bu.id in (' . implode(', ', $resultBuIds) . ')'] + ); + } else { + // There are no records to show, make query to return empty result + $where = array_merge($where, ['1 = 0']); + } } if (count($where)) { $config->offsetSetByPath('[source][query][where][and]', $where); diff --git a/src/Oro/Bundle/UserBundle/Resources/config/datagrid.yml b/src/Oro/Bundle/UserBundle/Resources/config/datagrid.yml index 6f815a08107..e07672042bc 100644 --- a/src/Oro/Bundle/UserBundle/Resources/config/datagrid.yml +++ b/src/Oro/Bundle/UserBundle/Resources/config/datagrid.yml @@ -4,8 +4,8 @@ datagrid: options: entityHint: user entity_pagination: true + acl_resource: oro_user_user_view source: - acl_resource: oro_user_user_view type: orm query: select: @@ -135,8 +135,8 @@ datagrid: options: entityHint: group entity_pagination: true + acl_resource: oro_user_group_view source: - acl_resource: oro_user_group_view type: orm query: select: @@ -195,9 +195,10 @@ datagrid: options: entityHint: role entity_pagination: true + acl_resource: oro_user_role_view source: - acl_resource: oro_user_role_view type: orm + skip_acl_apply: true query: select: - r @@ -293,8 +294,8 @@ datagrid: role-users-grid: extends: user-relation-grid + acl_resource: oro_user_role_update source: - acl_resource: oro_user_role_update query: select: - > @@ -373,8 +374,8 @@ datagrid: group-users-grid: extends: user-relation-grid + source: - acl_resource: oro_user_group_update query: select: - > @@ -454,8 +455,8 @@ datagrid: extended_entity_name: %oro_user.entity.class% options: entityHint: user + acl_resource: oro_user_user_view source: - acl_resource: oro_user_user_view type: orm query: select: @@ -526,10 +527,9 @@ datagrid: extended_entity_name: %oro_user.entity.class% options: entityHint: user - skip_acl_check: true source: - acl_resource: oro_user_user_view type: orm + skip_acl_apply: true query: select: - u.id @@ -614,9 +614,8 @@ datagrid: pageSize: default_per_page: 10 routerEnabled: false - + acl_resource: oro_user_user_view source: - acl_resource: oro_user_user_view type: orm query: select: diff --git a/src/Oro/Bundle/WorkflowBundle/Resources/config/datagrid.yml b/src/Oro/Bundle/WorkflowBundle/Resources/config/datagrid.yml index 5fea4a76940..fa72c37d98c 100644 --- a/src/Oro/Bundle/WorkflowBundle/Resources/config/datagrid.yml +++ b/src/Oro/Bundle/WorkflowBundle/Resources/config/datagrid.yml @@ -3,9 +3,9 @@ datagrid: options: entityHint: processes export: false + acl_resource: oro_process_definition_view source: - type: orm - acl_resource: oro_process_definition_view + type: orm query: select: - process.name @@ -100,9 +100,9 @@ datagrid: options: entityHint: workflowssss export: false + acl_resource: oro_workflow_definition_view source: type: orm - acl_resource: oro_workflow_definition_view query: select: - w.name as id From 430df1f8b7587b24b6f3926c6bcc927a198826c7 Mon Sep 17 00:00:00 2001 From: Andrii Muzalevskyi Date: Fri, 25 Dec 2015 11:40:31 +0200 Subject: [PATCH 268/471] CRM-4358 Display help in inline editing mode --- .../Resources/config/assets.yml | 1 + .../public/css/less/inline-editing-help.less | 40 +++++++++++++++++++ .../grid/inline-editing-help-plugin.js | 39 ++++++++++++++++++ .../app/plugins/grid/inline-editing-plugin.js | 2 + .../public/js/inline-editing/builder.js | 4 ++ .../Resources/translations/jsmessages.en.yml | 9 +++++ 6 files changed, 95 insertions(+) create mode 100644 src/Oro/Bundle/DataGridBundle/Resources/public/css/less/inline-editing-help.less create mode 100644 src/Oro/Bundle/DataGridBundle/Resources/public/js/app/plugins/grid/inline-editing-help-plugin.js diff --git a/src/Oro/Bundle/DataGridBundle/Resources/config/assets.yml b/src/Oro/Bundle/DataGridBundle/Resources/config/assets.yml index 30050d0e412..6f4b05d1e39 100644 --- a/src/Oro/Bundle/DataGridBundle/Resources/config/assets.yml +++ b/src/Oro/Bundle/DataGridBundle/Resources/config/assets.yml @@ -3,3 +3,4 @@ css: - 'bundles/orodatagrid/lib/backgrid/backgrid.css' - 'bundles/orodatagrid/lib/backgrid/extensions/paginator/backgrid-paginator.css' - 'bundles/orodatagrid/css/less/main.less' + - 'bundles/orodatagrid/css/less/inline-editing-help.less' diff --git a/src/Oro/Bundle/DataGridBundle/Resources/public/css/less/inline-editing-help.less b/src/Oro/Bundle/DataGridBundle/Resources/public/css/less/inline-editing-help.less new file mode 100644 index 00000000000..7bb0cd709de --- /dev/null +++ b/src/Oro/Bundle/DataGridBundle/Resources/public/css/less/inline-editing-help.less @@ -0,0 +1,40 @@ +.inline-editing-help-wrapper{ + z-index: 10000; + border-radius: 50px; + background: white; + width: 38px; + height: 38px; + position: absolute; + top: 30px; + right: 30px; + padding: 4px; + box-shadow: 0px 3px 16px rgba(0,0,0,0.4), 0 0 6px rgba(0,0,0,0.4); + .inline-editing-help-content { + width: 474px; + background: #FFF; + top: 39px; + margin-left: -415px !important; + max-width: 457px; + z-index: 10000; + color: #444; + font-size: 13px; + padding: 15px; + > .arrow { + left: 433px; + } + } + &:hover { + box-shadow: 0px 4px 20px rgba(0,0,0,0.4), 0 0 8px rgba(0,0,0,0.4); + } +} +.inline-editing-help{ + border-radius: 50px; + width: 30px; + height: 30px; + background: rgb(108, 146, 209); + color: white; + font-size: 24px; + text-align: center; + line-height: 31px; + font-weight: normal; +} diff --git a/src/Oro/Bundle/DataGridBundle/Resources/public/js/app/plugins/grid/inline-editing-help-plugin.js b/src/Oro/Bundle/DataGridBundle/Resources/public/js/app/plugins/grid/inline-editing-help-plugin.js new file mode 100644 index 00000000000..ca326396f85 --- /dev/null +++ b/src/Oro/Bundle/DataGridBundle/Resources/public/js/app/plugins/grid/inline-editing-help-plugin.js @@ -0,0 +1,39 @@ +define(function(require) { + 'use strict'; + + var InlineEditingHelpPlugin; + var __ = require('orotranslation/js/translator'); + var BasePlugin = require('oroui/js/app/plugins/base/plugin'); + var $ = require('jquery'); + + InlineEditingHelpPlugin = BasePlugin.extend({ + enable: function() { + this.listenTo(this.main, 'holdInlineEditingBackdrop', this.onHoldInlineEditingBackdrop); + this.listenTo(this.main, 'releaseInlineEditingBackdrop', this.onReleaseInlineEditingBackdrop); + InlineEditingHelpPlugin.__super__.enable.call(this); + }, + + onHoldInlineEditingBackdrop: function() { + $(document.body).append( + '
            ' + + '
            ' + + '' + + '
            ' + + '
            ' + + '
            ' + + __('oro.datagrid.inline_editing.help') + + '
            ' + + '
            ' + ); + $('.inline-editing-help-wrapper').click(function() { + $('.inline-editing-help-content').toggle(); + }); + }, + + onReleaseInlineEditingBackdrop: function() { + $('body > .inline-editing-help-wrapper').remove(); + } + }); + + return InlineEditingHelpPlugin; +}); diff --git a/src/Oro/Bundle/DataGridBundle/Resources/public/js/app/plugins/grid/inline-editing-plugin.js b/src/Oro/Bundle/DataGridBundle/Resources/public/js/app/plugins/grid/inline-editing-plugin.js index d879b024083..cf995f426da 100644 --- a/src/Oro/Bundle/DataGridBundle/Resources/public/js/app/plugins/grid/inline-editing-plugin.js +++ b/src/Oro/Bundle/DataGridBundle/Resources/public/js/app/plugins/grid/inline-editing-plugin.js @@ -234,6 +234,7 @@ define(function(require) { this.hidePopover(); // before adding backdrop this.backdropId = backdropManager.hold(); $(document).on('keydown', this.onKeyDown); + this.main.trigger('holdInlineEditingBackdrop'); } } this.editModeEnabled = true; @@ -388,6 +389,7 @@ define(function(require) { if (releaseBackdrop !== false) { backdropManager.release(this.backdropId); $(document).off('keydown', this.onKeyDown); + this.main.trigger('releaseInlineEditingBackdrop'); } delete this.editorComponent; }, diff --git a/src/Oro/Bundle/DataGridBundle/Resources/public/js/inline-editing/builder.js b/src/Oro/Bundle/DataGridBundle/Resources/public/js/inline-editing/builder.js index c0312fa4e49..843ff5a0405 100644 --- a/src/Oro/Bundle/DataGridBundle/Resources/public/js/inline-editing/builder.js +++ b/src/Oro/Bundle/DataGridBundle/Resources/public/js/inline-editing/builder.js @@ -4,6 +4,7 @@ define(function(require) { var $ = require('jquery'); var _ = require('underscore'); var tools = require('oroui/js/tools'); + var InlineEditingHelpPlugin = require('../app/plugins/grid/inline-editing-help-plugin'); var gridViewsBuilder = { /** @@ -29,6 +30,9 @@ define(function(require) { options.gridPromise.done(function(grid) { grid.pluginManager.create(options.metadata.inline_editing.plugin, options); grid.pluginManager.enable(options.metadata.inline_editing.plugin); + if (options.metadata.inline_editing.disable_help !== false) { + grid.pluginManager.enable(InlineEditingHelpPlugin); + } deferred.resolve(); }); }); diff --git a/src/Oro/Bundle/DataGridBundle/Resources/translations/jsmessages.en.yml b/src/Oro/Bundle/DataGridBundle/Resources/translations/jsmessages.en.yml index 2b934e7c14d..022b6a4b7bf 100644 --- a/src/Oro/Bundle/DataGridBundle/Resources/translations/jsmessages.en.yml +++ b/src/Oro/Bundle/DataGridBundle/Resources/translations/jsmessages.en.yml @@ -82,3 +82,12 @@ oro: close_tooltip: "Close Column Manager" empty_list: "No columns found" not_number: Not a number + inline_editing: + help: | +
              +
            • Enter, Shift + Enter - Save and edit next/previous cell in column
            • +
            • Tab, Shift + Tab - Save and edit next/previous cell in row
            • +
            • Alt + ←/↑/→/↓ - Navigate between cells
            • +
            • Ctrl + Enter - Save and close
            • +
            • Escape - Quit
            • +
            From 4393634d5f9ba8ffa915f8dfcb83cca369597e2e Mon Sep 17 00:00:00 2001 From: Oleksandr Nechyporenko Date: Fri, 25 Dec 2015 11:45:29 +0200 Subject: [PATCH 269/471] BB-1491: Login and Registration Page Layouts - fix to many new line --- .../Layout/Block/Extension/ConfigExpressionExtensionTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Block/Extension/ConfigExpressionExtensionTest.php b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Block/Extension/ConfigExpressionExtensionTest.php index 26842db0ed0..4761f937081 100644 --- a/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Block/Extension/ConfigExpressionExtensionTest.php +++ b/src/Oro/Bundle/LayoutBundle/Tests/Unit/Layout/Block/Extension/ConfigExpressionExtensionTest.php @@ -110,4 +110,3 @@ protected function getDataAccessorMock() return $this->getMock('Oro\Component\Layout\DataAccessorInterface'); } } - From 335aa70855fa1ac6786cb0da3a8e0cc4e4c519d4 Mon Sep 17 00:00:00 2001 From: zebimax Date: Fri, 25 Dec 2015 12:46:17 +0200 Subject: [PATCH 270/471] BAP-9577: Add option 'Use as default' to views options - add repository method to find default gv - add use as def option --- .../Entity/Repository/GridViewRepository.php | 22 ++++++++++ .../EventListener/GridViewsLoadListener.php | 8 ++-- .../Extension/GridViews/AbstractViewsList.php | 3 +- .../GridViews/GridViewsExtension.php | 9 +++-- .../Extension/GridViews/View.php | 40 +++++++++++++++---- .../public/js/datagrid/grid-views/model.js | 5 ++- .../public/js/datagrid/grid-views/view.js | 16 +++++++- .../Resources/translations/jsmessages.en.yml | 1 + 8 files changed, 85 insertions(+), 19 deletions(-) diff --git a/src/Oro/Bundle/DataGridBundle/Entity/Repository/GridViewRepository.php b/src/Oro/Bundle/DataGridBundle/Entity/Repository/GridViewRepository.php index 9d174a3a755..d203c31f8b5 100644 --- a/src/Oro/Bundle/DataGridBundle/Entity/Repository/GridViewRepository.php +++ b/src/Oro/Bundle/DataGridBundle/Entity/Repository/GridViewRepository.php @@ -36,4 +36,26 @@ public function findGridViews(AclHelper $aclHelper, UserInterface $user, $gridNa return $aclHelper->apply($qb)->getResult(); } + + /** + * @param AclHelper $aclHelper + * @param UserInterface $user + * @param $gridName + * + * @return mixed + */ + public function findUserDefaultGridView(AclHelper $aclHelper, UserInterface $user, $gridName) + { + $qb = $this->createQueryBuilder('gv'); + $qb->innerJoin('gv.users', 'u') + ->where('gv.gridName = :gridName') + ->andWhere('u = :user') + ->setParameters([ + 'gridName' => $gridName, + 'user' => $user + ]) + ->setMaxResults(1); + + return $aclHelper->apply($qb)->getOneOrNullResult(); + } } diff --git a/src/Oro/Bundle/DataGridBundle/EventListener/GridViewsLoadListener.php b/src/Oro/Bundle/DataGridBundle/EventListener/GridViewsLoadListener.php index c84e52b0670..311519eebfc 100644 --- a/src/Oro/Bundle/DataGridBundle/EventListener/GridViewsLoadListener.php +++ b/src/Oro/Bundle/DataGridBundle/EventListener/GridViewsLoadListener.php @@ -55,18 +55,19 @@ public function onViewsLoad(GridViewsLoadEvent $event) return; } - $gridViews = $this->getGridViewRepository()->findGridViews($this->aclHelper, $currentUser, $gridName); + $gridViewRepository = $this->getGridViewRepository(); + $gridViews = $gridViewRepository->findGridViews($this->aclHelper, $currentUser, $gridName); + $defaultGridView = $gridViewRepository->findUserDefaultGridView($this->aclHelper, $currentUser, $gridName); if (!$gridViews) { return; } - $choices = []; $views = []; foreach ($gridViews as $gridView) { $view = $gridView->createView(); $view->setEditable($this->securityFacade->isGranted('EDIT', $gridView)); $view->setDeletable($this->securityFacade->isGranted('DELETE', $gridView)); - + $view->setIsDefault($defaultGridView === $gridView); $views[] = $view->getMetadata(); $choices[] = [ 'label' => $this->createGridViewLabel($currentUser, $gridView), @@ -77,6 +78,7 @@ public function onViewsLoad(GridViewsLoadEvent $event) $newGridViews = $event->getGridViews(); $newGridViews['choices'] = array_merge($newGridViews['choices'], $choices); $newGridViews['views'] = array_merge($newGridViews['views'], $views); + $newGridViews['default'] = $defaultGridView; $event->setGridViews($newGridViews); } diff --git a/src/Oro/Bundle/DataGridBundle/Extension/GridViews/AbstractViewsList.php b/src/Oro/Bundle/DataGridBundle/Extension/GridViews/AbstractViewsList.php index bf5734c1785..ef27ddccaa9 100644 --- a/src/Oro/Bundle/DataGridBundle/Extension/GridViews/AbstractViewsList.php +++ b/src/Oro/Bundle/DataGridBundle/Extension/GridViews/AbstractViewsList.php @@ -100,7 +100,8 @@ function (View $view) { return [ 'choices' => $this->toChoiceList(), - 'views' => $result->toArray() + 'views' => $result->toArray(), + 'default' => null ]; } diff --git a/src/Oro/Bundle/DataGridBundle/Extension/GridViews/GridViewsExtension.php b/src/Oro/Bundle/DataGridBundle/Extension/GridViews/GridViewsExtension.php index 560112ff2f9..d50e4a9b75f 100644 --- a/src/Oro/Bundle/DataGridBundle/Extension/GridViews/GridViewsExtension.php +++ b/src/Oro/Bundle/DataGridBundle/Extension/GridViews/GridViewsExtension.php @@ -76,8 +76,9 @@ public function visitMetadata(DatagridConfiguration $config, MetadataObject $dat } /** @var AbstractViewsList $list */ - $list = $config->offsetGetOr(self::VIEWS_LIST_KEY, false); - $gridViews = [ + $list = $config->offsetGetOr(self::VIEWS_LIST_KEY, false); + $systemAllView = new View(self::DEFAULT_VIEW_ID); + $gridViews = [ 'choices' => [ [ 'label' => $allLabel, @@ -85,7 +86,7 @@ public function visitMetadata(DatagridConfiguration $config, MetadataObject $dat ], ], 'views' => [ - (new View(self::DEFAULT_VIEW_ID))->getMetadata(), + $systemAllView->getMetadata(), ], ]; if ($list !== false) { @@ -100,7 +101,7 @@ public function visitMetadata(DatagridConfiguration $config, MetadataObject $dat $this->eventDispatcher->dispatch(GridViewsLoadEvent::EVENT_NAME, $event); $gridViews = $event->getGridViews(); } - + $systemAllView->setIsDefault($gridViews['default'] === null); $gridViews['gridName'] = $config->getName(); $gridViews['permissions'] = $this->getPermissions(); $data->offsetAddToArray('gridViews', $gridViews); diff --git a/src/Oro/Bundle/DataGridBundle/Extension/GridViews/View.php b/src/Oro/Bundle/DataGridBundle/Extension/GridViews/View.php index 292a5c750e6..6a9feb46a79 100644 --- a/src/Oro/Bundle/DataGridBundle/Extension/GridViews/View.php +++ b/src/Oro/Bundle/DataGridBundle/Extension/GridViews/View.php @@ -25,6 +25,9 @@ class View /** @var bool */ protected $deletable = false; + /** @var bool */ + protected $isDefault = false; + /** * @var array * @@ -194,6 +197,26 @@ public function setColumnsData(array $columnsData = []) $this->columnsData = $columnsData; } + /** + * @return boolean + */ + public function isIsDefault() + { + return $this->isDefault; + } + + /** + * @param boolean $isDefault + * + * @return $this + */ + public function setIsDefault($isDefault) + { + $this->isDefault = $isDefault; + + return $this; + } + /** * Convert to view data * @@ -202,14 +225,15 @@ public function setColumnsData(array $columnsData = []) public function getMetadata() { return [ - 'name' => $this->getName(), - 'label' => $this->label, - 'type' => $this->getType(), - 'filters' => $this->getFiltersData(), - 'sorters' => $this->getSortersData(), - 'columns' => $this->columnsData, - 'editable' => $this->editable, - 'deletable' => $this->deletable, + 'name' => $this->getName(), + 'label' => $this->label, + 'type' => $this->getType(), + 'filters' => $this->getFiltersData(), + 'sorters' => $this->getSortersData(), + 'columns' => $this->columnsData, + 'editable' => $this->editable, + 'deletable' => $this->deletable, + 'is_default' => $this->isDefault ]; } } diff --git a/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/grid-views/model.js b/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/grid-views/model.js index 2ea4878c4e7..96d03f981c2 100644 --- a/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/grid-views/model.js +++ b/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/grid-views/model.js @@ -20,7 +20,8 @@ define([ sorters: [], columns: {}, deletable: false, - editable: false + editable: false, + is_default: false }, /** @property */ @@ -77,7 +78,7 @@ define([ * @returns {Array} */ toJSON: function() { - return _.omit(this.attributes, ['editable', 'deletable']); + return _.omit(this.attributes, ['editable', 'deletable', 'is_default']); } }); diff --git a/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/grid-views/view.js b/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/grid-views/view.js index a13a47423a1..d951f224d54 100644 --- a/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/grid-views/view.js +++ b/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/grid-views/view.js @@ -34,7 +34,8 @@ define([ 'click a.unshare': 'onUnshare', 'click a.delete': 'onDelete', 'click a.rename': 'onRename', - 'click a.discard_changes': 'onDiscardChanges' + 'click a.discard_changes': 'onDiscardChanges', + 'click a.use_as_default': 'onUseAsDefault' }, /** @property */ @@ -371,6 +372,14 @@ define([ this.changeView(this.collection.state.gridView); }, + /** + * @param {Event} e + */ + onUseAsDefault: function(e) { + var model = this._getCurrentViewModel(); + var self = this; + }, + /** * @private * @@ -543,6 +552,11 @@ define([ name: 'delete', enabled: typeof currentView !== 'undefined' && currentView.get('deletable') + }, + { + label: __('oro.datagrid.action.use_as_default_grid_view'), + name: 'use_as_default', + enabled: !currentView.get('is_default') } ]; }, diff --git a/src/Oro/Bundle/DataGridBundle/Resources/translations/jsmessages.en.yml b/src/Oro/Bundle/DataGridBundle/Resources/translations/jsmessages.en.yml index 2b934e7c14d..028cbdf266f 100644 --- a/src/Oro/Bundle/DataGridBundle/Resources/translations/jsmessages.en.yml +++ b/src/Oro/Bundle/DataGridBundle/Resources/translations/jsmessages.en.yml @@ -53,6 +53,7 @@ oro_datagrid.action.refresh: "Refresh" "oro.datagrid.action.share_grid_view": "Share With Others" "oro.datagrid.action.unshare_grid_view": "Unshare" "oro.datagrid.action.delete_grid_view": "Delete" +"oro.datagrid.action.use_as_default_grid_view": "Use as default" "oro.datagrid.gridView.all": "All" "oro.datagrid.gridView.default": "Default" "oro.datagrid.gridView.created": "View has been successfully created" From 612b2b211f00a49696718b3083dfed5dd9a238d3 Mon Sep 17 00:00:00 2001 From: Andrii Muzalevskyi Date: Fri, 25 Dec 2015 13:18:46 +0200 Subject: [PATCH 271/471] BAP-9553 Grids and debug toolbar fix --- .../js/app/views/page/debug-toolbar-view.js | 13 ++++++++++++ .../UIBundle/Resources/public/js/layout.js | 20 ++++++++++++++++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/Oro/Bundle/UIBundle/Resources/public/js/app/views/page/debug-toolbar-view.js b/src/Oro/Bundle/UIBundle/Resources/public/js/app/views/page/debug-toolbar-view.js index 439b2ea5868..75fc1b5ca19 100644 --- a/src/Oro/Bundle/UIBundle/Resources/public/js/app/views/page/debug-toolbar-view.js +++ b/src/Oro/Bundle/UIBundle/Resources/public/js/app/views/page/debug-toolbar-view.js @@ -14,6 +14,11 @@ define([ 'page:error mediator': 'onPageUpdate' }, + events: { + 'click .hide-button': 'sendUpdates', + 'click .sf-minitoolbar': 'sendUpdates' + }, + /** * Handles page load event * - loads debug data @@ -65,6 +70,14 @@ define([ .attr('data-sfurl', url); this.$el.html(data); + this.sendUpdates(); + }, + + /** + * Notifies application about updates + */ + sendUpdates: function() { + mediator.trigger('debugToolbar:afterUpdateView'); mediator.trigger('layout:adjustHeight'); } }); diff --git a/src/Oro/Bundle/UIBundle/Resources/public/js/layout.js b/src/Oro/Bundle/UIBundle/Resources/public/js/layout.js index 7691dd48e92..c7b834dcc9f 100644 --- a/src/Oro/Bundle/UIBundle/Resources/public/js/layout.js +++ b/src/Oro/Bundle/UIBundle/Resources/public/js/layout.js @@ -53,10 +53,24 @@ define(function(require) { * @returns {number} development toolbar height in dev mode, 0 in production mode */ getDevToolbarHeight: function() { - if (!this.devToolbarHeight) { + if (!mediator.execute('retrieveOption', 'debug')) { + return 0; + } + if (!this.devToolbarHeightListenersAttached) { + this.devToolbarHeightListenersAttached = true; + $(window).on('resize', function() { + delete layout.devToolbarHeight; + }); + mediator.on('debugToolbar:afterUpdateView', function() { + delete layout.devToolbarHeight; + }); + } + if (this.devToolbarHeight === void 0) { var devToolbarComposition = mediator.execute('composer:retrieve', 'debugToolbar', true); - if (devToolbarComposition && devToolbarComposition.view) { - this.devToolbarHeight = devToolbarComposition.view.$el.height(); + if (devToolbarComposition && + devToolbarComposition.view && + devToolbarComposition.view.$('.sf-toolbarreset').is(':visible')) { + this.devToolbarHeight = devToolbarComposition.view.$('.sf-toolbarreset').height(); } else { this.devToolbarHeight = 0; } From ef3cd4f10d2cfa81c489b51e280e716f6e596ed3 Mon Sep 17 00:00:00 2001 From: Andrii Muzalevskyi Date: Fri, 25 Dec 2015 13:37:01 +0200 Subject: [PATCH 272/471] BAP-9553 Use default fix --- .../ConfigBundle/Resources/public/css/less/config.less | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Oro/Bundle/ConfigBundle/Resources/public/css/less/config.less b/src/Oro/Bundle/ConfigBundle/Resources/public/css/less/config.less index 63417e236ea..ec10c7970e7 100644 --- a/src/Oro/Bundle/ConfigBundle/Resources/public/css/less/config.less +++ b/src/Oro/Bundle/ConfigBundle/Resources/public/css/less/config.less @@ -31,9 +31,14 @@ } .controls .parent-scope-checkbox { float: left; + color: #777; + padding-top: 6px; label { float: left; - margin: 0 15px 0 0; + margin: 0 4px 0 0; + } + input[type="checkbox"] { + margin: -3px 10px 0 0 } } .oro-item-collection input[type="text"]{ From 09b43d6324f32ca774d85e830225ef48c9910462 Mon Sep 17 00:00:00 2001 From: Andrii Muzalevskyi Date: Fri, 25 Dec 2015 14:08:16 +0200 Subject: [PATCH 273/471] BAP-9553 Select2/Uniform should have same styles --- .../UIBundle/Resources/public/css/less/oro.less | 13 ++++++------- .../Resources/public/css/less/oro/select2.less | 10 +++++++--- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/Oro/Bundle/UIBundle/Resources/public/css/less/oro.less b/src/Oro/Bundle/UIBundle/Resources/public/css/less/oro.less index 747902fdb2a..d641c3cbe17 100644 --- a/src/Oro/Bundle/UIBundle/Resources/public/css/less/oro.less +++ b/src/Oro/Bundle/UIBundle/Resources/public/css/less/oro.less @@ -2243,11 +2243,10 @@ select { line-height: 26px; } div.selector { - line-height: 28px; + line-height: 30px; height: 30px; position: relative; width: 235px; - font-size: 12px; border: 1px solid #ddd; #gradient > .vertical(#ffffff, #f3f3f3); -webkit-border-radius: 3px; @@ -2257,19 +2256,19 @@ div.selector { } div.selector span { - height: 28px; + height: 30px; display: block; - line-height: 28px; + line-height: 30px; .box-sizing(border-box); - padding: 0 5px; + padding: 0 5px 0 8px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; position: relative; &:after { - margin:2px 2px 0 0; + margin: 3px 2px 0 0; display:inline-block; - font:15px 'FontAwesome'; + font:16px 'FontAwesome'; content: "\f0dd"; position: absolute; right: 5px; diff --git a/src/Oro/Bundle/UIBundle/Resources/public/css/less/oro/select2.less b/src/Oro/Bundle/UIBundle/Resources/public/css/less/oro/select2.less index b00869f2ec4..3575576fdeb 100644 --- a/src/Oro/Bundle/UIBundle/Resources/public/css/less/oro/select2.less +++ b/src/Oro/Bundle/UIBundle/Resources/public/css/less/oro/select2.less @@ -19,11 +19,11 @@ Version: 3.4.1 Timestamp: Thu Jun 27 18:02:10 PDT 2013 .select2-choice { display: block; height: 28px; - padding: 0 0 0 8px; + padding: 0; overflow: hidden; position: relative; white-space: nowrap; - line-height: 28px; + line-height: 1em; color: #444; text-decoration: none; -webkit-background-clip: padding-box; @@ -40,6 +40,10 @@ Version: 3.4.1 Timestamp: Thu Jun 27 18:02:10 PDT 2013 } img{ max-width: 16px; + margin-top: -2px; + display: block; + float: left; + margin-right: 2px; } &:focus { border: none !important; @@ -508,7 +512,7 @@ disabled look for disabled choices in the results dropdown } .select2-container a.select2-choice .select2-chosen { - padding-right: 30px; + padding: 8px 30px 0 8px; } .select2-search-choice-close { From dbd9365eea081e635b3c16d45f70b4ad27d7dcfa Mon Sep 17 00:00:00 2001 From: Andrii Muzalevskyi Date: Fri, 25 Dec 2015 14:18:04 +0200 Subject: [PATCH 274/471] BAP-9553Select2 multi fixes --- .../UIBundle/Resources/public/css/less/oro/select2.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Oro/Bundle/UIBundle/Resources/public/css/less/oro/select2.less b/src/Oro/Bundle/UIBundle/Resources/public/css/less/oro/select2.less index 3575576fdeb..b81e0f89bd5 100644 --- a/src/Oro/Bundle/UIBundle/Resources/public/css/less/oro/select2.less +++ b/src/Oro/Bundle/UIBundle/Resources/public/css/less/oro/select2.less @@ -430,7 +430,7 @@ disabled look for disabled choices in the results dropdown height: 1%; margin: 0; padding: 0; - min-height: 29px; + min-height: 28px; position: relative; cursor: text; overflow: hidden; @@ -451,7 +451,7 @@ disabled look for disabled choices in the results dropdown } .select2-container-multi .select2-choices .select2-search-field input { - padding:3px 5px; + padding: 5px 5px 1px; margin: 1px 0; font-family:@baseFontFamily; font-size: 100%; From 717e61c79ec8c7012f83237bbce364a676cb6015 Mon Sep 17 00:00:00 2001 From: Andrii Muzalevskyi Date: Fri, 25 Dec 2015 14:54:52 +0200 Subject: [PATCH 275/471] BAP-9553 Tags IE display fix --- .../Bundle/TagBundle/Resources/public/css/tags-container.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Oro/Bundle/TagBundle/Resources/public/css/tags-container.less b/src/Oro/Bundle/TagBundle/Resources/public/css/tags-container.less index f42a2aee375..15140dc0145 100644 --- a/src/Oro/Bundle/TagBundle/Resources/public/css/tags-container.less +++ b/src/Oro/Bundle/TagBundle/Resources/public/css/tags-container.less @@ -13,8 +13,8 @@ border-radius: 3px; margin-right: 0px; font-size: 13px; - line-height: 13px; - padding: 4px 6px; + line-height: 20px; + padding: 1px 6px 0; display: inline-block; vertical-align: baseline; } From 75abf7d2b87eb948cab671775497f0e49916fd14 Mon Sep 17 00:00:00 2001 From: Makar Sichevoy Date: Fri, 25 Dec 2015 15:00:33 +0200 Subject: [PATCH 276/471] BAP-9301 Wrong List of BU in the filter on the grid - highlight search results --- .../FilterBundle/Resources/public/css/less/oro.filter.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Oro/Bundle/FilterBundle/Resources/public/css/less/oro.filter.less b/src/Oro/Bundle/FilterBundle/Resources/public/css/less/oro.filter.less index bb6ab7f1a02..06fbb95a259 100644 --- a/src/Oro/Bundle/FilterBundle/Resources/public/css/less/oro.filter.less +++ b/src/Oro/Bundle/FilterBundle/Resources/public/css/less/oro.filter.less @@ -384,9 +384,9 @@ button.ui-multiselect.select-filter-widget.ui-state-hover{ margin-top: 0; margin-left: -18px; } - label{ + label { padding-left: 20px; - .search-result { + &.search-result { color: #000000; font-weight: bold; } From ceab9c6228af9d7bdccca317e47eff0aaf47be1d Mon Sep 17 00:00:00 2001 From: Hryhorii Hrebiniuk Date: Fri, 25 Dec 2015 15:07:26 +0200 Subject: [PATCH 277/471] Revert "BAP-9553 fit dashboard widget header" --- .../Resources/public/css/less/dashboard.less | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less b/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less index eb1e040631d..641d3ea35ef 100644 --- a/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less +++ b/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less @@ -115,10 +115,7 @@ white-space: nowrap; .action-wrapper { border-right: 1px solid #ddd; - padding: 0 3px; - > a { - padding: 10px 8px; - } + padding: 3px 13px; } } @@ -146,6 +143,7 @@ padding-left: 0; padding-right: 0; max-width: ~"calc(100% - 120px)"; + padding-bottom: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -153,6 +151,7 @@ .row-fluid { border-top: 1px solid #ddd; + margin-top: 5px; } .dashboard-widget-content { @@ -195,10 +194,11 @@ .row-fluid { border-top: none; } + height: 43px; overflow: hidden; } .collapse-expand-action-container { - padding: 0 2px 0 12px; + padding-left: 8px; } } From ab329e611cabf29abd096f29982ef7afce34717d Mon Sep 17 00:00:00 2001 From: Andrii Muzalevskyi Date: Tue, 22 Dec 2015 19:54:59 +0200 Subject: [PATCH 278/471] BAP-9553 fit dashboard widget header --- .../DashboardBundle/Resources/public/css/less/dashboard.less | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less b/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less index 641d3ea35ef..d034d84e17b 100644 --- a/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less +++ b/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less @@ -143,7 +143,6 @@ padding-left: 0; padding-right: 0; max-width: ~"calc(100% - 120px)"; - padding-bottom: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -151,7 +150,6 @@ .row-fluid { border-top: 1px solid #ddd; - margin-top: 5px; } .dashboard-widget-content { From 0d4ab872b8c884ddbf73e1e0365b7526bed43a95 Mon Sep 17 00:00:00 2001 From: Andrii Muzalevskyi Date: Wed, 23 Dec 2015 13:20:49 +0200 Subject: [PATCH 279/471] dashboard widget header fix --- .../DashboardBundle/Resources/public/css/less/dashboard.less | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less b/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less index d034d84e17b..f346128d82f 100644 --- a/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less +++ b/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less @@ -192,7 +192,6 @@ .row-fluid { border-top: none; } - height: 43px; overflow: hidden; } .collapse-expand-action-container { From 64200aa97661fb34e839629d4ff73b75b6a57d70 Mon Sep 17 00:00:00 2001 From: Andrii Muzalevskyi Date: Wed, 23 Dec 2015 13:27:58 +0200 Subject: [PATCH 280/471] BAP-9553 access area of actions for dashboard widget --- .../DashboardBundle/Resources/public/css/less/dashboard.less | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less b/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less index f346128d82f..f28509e40d6 100644 --- a/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less +++ b/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less @@ -115,7 +115,10 @@ white-space: nowrap; .action-wrapper { border-right: 1px solid #ddd; - padding: 3px 13px; + padding: 0 3px; + > a { + padding: 10px 8px; + } } } From 26759016d3224c13fae145c9818cac26ff6a1b50 Mon Sep 17 00:00:00 2001 From: Andrii Muzalevskyi Date: Wed, 23 Dec 2015 13:35:48 +0200 Subject: [PATCH 281/471] BAP-9553 access area of actions for dashboard widget --- .../DashboardBundle/Resources/public/css/less/dashboard.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less b/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less index f28509e40d6..eb1e040631d 100644 --- a/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less +++ b/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less @@ -198,7 +198,7 @@ overflow: hidden; } .collapse-expand-action-container { - padding-left: 8px; + padding: 0 2px 0 12px; } } From 38fb8fbe03cc336b40cc532620069003064c9356 Mon Sep 17 00:00:00 2001 From: Andrey Yatsenco Date: Fri, 25 Dec 2015 15:58:44 +0200 Subject: [PATCH 282/471] BB-1491: Login and Registration Page Layouts - phpmd fix --- .../Layout/Tests/Unit/LayoutBuilderTest.php | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/Oro/Component/Layout/Tests/Unit/LayoutBuilderTest.php b/src/Oro/Component/Layout/Tests/Unit/LayoutBuilderTest.php index 3464b9e0ae5..a50023332bf 100644 --- a/src/Oro/Component/Layout/Tests/Unit/LayoutBuilderTest.php +++ b/src/Oro/Component/Layout/Tests/Unit/LayoutBuilderTest.php @@ -275,15 +275,14 @@ public function testGetLayout() ->with($this->identicalTo($rootView)) ->will($this->returnValue($layout)); - $rawLayout->expects($this->once()) - ->method('getRootId') + $rawLayout->expects($this->once())->method('getRootId') ->will($this->returnValue($rootId)); $rawLayout->expects($this->once()) ->method('getBlockThemes') ->will( $this->returnValue( [ - $rootId => ['RootTheme1', 'RootTheme2', 'RootTheme3'], + $rootId => ['RootTheme1', 'RootTheme2', 'RootTheme3'], 'test_block' => ['TestTheme1', 'TestTheme2', 'TestTheme3'] ] ) @@ -297,13 +296,8 @@ public function testGetLayout() $layout->expects($this->exactly(2)) ->method('setBlockTheme'); - $rawLayout->expects($this->once()) - ->method('getFormThemes') - ->will( - $this->returnValue( - ['TestFormTheme1', 'TestFormTheme2'] - ) - ); + $rawLayout->expects($this->once())->method('getFormThemes') + ->will($this->returnValue(['TestFormTheme1', 'TestFormTheme2'])); $layout->expects($this->at(2)) ->method('setFormTheme') ->with(['TestFormTheme1', 'TestFormTheme2']); From 5350303288abd6eb8d5c35acff7db04c775fbd5e Mon Sep 17 00:00:00 2001 From: Alexander Nezdoiminoga Date: Fri, 25 Dec 2015 16:06:19 +0200 Subject: [PATCH 283/471] BAP-9492: fix Attachments grid not configured to support extended fields --- src/Oro/Bundle/AttachmentBundle/Resources/config/datagrid.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Oro/Bundle/AttachmentBundle/Resources/config/datagrid.yml b/src/Oro/Bundle/AttachmentBundle/Resources/config/datagrid.yml index 825461245e7..b1275df03c1 100644 --- a/src/Oro/Bundle/AttachmentBundle/Resources/config/datagrid.yml +++ b/src/Oro/Bundle/AttachmentBundle/Resources/config/datagrid.yml @@ -1,5 +1,6 @@ datagrid: attachment-grid: + extended_entity_name: %oro_attachment.entity.class% source: type: orm query: From 749f8fa2045799b5c3ee03f5afcf948355cd5257 Mon Sep 17 00:00:00 2001 From: Hryhorii Hrebiniuk Date: Fri, 25 Dec 2015 16:12:30 +0200 Subject: [PATCH 284/471] fixed PR --- src/Oro/Bundle/UIBundle/Resources/public/css/less/layout.less | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Oro/Bundle/UIBundle/Resources/public/css/less/layout.less b/src/Oro/Bundle/UIBundle/Resources/public/css/less/layout.less index 71ddf0e89d7..c0c212154c9 100644 --- a/src/Oro/Bundle/UIBundle/Resources/public/css/less/layout.less +++ b/src/Oro/Bundle/UIBundle/Resources/public/css/less/layout.less @@ -161,6 +161,3 @@ } } } -.responsive-cell > .oro-tabs { - -} From d51a7055baa6b3efd63f0a24efe05493b1d17582 Mon Sep 17 00:00:00 2001 From: Denis Voronin Date: Fri, 25 Dec 2015 16:44:56 +0200 Subject: [PATCH 285/471] BAP-9578: Detect and set default grid view --- .../Entity/Repository/GridViewRepository.php | 23 +++++ .../GridViews/GridViewsExtension.php | 44 +++++++-- .../Resources/config/extensions.yml | 1 + .../GridViews/GridViewsExtensionTest.php | 91 +++++++++++++++++-- 4 files changed, 142 insertions(+), 17 deletions(-) diff --git a/src/Oro/Bundle/DataGridBundle/Entity/Repository/GridViewRepository.php b/src/Oro/Bundle/DataGridBundle/Entity/Repository/GridViewRepository.php index 9d174a3a755..8383af2851e 100644 --- a/src/Oro/Bundle/DataGridBundle/Entity/Repository/GridViewRepository.php +++ b/src/Oro/Bundle/DataGridBundle/Entity/Repository/GridViewRepository.php @@ -8,6 +8,7 @@ use Oro\Bundle\DataGridBundle\Entity\GridView; use Oro\Bundle\SecurityBundle\ORM\Walker\AclHelper; +use Oro\Bundle\UserBundle\Entity\User; class GridViewRepository extends EntityRepository { @@ -36,4 +37,26 @@ public function findGridViews(AclHelper $aclHelper, UserInterface $user, $gridNa return $aclHelper->apply($qb)->getResult(); } + + /** + * @param string $gridName + * @param User $user + * + * @return GridView|null + */ + public function findDefaultGridView($gridName, User $user) + { + $qb = $this->createQueryBuilder('gv') + ->join('gv.users', 'user') + ->where('gv.gridName = :gridName') + ->andWhere('user = :user') + ->setParameters( + [ + 'gridName' => $gridName, + 'user' => $user, + ] + ); + + return $qb->getQuery()->getOneOrNullResult(); + } } diff --git a/src/Oro/Bundle/DataGridBundle/Extension/GridViews/GridViewsExtension.php b/src/Oro/Bundle/DataGridBundle/Extension/GridViews/GridViewsExtension.php index 90ed2a3a772..19dd8378bf1 100644 --- a/src/Oro/Bundle/DataGridBundle/Extension/GridViews/GridViewsExtension.php +++ b/src/Oro/Bundle/DataGridBundle/Extension/GridViews/GridViewsExtension.php @@ -2,6 +2,8 @@ namespace Oro\Bundle\DataGridBundle\Extension\GridViews; +use Doctrine\Common\Persistence\ManagerRegistry; + use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Translation\TranslatorInterface; @@ -32,19 +34,25 @@ class GridViewsExtension extends AbstractExtension /** @var TranslatorInterface */ protected $translator; + /** @var ManagerRegistry */ + protected $registry; + /** * @param EventDispatcherInterface $eventDispatcher - * @param SecurityFacade $securityFacade - * @param TranslatorInterface $translator + * @param SecurityFacade $securityFacade + * @param TranslatorInterface $translator + * @param ManagerRegistry $registry */ public function __construct( EventDispatcherInterface $eventDispatcher, SecurityFacade $securityFacade, - TranslatorInterface $translator + TranslatorInterface $translator, + ManagerRegistry $registry ) { $this->eventDispatcher = $eventDispatcher; - $this->securityFacade = $securityFacade; - $this->translator = $translator; + $this->securityFacade = $securityFacade; + $this->translator = $translator; + $this->registry = $registry; } /** @@ -70,14 +78,18 @@ protected function isDisabled() */ public function visitMetadata(DatagridConfiguration $config, MetadataObject $data) { - $params = $this->getParameters()->get(ParameterBag::ADDITIONAL_PARAMETERS, []); + $defaultViewId = $this->getDefaultViewId($config->getName()); + $params = $this->getParameters()->get(ParameterBag::ADDITIONAL_PARAMETERS, []); + if (isset($params[self::VIEWS_PARAM_KEY])) { $currentView = (int)$params[self::VIEWS_PARAM_KEY]; } else { - $currentView = self::DEFAULT_VIEW_ID; + $currentView = $defaultViewId; + $params[self::VIEWS_PARAM_KEY] = $defaultViewId; + $this->getParameters()->set(ParameterBag::ADDITIONAL_PARAMETERS, $params); } - $data->offsetAddToArray('initialState', ['gridView' => self::DEFAULT_VIEW_ID]); + $data->offsetAddToArray('initialState', ['gridView' => $defaultViewId]); $data->offsetAddToArray('state', ['gridView' => $currentView]); $allLabel = null; @@ -119,6 +131,22 @@ public function visitMetadata(DatagridConfiguration $config, MetadataObject $dat $data->offsetAddToArray('gridViews', $gridViews); } + /** + * @param string $gridName + * + * @return int|string + */ + protected function getDefaultViewId($gridName) + { + $defaultGridView = null; + if ($this->securityFacade->isGranted('oro_datagrid_gridview_view')) { + $repository = $this->registry->getRepository('OroDataGridBundle:GridView'); + $defaultGridView = $repository->findDefaultGridView($gridName, $this->securityFacade->getLoggedUser()); + } + + return $defaultGridView ? $defaultGridView->getId() : self::DEFAULT_VIEW_ID; + } + /** * @return array */ diff --git a/src/Oro/Bundle/DataGridBundle/Resources/config/extensions.yml b/src/Oro/Bundle/DataGridBundle/Resources/config/extensions.yml index 1647a0e069d..f68083b9c3e 100644 --- a/src/Oro/Bundle/DataGridBundle/Resources/config/extensions.yml +++ b/src/Oro/Bundle/DataGridBundle/Resources/config/extensions.yml @@ -81,6 +81,7 @@ services: - @event_dispatcher - @oro_security.security_facade - @translator + - @doctrine tags: - { name: oro_datagrid.extension } diff --git a/src/Oro/Bundle/DataGridBundle/Tests/Unit/Extension/GridViews/GridViewsExtensionTest.php b/src/Oro/Bundle/DataGridBundle/Tests/Unit/Extension/GridViews/GridViewsExtensionTest.php index b37de7cd588..15bbfa99a1f 100644 --- a/src/Oro/Bundle/DataGridBundle/Tests/Unit/Extension/GridViews/GridViewsExtensionTest.php +++ b/src/Oro/Bundle/DataGridBundle/Tests/Unit/Extension/GridViews/GridViewsExtensionTest.php @@ -12,6 +12,8 @@ class GridViewsExtensionTest extends \PHPUnit_Framework_TestCase { private $eventDispatcher; + + /** @var GridViewsExtension */ private $gridViewsExtension; public function setUp() @@ -29,16 +31,36 @@ public function setUp() ->method('isGranted') ->will($this->returnValue(true)); - $this->gridViewsExtension = new GridViewsExtension($this->eventDispatcher, $securityFacade, $translator); + $repo = $this->getMockBuilder('Doctrine\ORM\EntityRepository') + ->disableOriginalConstructor() + ->getMock(); + + $repo->expects($this->any()) + ->method('findDefaultGridView') + ->willReturn(null); + + $registry = $this->getMock('Doctrine\Common\Persistence\ManagerRegistry'); + $registry->expects($this->any()) + ->method('getRepository') + ->willReturn($repo); + + $this->gridViewsExtension = new GridViewsExtension( + $this->eventDispatcher, + $securityFacade, + $translator, + $registry + ); } public function testVisitMetadataShouldAddGridViewsFromEvent() { $this->gridViewsExtension->setParameters(new ParameterBag()); - $data = MetadataObject::create([]); - $config = DatagridConfiguration::create([ - DatagridConfiguration::NAME_KEY => 'grid', - ]); + $data = MetadataObject::create([]); + $config = DatagridConfiguration::create( + [ + DatagridConfiguration::NAME_KEY => 'grid', + ] + ); $this->eventDispatcher ->expects($this->once()) @@ -64,11 +86,15 @@ public function testVisitMetadataShouldAddGridViewsFromEvent() ->expects($this->once()) ->method('dispatch') ->with(GridViewsLoadEvent::EVENT_NAME) - ->will($this->returnCallback(function ($eventName, GridViewsLoadEvent $event) use ($expectedViews) { - $event->setGridViews($expectedViews); + ->will( + $this->returnCallback( + function ($eventName, GridViewsLoadEvent $event) use ($expectedViews) { + $event->setGridViews($expectedViews); - return $event; - })); + return $event; + } + ) + ); $this->assertFalse($data->offsetExists('gridViews')); $this->gridViewsExtension->visitMetadata($config, $data); @@ -76,6 +102,53 @@ public function testVisitMetadataShouldAddGridViewsFromEvent() $this->assertEquals($expectedViews, $data->offsetGet('gridViews')); } + + /** + * @param array $input + * @param bool $expected + * + * @dataProvider isApplicableDataProvider + */ + public function testIsApplicable($input, $expected) + { + $this->gridViewsExtension->setParameters(new ParameterBag($input)); + $config = DatagridConfiguration::create( + [ + DatagridConfiguration::NAME_KEY => 'grid', + ] + ); + $this->assertEquals($expected, $this->gridViewsExtension->isApplicable($config)); + } + + /** + * @return array + */ + public function isApplicableDataProvider() + { + return [ + 'Default' => [ + 'input' => [], + 'expected' => true, + ], + 'Extension disabled' => [ + 'input' => [ + '_grid_view' => [ + '_disabled' => true + ] + ], + 'expected' => false, + ], + 'Extension enabled' => [ + 'input' => [ + '_grid_view' => [ + '_disabled' => false + ] + ], + 'expected' => true, + ], + ]; + } + /** * @param array $input * @param array $expected From 1d41d027844ae0f9be9ba881427c6513ee569c9e Mon Sep 17 00:00:00 2001 From: Hryhorii Hrebiniuk Date: Fri, 25 Dec 2015 17:21:40 +0200 Subject: [PATCH 286/471] BAP-9553 Grids and debug toolbar fix --- .../UIBundle/Resources/public/css/less/oro/fs-toolbar.less | 2 ++ .../public/js/app/views/page/debug-toolbar-view.js | 6 +++++- src/Oro/Bundle/UIBundle/Resources/public/js/init-layout.js | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Oro/Bundle/UIBundle/Resources/public/css/less/oro/fs-toolbar.less b/src/Oro/Bundle/UIBundle/Resources/public/css/less/oro/fs-toolbar.less index 18233886940..ae5f021e8c2 100644 --- a/src/Oro/Bundle/UIBundle/Resources/public/css/less/oro/fs-toolbar.less +++ b/src/Oro/Bundle/UIBundle/Resources/public/css/less/oro/fs-toolbar.less @@ -11,9 +11,11 @@ & > .sf-toolbar-block{ border-right: 1px solid #13161a; border-left: 1px solid #424951; + border-bottom-color: #424951; &:hover{ border-right: 1px solid #bbb; border-left: 1px solid #bbb; + border-bottom-color: #bbb; } } & > .hide-button{ diff --git a/src/Oro/Bundle/UIBundle/Resources/public/js/app/views/page/debug-toolbar-view.js b/src/Oro/Bundle/UIBundle/Resources/public/js/app/views/page/debug-toolbar-view.js index 75fc1b5ca19..42e42d5a78a 100644 --- a/src/Oro/Bundle/UIBundle/Resources/public/js/app/views/page/debug-toolbar-view.js +++ b/src/Oro/Bundle/UIBundle/Resources/public/js/app/views/page/debug-toolbar-view.js @@ -30,7 +30,11 @@ define([ * @override */ onPageUpdate: function(data, actionArgs, xhr) { - if (!xhr) { + if (!actionArgs.route.previous) { + this.sendUpdates(); + // nothing to do, the page just loaded + return; + } else if (!xhr) { this.$el.empty(); mediator.trigger('layout:adjustHeight'); return; diff --git a/src/Oro/Bundle/UIBundle/Resources/public/js/init-layout.js b/src/Oro/Bundle/UIBundle/Resources/public/js/init-layout.js index 343a003532d..384886a0ffb 100644 --- a/src/Oro/Bundle/UIBundle/Resources/public/js/init-layout.js +++ b/src/Oro/Bundle/UIBundle/Resources/public/js/init-layout.js @@ -264,7 +264,7 @@ require(['jquery', 'underscore', 'orotranslation/js/translator', 'oroui/js/tools $main.width(realWidth($topPage) - realWidth($leftPanel) - realWidth($rightPanel)); layout.updateResponsiveLayout(); - var debugBarHeight = $('.sf-toolbar:visible').height() || 0; + var debugBarHeight = layout.getDevToolbarHeight(); var anchorTop = anchor.position().top; var footerHeight = $('#footer:visible').height() || 0; var fixContent = 1; From a0977e3c251820f6fff7fad166112d50990783ea Mon Sep 17 00:00:00 2001 From: Makar Sichevoy Date: Fri, 25 Dec 2015 17:38:22 +0200 Subject: [PATCH 287/471] BAP-9612: Unread email's status doesn't sync via IMAP --- .../Entity/Repository/EmailUserRepository.php | 8 +++++++- .../Entity/Repository/ImapEmailRepository.php | 14 ++++++++++---- .../Sync/ImapEmailSynchronizationProcessor.php | 6 +++--- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/Oro/Bundle/EmailBundle/Entity/Repository/EmailUserRepository.php b/src/Oro/Bundle/EmailBundle/Entity/Repository/EmailUserRepository.php index 451542dda47..8039a674f80 100644 --- a/src/Oro/Bundle/EmailBundle/Entity/Repository/EmailUserRepository.php +++ b/src/Oro/Bundle/EmailBundle/Entity/Repository/EmailUserRepository.php @@ -79,10 +79,11 @@ public function getEmailUserList(User $user, Organization $organization, array $ /** * @param array $ids * @param EmailFolder $folder + * @param \DateTime $date * * @return array */ - public function getInvertedIdsFromFolder(array $ids, EmailFolder $folder) + public function getInvertedIdsFromFolder(array $ids, EmailFolder $folder, $date = null) { $qb = $this->createQueryBuilder('email_user'); @@ -96,6 +97,11 @@ public function getInvertedIdsFromFolder(array $ids, EmailFolder $folder) ->setParameter('ids', $ids); } + if ($date) { + $qb->andWhere($qb->expr()->gt('email_user.receivedAt', ':date')) + ->setParameter('date', $date); + } + $emailUserIds = $qb->getQuery()->getArrayResult(); $ids = []; diff --git a/src/Oro/Bundle/ImapBundle/Entity/Repository/ImapEmailRepository.php b/src/Oro/Bundle/ImapBundle/Entity/Repository/ImapEmailRepository.php index 4c33db03068..00fbd35e214 100644 --- a/src/Oro/Bundle/ImapBundle/Entity/Repository/ImapEmailRepository.php +++ b/src/Oro/Bundle/ImapBundle/Entity/Repository/ImapEmailRepository.php @@ -132,22 +132,28 @@ public function getUid($folder, $email) /** * @param $uids * @param EmailFolder $folder + * @param \DateTime $date * * @return array */ - public function getEmailUserIdsByUIDs($uids, EmailFolder $folder) + public function getEmailUserIdsByUIDs($uids, EmailFolder $folder, $date = null) { $qb = $this->createQueryBuilder('ie'); - $emailUserIds = $qb->select('email_user.id') + $qb = $this->createQueryBuilder('ie')->select('email_user.id') ->leftJoin('ie.email', 'email') ->leftJoin('email.emailUsers', 'email_user') ->leftJoin('email_user.folders', 'folders') ->andWhere($qb->expr()->in('folders', ':folder')) ->andWhere($qb->expr()->in('ie.uid', ':uids')) ->setParameter('uids', $uids) - ->setParameter('folder', $folder) - ->getQuery()->getArrayResult(); + ->setParameter('folder', $folder); + + if ($date) { + $qb->andWhere($qb->expr()->gt('email_user.receivedAt', ':date')) + ->setParameter('date', $date); + } + $emailUserIds = $qb->getQuery()->getArrayResult(); $ids = []; foreach ($emailUserIds as $emailUserId) { diff --git a/src/Oro/Bundle/ImapBundle/Sync/ImapEmailSynchronizationProcessor.php b/src/Oro/Bundle/ImapBundle/Sync/ImapEmailSynchronizationProcessor.php index 27aafe2df1e..8c25533d7a6 100644 --- a/src/Oro/Bundle/ImapBundle/Sync/ImapEmailSynchronizationProcessor.php +++ b/src/Oro/Bundle/ImapBundle/Sync/ImapEmailSynchronizationProcessor.php @@ -93,7 +93,7 @@ public function process(EmailOrigin $origin, $syncStartTime) $startDate = $folder->getSynchronizedAt(); $checkStartDate = clone $startDate; - $checkStartDate->modify('-1 month'); + $checkStartDate->modify('-6 month'); // set seen flags from previously synchronized emails $this->checkFlags($imapFolder, $checkStartDate); @@ -124,8 +124,8 @@ protected function checkFlags(ImapEmailfolder $imapFolder, $startDate) $emailImapRepository = $this->em->getRepository('OroImapBundle:ImapEmail'); $emailUserRepository = $this->em->getRepository('OroEmailBundle:EmailUser'); - $ids = $emailImapRepository->getEmailUserIdsByUIDs($uids, $imapFolder->getFolder()); - $invertedIds = $emailUserRepository->getInvertedIdsFromFolder($ids, $imapFolder->getFolder()); + $ids = $emailImapRepository->getEmailUserIdsByUIDs($uids, $imapFolder->getFolder(), $startDate); + $invertedIds = $emailUserRepository->getInvertedIdsFromFolder($ids, $imapFolder->getFolder(), $startDate); $emailUserRepository->setEmailUsersSeen($ids, false); $emailUserRepository->setEmailUsersSeen($invertedIds, true); From 703f4dc48e620677fa17b4458bd460716afa7d96 Mon Sep 17 00:00:00 2001 From: Mykhailo Kudelia Date: Fri, 25 Dec 2015 17:39:23 +0200 Subject: [PATCH 288/471] BAP-9592: Wrong ACL condition to view BU grid --- .../Event/BusinessUnitGridListener.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Oro/Bundle/OrganizationBundle/Event/BusinessUnitGridListener.php b/src/Oro/Bundle/OrganizationBundle/Event/BusinessUnitGridListener.php index d6e64cd0ba7..ab0c58735ef 100644 --- a/src/Oro/Bundle/OrganizationBundle/Event/BusinessUnitGridListener.php +++ b/src/Oro/Bundle/OrganizationBundle/Event/BusinessUnitGridListener.php @@ -72,10 +72,14 @@ public function onBuildBefore(BuildBefore $event) $organization->getId() ); } - $where = array_merge( - $where, - ['u.id in (' . implode(', ', $resultBuIds) . ')'] - ); + if (count($resultBuIds)) { + $where = array_merge( + $where, + ['u.id in (' . implode(', ', $resultBuIds) . ')'] + ); + } else { + $where = array_merge($where, ['1 = 0']); + } } if (count($where)) { $config->offsetSetByPath('[source][query][where][and]', $where); From 25e0c06deb43d232d354ce9eafa597c456dd391d Mon Sep 17 00:00:00 2001 From: Hryhorii Hrebiniuk Date: Fri, 25 Dec 2015 18:07:22 +0200 Subject: [PATCH 289/471] BAP-9553 Grids and debug toolbar fix --- .../Resources/public/css/less/dashboard.less | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less b/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less index 9a0c9f4b10d..6dd7f8d65dc 100644 --- a/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less +++ b/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less @@ -577,4 +577,14 @@ } #dashboard_selector, #uniform-dashboard_selector { height: 28px; + &.selector { + line-height: 28px; + span { + height: 28px; + line-height: 28px; + &:after { + margin: 1px 2px 0 0; + } + } + } } From ec25413d5208e13228b0f94b5ddb395bcad92490 Mon Sep 17 00:00:00 2001 From: Denis Voronin Date: Fri, 25 Dec 2015 18:56:37 +0200 Subject: [PATCH 290/471] BAP-9114: Demo fixes - Change confirmation messages. - Hide "Yes, Delete" confirmation button in case when deleted entities count is 0. --- .../js/datagrid/action/delete-mass-action.js | 34 ++++++++++++++++-- .../Resources/translations/jsmessages.en.yml | 10 +++--- .../public/js/delete-confirmation.js | 36 ++++++++++++++++++- 3 files changed, 73 insertions(+), 7 deletions(-) diff --git a/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/action/delete-mass-action.js b/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/action/delete-mass-action.js index abf5a587915..030e409ccc3 100644 --- a/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/action/delete-mass-action.js +++ b/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/action/delete-mass-action.js @@ -34,12 +34,16 @@ define([ confirmMessages: { selected_message: 'oro.datagrid.mass_action.delete.selected_message', max_limit_message: 'oro.datagrid.mass_action.delete.max_limit_message', - restricted_access_message: 'oro.datagrid.mass_action.delete.restricted_access_message' + restricted_access_message: 'oro.datagrid.mass_action.delete.restricted_access_message', + restricted_access_empty_message: 'oro.datagrid.mass_action.delete.restricted_access_empty_message' }, /** @property {String} */ confirmMessage: null, + /** @property {Boolean} */ + allowOk: true, + /** * As in this action we need to send POST request to get data for confirm message * we set this.confirmation = false at initialization to prevent opening confirm window. @@ -52,6 +56,28 @@ define([ this.confirmation = false; }, + /** + * Get view for confirm modal + * + * @return {oroui.Modal} + */ + getConfirmDialog: function(callback) { + if (!this.confirmModal) { + this.confirmModal = (new this.confirmModalConstructor({ + title: __(this.messages.confirm_title), + content: this.getConfirmContentMessage(), + okText: __(this.messages.confirm_ok), + cancelText: __(this.messages.confirm_cancel), + allowOk: this.allowOk + })); + this.listenTo(this.confirmModal, 'ok', callback); + + this.subviews.push(this.confirmModal); + } + return this.confirmModal; + }, + + /** * Need to handle POST and DELETE requests differently. * @@ -100,8 +126,12 @@ define([ * @param data */ setConfirmMessage: function(data) { + this.allowOk = true; if (this.isDefined(data.selected) && this.isDefined(data.deletable) && this.isDefined(data.max_limit)) { - if (data.deletable <= data.max_limit) { + if (data.deletable === 0) { + this.confirmMessage = __(this.confirmMessages.restricted_access_empty_message); + this.allowOk = false; + } else if (data.deletable <= data.max_limit) { if (data.deletable >= data.selected) { this.confirmMessage = __(this.confirmMessages.selected_message, {selected: data.selected}); } else { diff --git a/src/Oro/Bundle/DataGridBundle/Resources/translations/jsmessages.en.yml b/src/Oro/Bundle/DataGridBundle/Resources/translations/jsmessages.en.yml index 95bf6ed2357..8e23c747147 100644 --- a/src/Oro/Bundle/DataGridBundle/Resources/translations/jsmessages.en.yml +++ b/src/Oro/Bundle/DataGridBundle/Resources/translations/jsmessages.en.yml @@ -87,10 +87,12 @@ oro: delete: selected_message: > You have selected {{ selected }} records.
            - Are you sure you want to delete {{ selected }} records? + Are you sure you want to delete them? max_limit_message: > - Due to performance limitations the maximum amount of records to be deleted at once is {{ max_limit }}.
            + The maximum number of records that can be deleted at once is {{ max_limit }}.
            Are you sure you want to delete first {{ max_limit }} records in the selection? restricted_access_message: > - You have permissions to delete {{ deletable }} records out of {{ selected }} selected.
            - Are you sure you want to delete {{ deletable }} records? + You have permissions to delete {{ deletable }} records out of {{ selected }} selected.
            + Are you sure you want to delete them? + restricted_access_empty_message: > + You don't have permissions to delete any of the selected records. diff --git a/src/Oro/Bundle/UIBundle/Resources/public/js/delete-confirmation.js b/src/Oro/Bundle/UIBundle/Resources/public/js/delete-confirmation.js index 22cdfd58a8d..908cf544063 100644 --- a/src/Oro/Bundle/UIBundle/Resources/public/js/delete-confirmation.js +++ b/src/Oro/Bundle/UIBundle/Resources/public/js/delete-confirmation.js @@ -10,20 +10,54 @@ define(['underscore', 'orotranslation/js/translator', 'oroui/js/modal' * @extends oroui.Modal */ return Modal.extend({ + + /** @property {String} */ + template: '\ + <% if (title) { %>\ + \ + <% } %>\ + \ + \ + ', /** @property {String} */ className: 'modal oro-modal-danger', /** @property {String} */ okButtonClass: 'btn-danger', + /** @property {Boolean} */ + allowOk: true, + /** * @param {Object} options */ initialize: function(options) { + //Set custom template settings + var interpolate = { + interpolate: /\{\{(.+?)\}\}/g, + evaluate: /<%([\s\S]+?)%>/g + }; + options = _.extend({ title: __('Delete Confirmation'), okText: __('Yes, Delete'), - cancelText: __('Cancel') + cancelText: __('Cancel'), + template: _.template(this.template, interpolate), + allowOk: this.allowOk }, options); arguments[0] = options; From bf4670d83d93f1628f52f7c74b9497d8a1f04768 Mon Sep 17 00:00:00 2001 From: Hryhorii Hrebiniuk Date: Fri, 25 Dec 2015 19:55:03 +0200 Subject: [PATCH 291/471] BAP-9582: Dashboard widget header fix --- .../Resources/public/css/less/dashboard.less | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less b/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less index ddb38697fe8..0c60b55105c 100644 --- a/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less +++ b/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less @@ -115,6 +115,11 @@ white-space: nowrap; .action-wrapper { border-right: 1px solid #ddd; + padding: 3px 13px; + } + } + .default-actions-container { + .action-wrapper { padding: 0 3px; > a { padding: 10px 8px; @@ -132,6 +137,10 @@ margin: 10px -1px 0 0; } + .dashboard-btn { + margin-top: -3px; + } + .move-action { cursor: move; } From faf0086d81b19a5170d0dc5db7ff4ab205a5df86 Mon Sep 17 00:00:00 2001 From: Denis Voronin Date: Fri, 25 Dec 2015 20:33:38 +0200 Subject: [PATCH 292/471] BAP-9578: Detect and set default grid view - Review fixes --- .../DataGridBundle/Entity/Repository/GridViewRepository.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Oro/Bundle/DataGridBundle/Entity/Repository/GridViewRepository.php b/src/Oro/Bundle/DataGridBundle/Entity/Repository/GridViewRepository.php index 8383af2851e..c9471a3e48b 100644 --- a/src/Oro/Bundle/DataGridBundle/Entity/Repository/GridViewRepository.php +++ b/src/Oro/Bundle/DataGridBundle/Entity/Repository/GridViewRepository.php @@ -50,6 +50,7 @@ public function findDefaultGridView($gridName, User $user) ->join('gv.users', 'user') ->where('gv.gridName = :gridName') ->andWhere('user = :user') + ->setMaxResults(1) ->setParameters( [ 'gridName' => $gridName, From e5416338ee4f94da1d85f3e33109ed124865205d Mon Sep 17 00:00:00 2001 From: zebimax Date: Fri, 25 Dec 2015 21:27:20 +0200 Subject: [PATCH 293/471] BAP-9577: Add option 'Use as default' to views options - add api and use default js logic, need to fix __all__ label --- .../Api/Rest/GridViewController.php | 45 +++++++++++++++ .../Entity/Repository/GridViewRepository.php | 56 +++++++++++++++---- .../EventListener/GridViewsLoadListener.php | 2 +- .../GridViews/GridViewsExtension.php | 8 +-- .../public/js/datagrid/grid-views/view.js | 56 ++++++++++++++++++- 5 files changed, 149 insertions(+), 18 deletions(-) diff --git a/src/Oro/Bundle/DataGridBundle/Controller/Api/Rest/GridViewController.php b/src/Oro/Bundle/DataGridBundle/Controller/Api/Rest/GridViewController.php index efab9eacf64..8af8e4bdcb0 100644 --- a/src/Oro/Bundle/DataGridBundle/Controller/Api/Rest/GridViewController.php +++ b/src/Oro/Bundle/DataGridBundle/Controller/Api/Rest/GridViewController.php @@ -6,9 +6,11 @@ use FOS\RestBundle\Controller\Annotations\Post; use FOS\RestBundle\Controller\Annotations\Put; use FOS\RestBundle\Controller\Annotations as Rest; +use FOS\RestBundle\Util\Codes; use Nelmio\ApiDocBundle\Annotation\ApiDoc; +use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Exception\AccessDeniedException; @@ -16,6 +18,7 @@ use Oro\Bundle\SecurityBundle\Annotation\Acl; use Oro\Bundle\SecurityBundle\SecurityFacade; use Oro\Bundle\SoapBundle\Controller\Api\Rest\RestController; +use Oro\Bundle\DataGridBundle\Entity\Repository\GridViewRepository; /** * @Rest\NamePrefix("oro_datagrid_api_rest_gridview_") @@ -98,6 +101,48 @@ public function deleteAction($id) return $this->handleDeleteRequest($id); } + + /** + * @param int $id + * @param bool $default + * + * @return Response + * @Post( + * "/gridviews/{id}/setdefault/{default}", + * requirements={"id"="\d+", "default"="\d+"}, + * defaults={"default"=false} + *) + * @ApiDoc( + * description="Set/unset grid view as default for current user", + * resource=true, + * requirements={ + * {"name"="id", "dataType"="integer"}, + * {"name"="default", "dataType"="boolean"}, + * }, + * defaults={"default"="false"} + * ) + * @Acl( + * id="oro_datagrid_gridview_update", + * type="entity", + * class="OroDataGridBundle:GridView", + * permission="EDIT" + * ) + */ + public function setDefaultAction($id, $default = false) + { + /** @var GridView $gridView */ + $gridView = $this->getManager()->find($id); + if ($gridView->getType() === GridView::TYPE_PUBLIC) { + $this->checkEditPublicAccess($gridView); + } + /** @var GridViewRepository $repository */ + $repository = $this->getManager()->getRepository(); + $user = $this->getUser(); + $repository->setGridViewDefault($user, $gridView, $default); + + return new JsonResponse([], Codes::HTTP_OK); + } + /** * @param GridView $gridView * diff --git a/src/Oro/Bundle/DataGridBundle/Entity/Repository/GridViewRepository.php b/src/Oro/Bundle/DataGridBundle/Entity/Repository/GridViewRepository.php index d203c31f8b5..f98ca48c81b 100644 --- a/src/Oro/Bundle/DataGridBundle/Entity/Repository/GridViewRepository.php +++ b/src/Oro/Bundle/DataGridBundle/Entity/Repository/GridViewRepository.php @@ -2,10 +2,12 @@ namespace Oro\Bundle\DataGridBundle\Entity\Repository; -use Symfony\Component\Security\Core\User\UserInterface; - +use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\EntityRepository; +use Symfony\Component\Security\Core\User\UserInterface; + +use Oro\Bundle\UserBundle\Entity\User; use Oro\Bundle\DataGridBundle\Entity\GridView; use Oro\Bundle\SecurityBundle\ORM\Walker\AclHelper; @@ -38,13 +40,48 @@ public function findGridViews(AclHelper $aclHelper, UserInterface $user, $gridNa } /** - * @param AclHelper $aclHelper * @param UserInterface $user - * @param $gridName + * @param string $gridName + * + * @return GridView|null + */ + public function findDefaultGridView(UserInterface $user, $gridName) + { + $qb = $this->getFindDefaultGridViewQb($user, $gridName); + + return $qb->setMaxResults(1)->getQuery()->getOneOrNullResult(); + } + + /** + * @param User $user + * @param GridView $gridView + * @param bool $default + */ + public function setGridViewDefault(User $user, GridView $gridView, $default) + { + /** @var GridView[] $defaultGridViews */ + $defaultGridViews = $this + ->getFindDefaultGridViewQb($user, $gridView->getGridName()) + ->getQuery() + ->getResult(); + + foreach ($defaultGridViews as $view) { + $view->removeUser($user); + } + if ($default) { + $gridView->addUser($user); + } + + $this->getEntityManager()->flush(); + } + + /** + * @param UserInterface $user + * @param string $gridName * - * @return mixed + * @return QueryBuilder */ - public function findUserDefaultGridView(AclHelper $aclHelper, UserInterface $user, $gridName) + protected function getFindDefaultGridViewQb(UserInterface $user, $gridName) { $qb = $this->createQueryBuilder('gv'); $qb->innerJoin('gv.users', 'u') @@ -52,10 +89,9 @@ public function findUserDefaultGridView(AclHelper $aclHelper, UserInterface $use ->andWhere('u = :user') ->setParameters([ 'gridName' => $gridName, - 'user' => $user - ]) - ->setMaxResults(1); + 'user' => $user + ]); - return $aclHelper->apply($qb)->getOneOrNullResult(); + return $qb; } } diff --git a/src/Oro/Bundle/DataGridBundle/EventListener/GridViewsLoadListener.php b/src/Oro/Bundle/DataGridBundle/EventListener/GridViewsLoadListener.php index 311519eebfc..1e5dc1cdf4f 100644 --- a/src/Oro/Bundle/DataGridBundle/EventListener/GridViewsLoadListener.php +++ b/src/Oro/Bundle/DataGridBundle/EventListener/GridViewsLoadListener.php @@ -57,7 +57,7 @@ public function onViewsLoad(GridViewsLoadEvent $event) $gridViewRepository = $this->getGridViewRepository(); $gridViews = $gridViewRepository->findGridViews($this->aclHelper, $currentUser, $gridName); - $defaultGridView = $gridViewRepository->findUserDefaultGridView($this->aclHelper, $currentUser, $gridName); + $defaultGridView = $gridViewRepository->findDefaultGridView($currentUser, $gridName); if (!$gridViews) { return; } diff --git a/src/Oro/Bundle/DataGridBundle/Extension/GridViews/GridViewsExtension.php b/src/Oro/Bundle/DataGridBundle/Extension/GridViews/GridViewsExtension.php index d50e4a9b75f..88292cd49fc 100644 --- a/src/Oro/Bundle/DataGridBundle/Extension/GridViews/GridViewsExtension.php +++ b/src/Oro/Bundle/DataGridBundle/Extension/GridViews/GridViewsExtension.php @@ -85,9 +85,7 @@ public function visitMetadata(DatagridConfiguration $config, MetadataObject $dat 'value' => self::DEFAULT_VIEW_ID, ], ], - 'views' => [ - $systemAllView->getMetadata(), - ], + 'views' => [], ]; if ($list !== false) { $configuredGridViews = $list->getMetadata(); @@ -101,9 +99,11 @@ public function visitMetadata(DatagridConfiguration $config, MetadataObject $dat $this->eventDispatcher->dispatch(GridViewsLoadEvent::EVENT_NAME, $event); $gridViews = $event->getGridViews(); } - $systemAllView->setIsDefault($gridViews['default'] === null); + $systemAllView->setIsDefault(empty($gridViews['default'])); + unset($gridViews['default']); $gridViews['gridName'] = $config->getName(); $gridViews['permissions'] = $this->getPermissions(); + $gridViews['views'][] = $systemAllView->getMetadata(); $data->offsetAddToArray('gridViews', $gridViews); } diff --git a/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/grid-views/view.js b/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/grid-views/view.js index d951f224d54..41e47835452 100644 --- a/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/grid-views/view.js +++ b/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/grid-views/view.js @@ -6,8 +6,9 @@ define([ './model', './view-name-modal', 'oroui/js/mediator', - 'oroui/js/delete-confirmation' -], function(Backbone, _, __, GridViewsCollection, GridViewModel, ViewNameModal, mediator, DeleteConfirmation) { + 'oroui/js/delete-confirmation', + 'routing' +], function(Backbone, _, __, GridViewsCollection, GridViewModel, ViewNameModal, mediator, DeleteConfirmation, routing) { 'use strict'; var GridViewsView; @@ -376,8 +377,33 @@ define([ * @param {Event} e */ onUseAsDefault: function(e) { - var model = this._getCurrentViewModel(); var self = this; + var isDefault = 1; + var defaultModel = this._getCurrentDefaultViewModel(); + var currentViewModel = this._getCurrentViewModel(); + var id = currentViewModel.id; + if (this._isSystemView()) { + isDefault = 0; + id = defaultModel.id; + if (defaultModel.id === self.DEFAULT_GRID_VIEW_ID) { + return self._showFlashMessage('success', __('oro.datagrid.gridView.updated')); + } + } + + return $.post( + routing.generate('oro_datagrid_api_rest_gridview_set_default', { + id: id, + default: isDefault + }), + {}, + function(response) { + defaultModel.set('is_default', false); + currentViewModel.set('is_default', true); + mediator.trigger('datagrid:' + this.gridName + ':views:change', currentViewModel); + + self._showFlashMessage('success', __('oro.datagrid.gridView.updated')); + } + ); }, /** @@ -586,6 +612,30 @@ define([ }); }, + /** + * @private + * + * @returns {undefined|GridViewModel} + */ + _getCurrentDefaultViewModel: function() { + if (!this._hasActiveView()) { + return; + } + + return this.viewsCollection.findWhere({ + is_default: true + }); + }, + + /** + * @private + * + * @returns {boolean} + */ + _isSystemView: function() { + return this._getCurrentView().value === this.DEFAULT_GRID_VIEW_ID; + }, + /** * @private * From 0e4d46bd1ce8ce30a09817b7f227661a8b929409 Mon Sep 17 00:00:00 2001 From: Denis Voronin Date: Fri, 25 Dec 2015 21:34:06 +0200 Subject: [PATCH 294/471] BAP-9578: Detect and set default grid view - Fix code styles --- .../js/datagrid/action/delete-mass-action.js | 1 - .../public/js/delete-confirmation.js | 30 +++++-------------- .../public/templates/delete-confirmation.html | 19 ++++++++++++ 3 files changed, 26 insertions(+), 24 deletions(-) create mode 100644 src/Oro/Bundle/UIBundle/Resources/public/templates/delete-confirmation.html diff --git a/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/action/delete-mass-action.js b/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/action/delete-mass-action.js index 030e409ccc3..5533eb2e3ed 100644 --- a/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/action/delete-mass-action.js +++ b/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/action/delete-mass-action.js @@ -77,7 +77,6 @@ define([ return this.confirmModal; }, - /** * Need to handle POST and DELETE requests differently. * diff --git a/src/Oro/Bundle/UIBundle/Resources/public/js/delete-confirmation.js b/src/Oro/Bundle/UIBundle/Resources/public/js/delete-confirmation.js index 908cf544063..5b592d8f4da 100644 --- a/src/Oro/Bundle/UIBundle/Resources/public/js/delete-confirmation.js +++ b/src/Oro/Bundle/UIBundle/Resources/public/js/delete-confirmation.js @@ -1,7 +1,10 @@ -define(['underscore', 'orotranslation/js/translator', 'oroui/js/modal' - ], function(_, __, Modal) { +define(function(require) { 'use strict'; + var _ = require('underscore'); + var __ = require('orotranslation/js/translator'); + var Modal = require('oroui/js/modal'); + /** * Delete confirmation dialog * @@ -12,27 +15,8 @@ define(['underscore', 'orotranslation/js/translator', 'oroui/js/modal' return Modal.extend({ /** @property {String} */ - template: '\ - <% if (title) { %>\ - \ - <% } %>\ - \ - \ - ', + template: require('text!oroui/templates/delete-confirmation.html'), + /** @property {String} */ className: 'modal oro-modal-danger', diff --git a/src/Oro/Bundle/UIBundle/Resources/public/templates/delete-confirmation.html b/src/Oro/Bundle/UIBundle/Resources/public/templates/delete-confirmation.html new file mode 100644 index 00000000000..bb08ee0ca60 --- /dev/null +++ b/src/Oro/Bundle/UIBundle/Resources/public/templates/delete-confirmation.html @@ -0,0 +1,19 @@ +<% if (title) { %> + +<% } %> + + From 31fab93d6c134c8eed2bcb55b2cff4c25c8d949d Mon Sep 17 00:00:00 2001 From: dmitrosh Date: Fri, 25 Dec 2015 22:52:49 +0200 Subject: [PATCH 295/471] CRM-4318: implement up down buttons for mobile version instead dnd --- .../Resources/public/css/less/dashboard.less | 11 ++++- .../Resources/public/js/items/view.js | 43 ++++++++++++++++--- .../Resources/translations/messages.en.yml | 2 + .../Resources/views/Js/items.html.twig | 9 ++++ .../public/js/items-manager/table.js | 7 ++- 5 files changed, 65 insertions(+), 7 deletions(-) diff --git a/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less b/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less index 9a0c9f4b10d..fbb575ab012 100644 --- a/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less +++ b/src/Oro/Bundle/DashboardBundle/Resources/public/css/less/dashboard.less @@ -570,9 +570,18 @@ margin-bottom: 10px; .controls { width: 445px; - .mobile-version & { + } + .mobile-version & { + .controls { width: initial; } + .action-column { + width: 110px; + text-align: center; + } + .action-cell .btn { + padding: 0 4px; + } } } #dashboard_selector, #uniform-dashboard_selector { diff --git a/src/Oro/Bundle/DashboardBundle/Resources/public/js/items/view.js b/src/Oro/Bundle/DashboardBundle/Resources/public/js/items/view.js index 91bdba5d69f..2d64c41c9f2 100644 --- a/src/Oro/Bundle/DashboardBundle/Resources/public/js/items/view.js +++ b/src/Oro/Bundle/DashboardBundle/Resources/public/js/items/view.js @@ -88,13 +88,20 @@ define([ }, _initializeItemGrid: function(items) { + var comparator = function(model) { + return model.get('order'); + }; var $itemContainer = this.$('.item-container'); - var showedItems = items.where({show: true}); - var filteredItems = new ItemCollection(showedItems); + var showedItems = items.where({show: true}).map(function(model) { + return model.toJSON(); + }); + var filteredItems = new ItemCollection(showedItems, {comparator: comparator}); $itemContainer.itemsManagerTable({ itemTemplate: Backbone.$(this.itemTplSelector).html(), - collection: filteredItems + collection: filteredItems, + moveUpHandler: _.bind(this.moveUpHandler, this), + moveDownHandler: _.bind(this.moveDownHandler, this) }); filteredItems.on('sort add', function() { @@ -118,10 +125,13 @@ define([ }); $itemContainer.on('change', function(e) { + var value; var $target = Backbone.$(e.target); var item = items.get($target.closest('tr').data('cid')); - var value = $target.is(':checkbox') ? $target.is(':checked') : $target.val(); - item.set($target.data('name'), value); + if (item) { + value = $target.is(':checkbox') ? $target.is(':checked') : $target.val(); + item.set($target.data('name'), value); + } }); }, @@ -148,6 +158,29 @@ define([ } else { this.$('.add-button').addClass('disabled'); } + }, + + moveUpHandler: function(model) { + this._moveModel(model, -1); + }, + + moveDownHandler: function(model) { + this._moveModel(model, +1); + }, + + _moveModel: function(model, shift) { + var order; + var targetModel; + var collection = model.collection; + var targetIndex = collection.indexOf(model) + shift; + if (targetIndex >= 0 && targetIndex < collection.length) { + targetModel = collection.at(targetIndex); + order = model.get('order'); + model.set('order', targetModel.get('order')); + targetModel.set('order', order); + collection.sort(); + collection.trigger('reset'); + } } }); }); diff --git a/src/Oro/Bundle/DashboardBundle/Resources/translations/messages.en.yml b/src/Oro/Bundle/DashboardBundle/Resources/translations/messages.en.yml index 95ee268ec80..55d48aac1b9 100644 --- a/src/Oro/Bundle/DashboardBundle/Resources/translations/messages.en.yml +++ b/src/Oro/Bundle/DashboardBundle/Resources/translations/messages.en.yml @@ -77,6 +77,8 @@ oro: actions: Actions actions: move_column.label: Move column + move_column_up.label: Move column up + move_column_down.label: Move column down add.label: Add add_all.label: Add all delete.label: Delete column diff --git a/src/Oro/Bundle/DashboardBundle/Resources/views/Js/items.html.twig b/src/Oro/Bundle/DashboardBundle/Resources/views/Js/items.html.twig index 64c27aab57e..55388a75f38 100644 --- a/src/Oro/Bundle/DashboardBundle/Resources/views/Js/items.html.twig +++ b/src/Oro/Bundle/DashboardBundle/Resources/views/Js/items.html.twig @@ -21,9 +21,18 @@ data-collection-action="delete"> + <% if (!_.isMobile()) { %> + <% } else { %> + + + + + + + <% } %> diff --git a/src/Oro/Bundle/UIBundle/Resources/public/js/items-manager/table.js b/src/Oro/Bundle/UIBundle/Resources/public/js/items-manager/table.js index e24814fc6d6..1f50486e1a1 100644 --- a/src/Oro/Bundle/UIBundle/Resources/public/js/items-manager/table.js +++ b/src/Oro/Bundle/UIBundle/Resources/public/js/items-manager/table.js @@ -161,7 +161,12 @@ define(['jquery', 'underscore', 'oroui/js/mediator', 'jquery-ui'], function($, _ }, _renderModel: function(model) { - var data = _.extend({cid: model.cid}, model.toJSON()); + var index = model.collection.indexOf(model); + var data = _.extend({ + cid: model.cid, + isFirst: index === 0, + isLast: index === model.collection.length -1 + }, model.toJSON()); return this._itemRender(this.itemTemplate, data); }, From 98e0e190c4c2527dcf1fdfab76d14c00b4fcca7b Mon Sep 17 00:00:00 2001 From: Sergey Zhuravel Date: Mon, 28 Dec 2015 11:28:12 +0200 Subject: [PATCH 296/471] BB-1539: 500 error on frontend api/rest/latest/windows request --- src/Oro/Bundle/WindowsBundle/Manager/WindowsStateManager.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Oro/Bundle/WindowsBundle/Manager/WindowsStateManager.php b/src/Oro/Bundle/WindowsBundle/Manager/WindowsStateManager.php index 6c9a54e5e2c..35a8f61f3ef 100644 --- a/src/Oro/Bundle/WindowsBundle/Manager/WindowsStateManager.php +++ b/src/Oro/Bundle/WindowsBundle/Manager/WindowsStateManager.php @@ -144,7 +144,9 @@ protected function getUser() return $user; } - /** @return bool */ + /** + * @return bool + */ public function isApplicable() { try { From f6d1fca86cb1c9e1c4395f033d34c8e00286da49 Mon Sep 17 00:00:00 2001 From: Yevhen Shyshkin Date: Mon, 28 Dec 2015 12:39:08 +0100 Subject: [PATCH 297/471] OC-630: Refactored several items in transition-actions.md - review fixes --- .../workflow/workflow-entities/transition-actions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Oro/Bundle/WorkflowBundle/Resources/doc/reference/workflow/workflow-entities/transition-actions.md b/src/Oro/Bundle/WorkflowBundle/Resources/doc/reference/workflow/workflow-entities/transition-actions.md index d652e3e7eb0..0177c00109d 100644 --- a/src/Oro/Bundle/WorkflowBundle/Resources/doc/reference/workflow/workflow-entities/transition-actions.md +++ b/src/Oro/Bundle/WorkflowBundle/Resources/doc/reference/workflow/workflow-entities/transition-actions.md @@ -398,7 +398,7 @@ Call Method conditions: # optional condition configuration parameters: - attribute: $.leadContactAddAddress + attribute: $.result.addressResult object: $lead.contact method: addAddress method_parameters: [$.result.address] @@ -406,7 +406,7 @@ Call Method OR - @call_method: # add Address to Contact - attribute: $.leadContactAddAddress + attribute: $.result.addressResult object: $lead.contact method: addAddress method_parameters: [$.result.address] From 40afe8768fc352a0429f19ae6fc014613a393459 Mon Sep 17 00:00:00 2001 From: Hryhorii Hrebiniuk Date: Mon, 28 Dec 2015 13:53:15 +0200 Subject: [PATCH 298/471] BAP-9553 Mobile page header fixes --- .../Resources/public/css/less/activity-list.less | 2 +- .../Resources/public/css/less/mobile/layout.less | 15 --------------- .../public/css/less/mobile/page-header.less | 10 +++------- 3 files changed, 4 insertions(+), 23 deletions(-) diff --git a/src/Oro/Bundle/ActivityListBundle/Resources/public/css/less/activity-list.less b/src/Oro/Bundle/ActivityListBundle/Resources/public/css/less/activity-list.less index 9e5b65c28d5..749770af82f 100644 --- a/src/Oro/Bundle/ActivityListBundle/Resources/public/css/less/activity-list.less +++ b/src/Oro/Bundle/ActivityListBundle/Resources/public/css/less/activity-list.less @@ -220,7 +220,7 @@ .accordion > .items { .message-item { clear: left; - margin-left: 4px; + margin: 7px 0 7px 4px; width: calc(~"100% - 186px"); } .details { diff --git a/src/Oro/Bundle/UIBundle/Resources/public/css/less/mobile/layout.less b/src/Oro/Bundle/UIBundle/Resources/public/css/less/mobile/layout.less index e93b99f973b..e2a9c2eaad7 100644 --- a/src/Oro/Bundle/UIBundle/Resources/public/css/less/mobile/layout.less +++ b/src/Oro/Bundle/UIBundle/Resources/public/css/less/mobile/layout.less @@ -186,18 +186,3 @@ position: fixed; bottom: 0; } - -/* START: moved from oroui/mobile/dashboard.less */ -.box-type1 { - margin: 0 @contentPadding @contentPadding; - /*border: 1px solid #b6b6b6;*/ - .border-radius(3px); - .title{ - /*padding: @contentPadding @contentPadding @contentPadding 0;*/ - padding: @contentPadding; - } - .calendar-container { - padding: 0; - } -} -/* END: moved from oroui/mobile/dashboard.less */ diff --git a/src/Oro/Bundle/UIBundle/Resources/public/css/less/mobile/page-header.less b/src/Oro/Bundle/UIBundle/Resources/public/css/less/mobile/page-header.less index 2cf6ea074ad..47f41d3d24b 100644 --- a/src/Oro/Bundle/UIBundle/Resources/public/css/less/mobile/page-header.less +++ b/src/Oro/Bundle/UIBundle/Resources/public/css/less/mobile/page-header.less @@ -10,10 +10,6 @@ padding-right: 0; padding-left: 0; } -.page-title h1 { - line-height: @fontSizeLarge * 2; - margin: 0; -} .navbar-extra { .container-fluid.page-title; & > .row > [class^=span] { @@ -55,11 +51,11 @@ } } -.customer-info { +.page-title { h1 { font-size: @baseFontSize * 1.75; - margin: 5px 0 10px; - line-height: @baseFontSize * 2; + margin: 0; + line-height: @baseFontSize * 2.5; display: block; } .sub-title { From 85eaebc54e2dc5e49e088a9ca865b8df852747af Mon Sep 17 00:00:00 2001 From: zebimax Date: Mon, 28 Dec 2015 15:29:16 +0200 Subject: [PATCH 299/471] BAP-9577:Add option 'Use as default' to views options - rename relations table - set view permission to set default grid - add fix to save specific views labels --- .../Api/Rest/GridViewController.php | 10 +++------ .../Bundle/DataGridBundle/Entity/GridView.php | 2 +- .../EventListener/GridViewsLoadListener.php | 22 ++++++++++++------- .../v1_2/DefaultGridViewUsersRelation.php | 4 ++-- .../public/js/datagrid/grid-views/view.js | 16 ++++++-------- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/Oro/Bundle/DataGridBundle/Controller/Api/Rest/GridViewController.php b/src/Oro/Bundle/DataGridBundle/Controller/Api/Rest/GridViewController.php index 8af8e4bdcb0..b3c25839909 100644 --- a/src/Oro/Bundle/DataGridBundle/Controller/Api/Rest/GridViewController.php +++ b/src/Oro/Bundle/DataGridBundle/Controller/Api/Rest/GridViewController.php @@ -122,23 +122,19 @@ public function deleteAction($id) * defaults={"default"="false"} * ) * @Acl( - * id="oro_datagrid_gridview_update", + * id="oro_datagrid_gridview_view", * type="entity", * class="OroDataGridBundle:GridView", - * permission="EDIT" + * permission="VIEW" * ) */ public function setDefaultAction($id, $default = false) { /** @var GridView $gridView */ $gridView = $this->getManager()->find($id); - if ($gridView->getType() === GridView::TYPE_PUBLIC) { - $this->checkEditPublicAccess($gridView); - } /** @var GridViewRepository $repository */ $repository = $this->getManager()->getRepository(); - $user = $this->getUser(); - $repository->setGridViewDefault($user, $gridView, $default); + $repository->setGridViewDefault($this->getUser(), $gridView, $default); return new JsonResponse([], Codes::HTTP_OK); } diff --git a/src/Oro/Bundle/DataGridBundle/Entity/GridView.php b/src/Oro/Bundle/DataGridBundle/Entity/GridView.php index e16a9f38c6d..89f051fd969 100644 --- a/src/Oro/Bundle/DataGridBundle/Entity/GridView.php +++ b/src/Oro/Bundle/DataGridBundle/Entity/GridView.php @@ -128,7 +128,7 @@ class GridView * @ORM\ManyToMany( * targetEntity="Oro\Bundle\UserBundle\Entity\User" * ) - * @ORM\JoinTable(name="oro_default_grid_view_users", + * @ORM\JoinTable(name="oro_grid_view_users", * joinColumns={@ORM\JoinColumn(name="grid_view_id", referencedColumnName="id", onDelete="CASCADE")}, * inverseJoinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id", onDelete="CASCADE")}, * ) diff --git a/src/Oro/Bundle/DataGridBundle/EventListener/GridViewsLoadListener.php b/src/Oro/Bundle/DataGridBundle/EventListener/GridViewsLoadListener.php index 1e5dc1cdf4f..da729013384 100644 --- a/src/Oro/Bundle/DataGridBundle/EventListener/GridViewsLoadListener.php +++ b/src/Oro/Bundle/DataGridBundle/EventListener/GridViewsLoadListener.php @@ -49,7 +49,7 @@ public function __construct( */ public function onViewsLoad(GridViewsLoadEvent $event) { - $gridName = $event->getGridName(); + $gridName = $event->getGridName(); $currentUser = $this->getCurrentUser(); if (!$currentUser) { return; @@ -62,24 +62,30 @@ public function onViewsLoad(GridViewsLoadEvent $event) return; } $choices = []; - $views = []; + $views = []; + $default = null; foreach ($gridViews as $gridView) { + $isDefault = false; + if ($defaultGridView === $gridView) { + $isDefault = true; + $default = $gridView; + } $view = $gridView->createView(); $view->setEditable($this->securityFacade->isGranted('EDIT', $gridView)); $view->setDeletable($this->securityFacade->isGranted('DELETE', $gridView)); - $view->setIsDefault($defaultGridView === $gridView); - $views[] = $view->getMetadata(); + $view->setIsDefault($isDefault); + $views[] = $view->getMetadata(); $choices[] = [ 'label' => $this->createGridViewLabel($currentUser, $gridView), 'value' => $gridView->getId(), ]; + } - $newGridViews = $event->getGridViews(); + $newGridViews = $event->getGridViews(); $newGridViews['choices'] = array_merge($newGridViews['choices'], $choices); - $newGridViews['views'] = array_merge($newGridViews['views'], $views); - $newGridViews['default'] = $defaultGridView; - + $newGridViews['views'] = array_merge($newGridViews['views'], $views); + $newGridViews['default'] = $default; $event->setGridViews($newGridViews); } diff --git a/src/Oro/Bundle/DataGridBundle/Migrations/Schema/v1_2/DefaultGridViewUsersRelation.php b/src/Oro/Bundle/DataGridBundle/Migrations/Schema/v1_2/DefaultGridViewUsersRelation.php index 588045784d6..dac38a28733 100644 --- a/src/Oro/Bundle/DataGridBundle/Migrations/Schema/v1_2/DefaultGridViewUsersRelation.php +++ b/src/Oro/Bundle/DataGridBundle/Migrations/Schema/v1_2/DefaultGridViewUsersRelation.php @@ -15,14 +15,14 @@ public function up(Schema $schema, QueryBag $queries) } /** - * Creates 'oro_default_grid_view_users' table which represents relationship between grid views and + * Creates 'oro_grid_view_users' table which represents relationship between grid views and * users who chosen this grid view as default. * * @param Schema $schema */ public static function createOroDefaultGridViewUsersTable(Schema $schema) { - $table = $schema->createTable('oro_default_grid_view_users'); + $table = $schema->createTable('oro_grid_view_users'); $table->addColumn('grid_view_id', 'integer', []); $table->addColumn('user_id', 'integer', []); $table->setPrimaryKey(['grid_view_id', 'user_id']); diff --git a/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/grid-views/view.js b/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/grid-views/view.js index 41e47835452..9a336c17a5e 100644 --- a/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/grid-views/view.js +++ b/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/grid-views/view.js @@ -382,14 +382,11 @@ define([ var defaultModel = this._getCurrentDefaultViewModel(); var currentViewModel = this._getCurrentViewModel(); var id = currentViewModel.id; - if (this._isSystemView()) { + if (this._isCurrentViewSystem()) { + // in this case we need to set default to false on current default view isDefault = 0; id = defaultModel.id; - if (defaultModel.id === self.DEFAULT_GRID_VIEW_ID) { - return self._showFlashMessage('success', __('oro.datagrid.gridView.updated')); - } } - return $.post( routing.generate('oro_datagrid_api_rest_gridview_set_default', { id: id, @@ -397,10 +394,11 @@ define([ }), {}, function(response) { - defaultModel.set('is_default', false); - currentViewModel.set('is_default', true); - mediator.trigger('datagrid:' + this.gridName + ':views:change', currentViewModel); + // TODO how we can remove this fix with labels? + // for now we need to get label from choices to save specifically built labels(shared, system) + defaultModel.set({is_default:false, label: self._getView(defaultModel.get('name')).label}); + currentViewModel.set({is_default:true, label: self._getView(currentViewModel.get('name')).label}); self._showFlashMessage('success', __('oro.datagrid.gridView.updated')); } ); @@ -632,7 +630,7 @@ define([ * * @returns {boolean} */ - _isSystemView: function() { + _isCurrentViewSystem: function() { return this._getCurrentView().value === this.DEFAULT_GRID_VIEW_ID; }, From 684882b8505f1951bc4d8c8ce9dcf2a7b9d14131 Mon Sep 17 00:00:00 2001 From: Ivan Shirko Date: Mon, 28 Dec 2015 16:59:15 +0300 Subject: [PATCH 300/471] BB-1777: Action Improvements: - small changes for ConfigurationMerger --- .../Component/Config/Merger/ConfigurationMerger.php | 12 ++++-------- src/Oro/Component/Config/README.md | 2 +- .../Config/Resources/doc/configuration_merger.md | 2 +- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/Oro/Component/Config/Merger/ConfigurationMerger.php b/src/Oro/Component/Config/Merger/ConfigurationMerger.php index a244eb00ff3..2778124464b 100644 --- a/src/Oro/Component/Config/Merger/ConfigurationMerger.php +++ b/src/Oro/Component/Config/Merger/ConfigurationMerger.php @@ -100,14 +100,10 @@ protected function merge(array $data, array $config) if (is_int($key)) { $data[] = $value; } else { - if (!array_key_exists($key, $data)) { - $data[$key] = $value; + if (array_key_exists($key, $data) && is_array($value)) { + $data[$key] = $this->merge($data[$key], $value); } else { - if (is_array($value)) { - $data[$key] = $this->merge($data[$key], $value); - } else { - $data[$key] = $value; - } + $data[$key] = $value; } } } @@ -128,7 +124,7 @@ protected function resolveExtends(array $configs, $actionName) } $config = $configs[$actionName]; - if (!array_key_exists(self::EXTENDS_NODE_NAME, $config) || empty($config[self::EXTENDS_NODE_NAME])) { + if (empty($config[self::EXTENDS_NODE_NAME])) { return $config; } diff --git a/src/Oro/Component/Config/README.md b/src/Oro/Component/Config/README.md index a662464ac3b..79344a5b537 100644 --- a/src/Oro/Component/Config/README.md +++ b/src/Oro/Component/Config/README.md @@ -11,7 +11,7 @@ Resource Types Resource Merge -------------- - - [Configuration Merger](./Resources/doc/configuration_merger.md) provides a way to merge configurations of some resource both from one or many bundles. Supports two strategy: replace and append. + - [Configuration Merger](./Resources/doc/configuration_merger.md) provides a way to merge configurations of some resource both from one or many bundles. Supports two strategies: replace and append. System Aware Resolver diff --git a/src/Oro/Component/Config/Resources/doc/configuration_merger.md b/src/Oro/Component/Config/Resources/doc/configuration_merger.md index f251b58c4b2..09ed9f5a0a5 100644 --- a/src/Oro/Component/Config/Resources/doc/configuration_merger.md +++ b/src/Oro/Component/Config/Resources/doc/configuration_merger.md @@ -9,7 +9,7 @@ nodes, which you want to replace, on the same level of this nodes. Initialization -------------- -For creating new instance of merger you need list of some keys. It will use as sorting order for merging all +For creating new instance of merger you need list of some keys. It will be used as sorting order for merging all configuration from groups which have equal name. ``` php From 99dcef06b918905297140dbbad54c4ec7feb079f Mon Sep 17 00:00:00 2001 From: Mykhailo Kudelia Date: Mon, 28 Dec 2015 17:01:33 +0200 Subject: [PATCH 301/471] OEE-784: Wrong ACL condition to view BU in the Second Org --- src/Oro/Bundle/SecurityBundle/Owner/OwnerTreeProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Oro/Bundle/SecurityBundle/Owner/OwnerTreeProvider.php b/src/Oro/Bundle/SecurityBundle/Owner/OwnerTreeProvider.php index 914b656f5ee..e2398a942c3 100644 --- a/src/Oro/Bundle/SecurityBundle/Owner/OwnerTreeProvider.php +++ b/src/Oro/Bundle/SecurityBundle/Owner/OwnerTreeProvider.php @@ -106,7 +106,7 @@ protected function fillTree(OwnerTreeInterface $tree) foreach ($businessUnits as $businessUnit) { if (!empty($businessUnit['organization'])) { - $tree->addLocalEntity($businessUnit['id'], $businessUnit['organization']); + $tree->addLocalEntity($businessUnit['id'], (int)$businessUnit['organization']); if ($businessUnit['owner']) { $tree->addDeepEntity($businessUnit['id'], $businessUnit['owner']); } From 191ce113562e04a3f8ed7b7632c603a29359908b Mon Sep 17 00:00:00 2001 From: Hryhorii Hrebiniuk Date: Mon, 28 Dec 2015 17:56:52 +0200 Subject: [PATCH 302/471] BAP-9553 Emails markup on mobile --- .../Resources/config/assets.yml | 2 +- .../Resources/public/css/less/main.less | 4 ++ .../public/css/less/mobile/activity-list.less | 21 +++++++ .../public/css/less/mobile/main.less | 4 ++ .../ActivityList/widget/activities.html.twig | 2 +- .../public/css/less/mobile/oro.grid.less | 8 +++ .../EmailBundle/Resources/config/assets.yml | 2 +- .../Resources/public/css/less/main.less | 9 +++ .../public/css/less/mobile/main.less | 4 ++ .../public/css/less/mobile/thread-view.less | 56 +++++++++++++++++++ .../Resources/public/css/less/style.less | 23 -------- .../public/css/less/mobile/filter.less | 6 +- .../public/css/less/mobile/layout.less | 4 +- 13 files changed, 116 insertions(+), 29 deletions(-) create mode 100644 src/Oro/Bundle/ActivityListBundle/Resources/public/css/less/main.less create mode 100644 src/Oro/Bundle/ActivityListBundle/Resources/public/css/less/mobile/activity-list.less create mode 100644 src/Oro/Bundle/ActivityListBundle/Resources/public/css/less/mobile/main.less create mode 100644 src/Oro/Bundle/EmailBundle/Resources/public/css/less/main.less create mode 100644 src/Oro/Bundle/EmailBundle/Resources/public/css/less/mobile/main.less create mode 100644 src/Oro/Bundle/EmailBundle/Resources/public/css/less/mobile/thread-view.less diff --git a/src/Oro/Bundle/ActivityListBundle/Resources/config/assets.yml b/src/Oro/Bundle/ActivityListBundle/Resources/config/assets.yml index 7831d3d2c3b..a3cc25040ef 100644 --- a/src/Oro/Bundle/ActivityListBundle/Resources/config/assets.yml +++ b/src/Oro/Bundle/ActivityListBundle/Resources/config/assets.yml @@ -1,3 +1,3 @@ css: oroactivitylist: - - 'bundles/oroactivitylist/css/less/activity-list.less' + - 'bundles/oroactivitylist/css/less/main.less' diff --git a/src/Oro/Bundle/ActivityListBundle/Resources/public/css/less/main.less b/src/Oro/Bundle/ActivityListBundle/Resources/public/css/less/main.less new file mode 100644 index 00000000000..e70f1ca72b7 --- /dev/null +++ b/src/Oro/Bundle/ActivityListBundle/Resources/public/css/less/main.less @@ -0,0 +1,4 @@ +@import "./activity-list"; + +// mobile +@import "./mobile/main"; diff --git a/src/Oro/Bundle/ActivityListBundle/Resources/public/css/less/mobile/activity-list.less b/src/Oro/Bundle/ActivityListBundle/Resources/public/css/less/mobile/activity-list.less new file mode 100644 index 00000000000..93333ccb0c5 --- /dev/null +++ b/src/Oro/Bundle/ActivityListBundle/Resources/public/css/less/mobile/activity-list.less @@ -0,0 +1,21 @@ +.activity-list-widget { + .activity-context-activity-list { + padding: 0 @contentPadding; + } + .activity-list { + .info { + margin-bottom: @contentPadding; + } + } + .grid-toolbar { + padding: 0; + margin-bottom: @contentPadding; + .filter-container { + padding: 0; + margin-bottom: @contentPadding; + } + } + .comments-view-footer { + margin-bottom: 0; + } +} diff --git a/src/Oro/Bundle/ActivityListBundle/Resources/public/css/less/mobile/main.less b/src/Oro/Bundle/ActivityListBundle/Resources/public/css/less/mobile/main.less new file mode 100644 index 00000000000..4e3b4d898b3 --- /dev/null +++ b/src/Oro/Bundle/ActivityListBundle/Resources/public/css/less/mobile/main.less @@ -0,0 +1,4 @@ +.mobile-version { + @import "oroui/css/less/mobile/variables"; + @import "./activity-list"; +} diff --git a/src/Oro/Bundle/ActivityListBundle/Resources/views/ActivityList/widget/activities.html.twig b/src/Oro/Bundle/ActivityListBundle/Resources/views/ActivityList/widget/activities.html.twig index 9297614e60a..cf0bfa1cee3 100644 --- a/src/Oro/Bundle/ActivityListBundle/Resources/views/ActivityList/widget/activities.html.twig +++ b/src/Oro/Bundle/ActivityListBundle/Resources/views/ActivityList/widget/activities.html.twig @@ -20,7 +20,7 @@
          • -
            +
            {{ UI.clientLink({ 'aCss': 'action btn', 'iCss': 'icon-refresh', diff --git a/src/Oro/Bundle/DataGridBundle/Resources/public/css/less/mobile/oro.grid.less b/src/Oro/Bundle/DataGridBundle/Resources/public/css/less/mobile/oro.grid.less index 82ea344ea28..7a0c9e3907c 100644 --- a/src/Oro/Bundle/DataGridBundle/Resources/public/css/less/mobile/oro.grid.less +++ b/src/Oro/Bundle/DataGridBundle/Resources/public/css/less/mobile/oro.grid.less @@ -1,3 +1,6 @@ +.grid-views { + margin: 0; +} .grid-toolbar { padding: @contentPadding 10px; margin-bottom: -@contentPadding; @@ -71,6 +74,11 @@ .other-scroll-container { margin: 10px 10px 10px 10px; } +.responsive-cell { + .other-scroll-container { + margin: 10px 0; + } +} .grid-scrollable-container { -webkit-overflow-scrolling: touch; overflow-scrolling: touch; diff --git a/src/Oro/Bundle/EmailBundle/Resources/config/assets.yml b/src/Oro/Bundle/EmailBundle/Resources/config/assets.yml index 924e52487eb..44e0c8b46e4 100644 --- a/src/Oro/Bundle/EmailBundle/Resources/config/assets.yml +++ b/src/Oro/Bundle/EmailBundle/Resources/config/assets.yml @@ -1,3 +1,3 @@ css: 'oroemail': - - 'bundles/oroemail/css/less/style.less' + - 'bundles/oroemail/css/less/main.less' diff --git a/src/Oro/Bundle/EmailBundle/Resources/public/css/less/main.less b/src/Oro/Bundle/EmailBundle/Resources/public/css/less/main.less new file mode 100644 index 00000000000..0cb7af53631 --- /dev/null +++ b/src/Oro/Bundle/EmailBundle/Resources/public/css/less/main.less @@ -0,0 +1,9 @@ +@import "oroui/css/less/mixins"; +@import "./header"; +@import "./email-notification-menu"; +@import "./short-emails-list"; +@import "./sidebar-widget"; +@import "./style"; + +// mobile +@import "./mobile/main"; diff --git a/src/Oro/Bundle/EmailBundle/Resources/public/css/less/mobile/main.less b/src/Oro/Bundle/EmailBundle/Resources/public/css/less/mobile/main.less new file mode 100644 index 00000000000..f8ce0327713 --- /dev/null +++ b/src/Oro/Bundle/EmailBundle/Resources/public/css/less/mobile/main.less @@ -0,0 +1,4 @@ +.mobile-version { + @import "oroui/css/less/mobile/variables"; + @import "./thread-view"; +} diff --git a/src/Oro/Bundle/EmailBundle/Resources/public/css/less/mobile/thread-view.less b/src/Oro/Bundle/EmailBundle/Resources/public/css/less/mobile/thread-view.less new file mode 100644 index 00000000000..a056efd61ef --- /dev/null +++ b/src/Oro/Bundle/EmailBundle/Resources/public/css/less/mobile/thread-view.less @@ -0,0 +1,56 @@ +.thread-view { + padding-left: 0; + .email-info, .email-load-more { + margin: 0; + } + .email-info { + .responsive-cell { + padding: 0; + } + .email-full { + .email-sent-date { + padding-right: 0; + } + } + .email-view-toggle { + padding: @contentPadding; + } + .email-content { + padding: 0 @contentPadding @contentPadding; + } + &:only-child { + .email-view-toggle { + padding: 0 0 10px; + } + .email-content { + padding: 0; + } + } + } + .email-detailed-info-table { + .dropdown-menu { + padding: 0px @contentPadding; + } + .control-group:last-child { + margin-bottom: 0; + .controls { + margin-bottom: 0; + } + } + } +} + +.activity-list-widget { + .thread-view { + .email-info { + &:last-child, &:only-child { + .email-view-toggle { + padding: @contentPadding; + } + .email-content { + padding: 0 @contentPadding; + } + } + } + } +} diff --git a/src/Oro/Bundle/EmailBundle/Resources/public/css/less/style.less b/src/Oro/Bundle/EmailBundle/Resources/public/css/less/style.less index a5dedd77ce2..fb6db1be071 100644 --- a/src/Oro/Bundle/EmailBundle/Resources/public/css/less/style.less +++ b/src/Oro/Bundle/EmailBundle/Resources/public/css/less/style.less @@ -291,10 +291,6 @@ span.icon.grid .icon-paperclip { } } -&.mobile-version .box-type1 > .row-fluid > .widget-content > .thread-view { - padding-left: 0; -} - .email-thread-action-panel { .email-view-toggle-all { color: #888; @@ -730,22 +726,3 @@ a.sync-btn span.dots { .check-connection-messages { width: 294px; } - -&.mobile-version { - .email-view-toggle > .pull-right > .pull-left { - margin: 0 5px 5px 0; - } - .email-info .email-participants { - margin-right: 0; - float: left; - } - .email-info .email-short { - .email-sent-date { - padding: 0 - } - .email-body { - margin: 2px 0 0 0; - clear: both; - } - } -} diff --git a/src/Oro/Bundle/UIBundle/Resources/public/css/less/mobile/filter.less b/src/Oro/Bundle/UIBundle/Resources/public/css/less/mobile/filter.less index 4ecf01619bd..19d55c80c8a 100644 --- a/src/Oro/Bundle/UIBundle/Resources/public/css/less/mobile/filter.less +++ b/src/Oro/Bundle/UIBundle/Resources/public/css/less/mobile/filter.less @@ -20,6 +20,7 @@ display: inline-block; } .dropdown > .dropdown-menu { + padding: 10px; margin: 10px auto 0; max-width: 506px; width: 100%; @@ -52,7 +53,7 @@ top: -7px; } .filter-container { - padding: 10px; + padding: 0; .reset-filter-button { padding: 6px 0 4px 6px; position: absolute; @@ -62,6 +63,9 @@ margin: 0 0 10px 0; width: 100%; white-space: normal; + &:last-child { + margin-bottom: 0; + } .btn-group { margin-bottom: 0; } diff --git a/src/Oro/Bundle/UIBundle/Resources/public/css/less/mobile/layout.less b/src/Oro/Bundle/UIBundle/Resources/public/css/less/mobile/layout.less index 157db2c4357..f426ca13089 100644 --- a/src/Oro/Bundle/UIBundle/Resources/public/css/less/mobile/layout.less +++ b/src/Oro/Bundle/UIBundle/Resources/public/css/less/mobile/layout.less @@ -125,8 +125,8 @@ } .container-fluid, .grid-toolbar{ - padding-left: 10px; - padding-right: 10px; + padding-left: 0; + padding-right: 0; } } } From 85d90b5e7539bf0c53a0ed2042d2075840c9ad77 Mon Sep 17 00:00:00 2001 From: Hryhorii Hrebiniuk Date: Mon, 28 Dec 2015 18:42:08 +0200 Subject: [PATCH 303/471] BAP-9618: Broken view of field integration's Type --- .../IntegrationBundle/Resources/public/css/style.css | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Oro/Bundle/IntegrationBundle/Resources/public/css/style.css b/src/Oro/Bundle/IntegrationBundle/Resources/public/css/style.css index fca6170f8dc..fc5ed37e01a 100644 --- a/src/Oro/Bundle/IntegrationBundle/Resources/public/css/style.css +++ b/src/Oro/Bundle/IntegrationBundle/Resources/public/css/style.css @@ -4,6 +4,10 @@ height: 16px; width: 16px; background-size: contain !important; + margin-right: 4px; + margin-top: -2px; + vertical-align: middle; + float: left; } .select2-results .select2-result-label .aware-icon-block { @@ -11,11 +15,6 @@ top: 3px; } -.select2-choice .select2-chosen .aware-icon-block-selected, -.select2-results .select2-result-label .aware-icon-block-selected { - margin-bottom: 5px; -} - .integration-icon, .select2-choice .select2-chosen .aware-icon-block-text, .select2-results .select2-result-label .aware-icon-block-text { From 5fe6413ec9ecd88da6d54d4cd0187c7fd5e9eac1 Mon Sep 17 00:00:00 2001 From: Yevhen Shyshkin Date: Mon, 28 Dec 2015 18:56:32 +0100 Subject: [PATCH 304/471] OC-631: Problem with process parameters in EntityPaginationBundle - small fixes - added unit test --- .../Storage/StorageDataCollector.php | 3 ++- .../Unit/Storage/StorageDataCollectorTest.php | 24 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/Oro/Bundle/EntityPaginationBundle/Storage/StorageDataCollector.php b/src/Oro/Bundle/EntityPaginationBundle/Storage/StorageDataCollector.php index a4a36b3656f..67bad490673 100644 --- a/src/Oro/Bundle/EntityPaginationBundle/Storage/StorageDataCollector.php +++ b/src/Oro/Bundle/EntityPaginationBundle/Storage/StorageDataCollector.php @@ -85,13 +85,14 @@ public function collect(Request $request, $scope) $gridNames = array(); if ($request->query->get('grid')) { - $gridNames = array_keys($request->query->get('grid', [])); + $gridNames = array_keys((array)$request->query->get('grid', [])); } foreach ($gridNames as $gridName) { try { // datagrid manager automatically extracts all required parameters from request $dataGrid = $this->datagridManager->getDatagridByRequestParams($gridName); } catch (\RuntimeException $e) { + // processing of invalid grid names continue; } diff --git a/src/Oro/Bundle/EntityPaginationBundle/Tests/Unit/Storage/StorageDataCollectorTest.php b/src/Oro/Bundle/EntityPaginationBundle/Tests/Unit/Storage/StorageDataCollectorTest.php index 0e282def516..040bb4e53d7 100644 --- a/src/Oro/Bundle/EntityPaginationBundle/Tests/Unit/Storage/StorageDataCollectorTest.php +++ b/src/Oro/Bundle/EntityPaginationBundle/Tests/Unit/Storage/StorageDataCollectorTest.php @@ -96,6 +96,30 @@ public function testCollectWithDisabledPagination() $this->assertFalse($this->collector->collect($this->getGridRequest(), 'test')); } + public function testCollectWithEmptyGridRequest() + { + $this->setPaginationEnabled(true); + $this->datagridManager->expects($this->never()) + ->method('getDatagridByRequestParams'); + + $this->assertFalse($this->collector->collect(new Request(['grid' => '']), 'test')); + } + + public function testCollectWithInvalidGridName() + { + $invalidGridName = 'invalid'; + + $this->setPaginationEnabled(true); + $this->datagridManager->expects($this->once()) + ->method('getDatagridByRequestParams') + ->with($invalidGridName) + ->willThrowException(new \RuntimeException()); + $this->storage->expects($this->never()) + ->method('hasData'); + + $this->assertFalse($this->collector->collect(new Request(['grid' => [$invalidGridName => null]]), 'test')); + } + public function testCollectGridNotApplicable() { $this->setPaginationEnabled(true); From 00a316d6aae5f005f4d29131f8afb88948f53cd9 Mon Sep 17 00:00:00 2001 From: Denis Voronin Date: Mon, 28 Dec 2015 20:09:35 +0200 Subject: [PATCH 305/471] BAP-9579: Process default filters, sorters from grid view --- .../GridViews/GridViewsExtension.php | 97 ++++++++++++++++--- 1 file changed, 81 insertions(+), 16 deletions(-) diff --git a/src/Oro/Bundle/DataGridBundle/Extension/GridViews/GridViewsExtension.php b/src/Oro/Bundle/DataGridBundle/Extension/GridViews/GridViewsExtension.php index 19dd8378bf1..eb9058dc53c 100644 --- a/src/Oro/Bundle/DataGridBundle/Extension/GridViews/GridViewsExtension.php +++ b/src/Oro/Bundle/DataGridBundle/Extension/GridViews/GridViewsExtension.php @@ -14,6 +14,7 @@ use Oro\Bundle\DataGridBundle\Datagrid\Common\DatagridConfiguration; use Oro\Bundle\SecurityBundle\SecurityFacade; +use Oro\Bundle\DataGridBundle\Entity\GridView; class GridViewsExtension extends AbstractExtension { @@ -37,6 +38,9 @@ class GridViewsExtension extends AbstractExtension /** @var ManagerRegistry */ protected $registry; + /** @var GridView|null */ + protected $defaultGridView; + /** * @param EventDispatcherInterface $eventDispatcher * @param SecurityFacade $securityFacade @@ -63,6 +67,14 @@ public function isApplicable(DatagridConfiguration $config) return !$this->isDisabled(); } + /** + * {@inheritdoc} + */ + public function getPriority() + { + return 10; + } + /** * @return bool */ @@ -78,19 +90,11 @@ protected function isDisabled() */ public function visitMetadata(DatagridConfiguration $config, MetadataObject $data) { - $defaultViewId = $this->getDefaultViewId($config->getName()); - $params = $this->getParameters()->get(ParameterBag::ADDITIONAL_PARAMETERS, []); - - if (isset($params[self::VIEWS_PARAM_KEY])) { - $currentView = (int)$params[self::VIEWS_PARAM_KEY]; - } else { - $currentView = $defaultViewId; - $params[self::VIEWS_PARAM_KEY] = $defaultViewId; - $this->getParameters()->set(ParameterBag::ADDITIONAL_PARAMETERS, $params); - } + $currentViewId = $this->getCurrentViewId($config); + $this->setDefaultParams($config); - $data->offsetAddToArray('initialState', ['gridView' => $defaultViewId]); - $data->offsetAddToArray('state', ['gridView' => $currentView]); + $data->offsetAddToArray('initialState', ['gridView' => self::DEFAULT_VIEW_ID]); + $data->offsetAddToArray('state', ['gridView' => $currentViewId]); $allLabel = null; if (isset($config['options']) @@ -132,19 +136,80 @@ public function visitMetadata(DatagridConfiguration $config, MetadataObject $dat } /** + * Gets id for current grid view + * + * @param DatagridConfiguration $config + * + * @return int|string + */ + protected function getCurrentViewId(DatagridConfiguration $config) + { + $params = $this->getParameters()->get(ParameterBag::ADDITIONAL_PARAMETERS, []); + if (isset($params[self::VIEWS_PARAM_KEY])) { + return (int)$params[self::VIEWS_PARAM_KEY]; + } else { + return $this->getDefaultViewId($config->getName()); + } + } + + /** + * Gets id for defined as default grid view for current logged user. + * If is not defined returns id for raw configured grid. + * * @param string $gridName * * @return int|string */ protected function getDefaultViewId($gridName) { - $defaultGridView = null; - if ($this->securityFacade->isGranted('oro_datagrid_gridview_view')) { + $defaultGridView = $this->getDefaultView($gridName); + + return $defaultGridView ? $defaultGridView->getId() : self::DEFAULT_VIEW_ID; + } + + /** + * Gets defined as default grid view for current logged user. + * + * @param string $gridName + * + * @return GridView|null + */ + protected function getDefaultView($gridName) + { + + if ($this->defaultGridView === null && + $this->securityFacade->isGranted('oro_datagrid_gridview_view') + ) { $repository = $this->registry->getRepository('OroDataGridBundle:GridView'); - $defaultGridView = $repository->findDefaultGridView($gridName, $this->securityFacade->getLoggedUser()); + $defaultGridView = $repository->findDefaultGridView( + $gridName, + $this->securityFacade->getLoggedUser() + ); + + $this->defaultGridView = $defaultGridView; } - return $defaultGridView ? $defaultGridView->getId() : self::DEFAULT_VIEW_ID; + return $this->defaultGridView; + } + + /** + * Sets default parameters. + * Added filters and sorters for defined as default grid view for current logged user. + * + * @param DatagridConfiguration $config + */ + protected function setDefaultParams(DatagridConfiguration $config) + { + $params = $this->getParameters()->get(ParameterBag::ADDITIONAL_PARAMETERS, []); + + $params[self::VIEWS_PARAM_KEY] = $this->getDefaultViewId($config->getName()); + + $defaultGridView = $this->getDefaultView($config->getName()); + if ($defaultGridView) { + $this->getParameters()->mergeKey('_filter', $defaultGridView->getFiltersData()); + $this->getParameters()->mergeKey('_sort_by', $defaultGridView->getSortersData()); + } + $this->getParameters()->set(ParameterBag::ADDITIONAL_PARAMETERS, $params); } /** From 87fde77343a4a3e8314fe532a19322bd29cc79d8 Mon Sep 17 00:00:00 2001 From: zebimax Date: Mon, 28 Dec 2015 21:03:41 +0200 Subject: [PATCH 306/471] BAP-9577:Add option 'Use as default' to views options - fix review comments - fix removing default view --- .../Api/Rest/GridViewController.php | 13 ++++---- .../Bundle/DataGridBundle/Entity/GridView.php | 2 +- .../Manager/GridViewApiEntityManager.php | 32 +++++++++++++++++++ .../Entity/Repository/GridViewRepository.php | 13 ++------ .../EventListener/GridViewsLoadListener.php | 9 +----- .../GridViews/GridViewsExtension.php | 11 +++++-- .../Extension/GridViews/View.php | 14 ++++---- .../v1_2/DefaultGridViewUsersRelation.php | 4 +-- .../Resources/config/services.yml | 2 +- .../public/js/datagrid/grid-views/view.js | 19 +++++++++-- 10 files changed, 80 insertions(+), 39 deletions(-) create mode 100644 src/Oro/Bundle/DataGridBundle/Entity/Manager/GridViewApiEntityManager.php diff --git a/src/Oro/Bundle/DataGridBundle/Controller/Api/Rest/GridViewController.php b/src/Oro/Bundle/DataGridBundle/Controller/Api/Rest/GridViewController.php index b3c25839909..98edee243bd 100644 --- a/src/Oro/Bundle/DataGridBundle/Controller/Api/Rest/GridViewController.php +++ b/src/Oro/Bundle/DataGridBundle/Controller/Api/Rest/GridViewController.php @@ -103,12 +103,14 @@ public function deleteAction($id) /** + * Set/unset grid view as default for current user. + * * @param int $id * @param bool $default * * @return Response * @Post( - * "/gridviews/{id}/setdefault/{default}", + * "/gridviews/{id}/default/{default}", * requirements={"id"="\d+", "default"="\d+"}, * defaults={"default"=false} *) @@ -131,12 +133,11 @@ public function deleteAction($id) public function setDefaultAction($id, $default = false) { /** @var GridView $gridView */ - $gridView = $this->getManager()->find($id); - /** @var GridViewRepository $repository */ - $repository = $this->getManager()->getRepository(); - $repository->setGridViewDefault($this->getUser(), $gridView, $default); + $manager = $this->getManager(); + $gridView = $manager->find($id); + $manager->setGridViewDefault($this->getUser(), $gridView, $default); - return new JsonResponse([], Codes::HTTP_OK); + return new JsonResponse([], Codes::HTTP_NO_CONTENT); } /** diff --git a/src/Oro/Bundle/DataGridBundle/Entity/GridView.php b/src/Oro/Bundle/DataGridBundle/Entity/GridView.php index 89f051fd969..e29cffaf28e 100644 --- a/src/Oro/Bundle/DataGridBundle/Entity/GridView.php +++ b/src/Oro/Bundle/DataGridBundle/Entity/GridView.php @@ -128,7 +128,7 @@ class GridView * @ORM\ManyToMany( * targetEntity="Oro\Bundle\UserBundle\Entity\User" * ) - * @ORM\JoinTable(name="oro_grid_view_users", + * @ORM\JoinTable(name="oro_grid_view_user", * joinColumns={@ORM\JoinColumn(name="grid_view_id", referencedColumnName="id", onDelete="CASCADE")}, * inverseJoinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id", onDelete="CASCADE")}, * ) diff --git a/src/Oro/Bundle/DataGridBundle/Entity/Manager/GridViewApiEntityManager.php b/src/Oro/Bundle/DataGridBundle/Entity/Manager/GridViewApiEntityManager.php new file mode 100644 index 00000000000..046e606822f --- /dev/null +++ b/src/Oro/Bundle/DataGridBundle/Entity/Manager/GridViewApiEntityManager.php @@ -0,0 +1,32 @@ +getRepository(); + $gridViews = $repository->findDefaultGridViews($user, $gridView); + + foreach ($gridViews as $view) { + $view->removeUser($user); + } + if ($default) { + $gridView->addUser($user); + } + + $this->getObjectManager()->flush(); + } +} diff --git a/src/Oro/Bundle/DataGridBundle/Entity/Repository/GridViewRepository.php b/src/Oro/Bundle/DataGridBundle/Entity/Repository/GridViewRepository.php index f98ca48c81b..e2224fc41fd 100644 --- a/src/Oro/Bundle/DataGridBundle/Entity/Repository/GridViewRepository.php +++ b/src/Oro/Bundle/DataGridBundle/Entity/Repository/GridViewRepository.php @@ -55,9 +55,9 @@ public function findDefaultGridView(UserInterface $user, $gridName) /** * @param User $user * @param GridView $gridView - * @param bool $default + * @return GridView[] */ - public function setGridViewDefault(User $user, GridView $gridView, $default) + public function findDefaultGridViews(User $user, GridView $gridView) { /** @var GridView[] $defaultGridViews */ $defaultGridViews = $this @@ -65,14 +65,7 @@ public function setGridViewDefault(User $user, GridView $gridView, $default) ->getQuery() ->getResult(); - foreach ($defaultGridViews as $view) { - $view->removeUser($user); - } - if ($default) { - $gridView->addUser($user); - } - - $this->getEntityManager()->flush(); + return $defaultGridViews; } /** diff --git a/src/Oro/Bundle/DataGridBundle/EventListener/GridViewsLoadListener.php b/src/Oro/Bundle/DataGridBundle/EventListener/GridViewsLoadListener.php index da729013384..406a39bd55a 100644 --- a/src/Oro/Bundle/DataGridBundle/EventListener/GridViewsLoadListener.php +++ b/src/Oro/Bundle/DataGridBundle/EventListener/GridViewsLoadListener.php @@ -63,17 +63,11 @@ public function onViewsLoad(GridViewsLoadEvent $event) } $choices = []; $views = []; - $default = null; foreach ($gridViews as $gridView) { - $isDefault = false; - if ($defaultGridView === $gridView) { - $isDefault = true; - $default = $gridView; - } $view = $gridView->createView(); $view->setEditable($this->securityFacade->isGranted('EDIT', $gridView)); $view->setDeletable($this->securityFacade->isGranted('DELETE', $gridView)); - $view->setIsDefault($isDefault); + $view->setDefault($defaultGridView === $gridView); $views[] = $view->getMetadata(); $choices[] = [ 'label' => $this->createGridViewLabel($currentUser, $gridView), @@ -85,7 +79,6 @@ public function onViewsLoad(GridViewsLoadEvent $event) $newGridViews = $event->getGridViews(); $newGridViews['choices'] = array_merge($newGridViews['choices'], $choices); $newGridViews['views'] = array_merge($newGridViews['views'], $views); - $newGridViews['default'] = $default; $event->setGridViews($newGridViews); } diff --git a/src/Oro/Bundle/DataGridBundle/Extension/GridViews/GridViewsExtension.php b/src/Oro/Bundle/DataGridBundle/Extension/GridViews/GridViewsExtension.php index 88292cd49fc..efb8669585a 100644 --- a/src/Oro/Bundle/DataGridBundle/Extension/GridViews/GridViewsExtension.php +++ b/src/Oro/Bundle/DataGridBundle/Extension/GridViews/GridViewsExtension.php @@ -99,8 +99,15 @@ public function visitMetadata(DatagridConfiguration $config, MetadataObject $dat $this->eventDispatcher->dispatch(GridViewsLoadEvent::EVENT_NAME, $event); $gridViews = $event->getGridViews(); } - $systemAllView->setIsDefault(empty($gridViews['default'])); - unset($gridViews['default']); + $hasDefault = false; + foreach ($gridViews['views'] as $view) { + if (!empty($view['is_default'])) { + $hasDefault = true; + break; + } + } + + $systemAllView->setDefault(!$hasDefault); $gridViews['gridName'] = $config->getName(); $gridViews['permissions'] = $this->getPermissions(); $gridViews['views'][] = $systemAllView->getMetadata(); diff --git a/src/Oro/Bundle/DataGridBundle/Extension/GridViews/View.php b/src/Oro/Bundle/DataGridBundle/Extension/GridViews/View.php index 6a9feb46a79..434d4bcca31 100644 --- a/src/Oro/Bundle/DataGridBundle/Extension/GridViews/View.php +++ b/src/Oro/Bundle/DataGridBundle/Extension/GridViews/View.php @@ -26,7 +26,7 @@ class View protected $deletable = false; /** @var bool */ - protected $isDefault = false; + protected $default = false; /** * @var array @@ -200,19 +200,19 @@ public function setColumnsData(array $columnsData = []) /** * @return boolean */ - public function isIsDefault() + public function isDefault() { - return $this->isDefault; + return $this->default; } /** - * @param boolean $isDefault + * @param boolean $default * * @return $this */ - public function setIsDefault($isDefault) + public function setDefault($default) { - $this->isDefault = $isDefault; + $this->default = $default; return $this; } @@ -233,7 +233,7 @@ public function getMetadata() 'columns' => $this->columnsData, 'editable' => $this->editable, 'deletable' => $this->deletable, - 'is_default' => $this->isDefault + 'is_default' => $this->default ]; } } diff --git a/src/Oro/Bundle/DataGridBundle/Migrations/Schema/v1_2/DefaultGridViewUsersRelation.php b/src/Oro/Bundle/DataGridBundle/Migrations/Schema/v1_2/DefaultGridViewUsersRelation.php index dac38a28733..241af2abd43 100644 --- a/src/Oro/Bundle/DataGridBundle/Migrations/Schema/v1_2/DefaultGridViewUsersRelation.php +++ b/src/Oro/Bundle/DataGridBundle/Migrations/Schema/v1_2/DefaultGridViewUsersRelation.php @@ -15,14 +15,14 @@ public function up(Schema $schema, QueryBag $queries) } /** - * Creates 'oro_grid_view_users' table which represents relationship between grid views and + * Creates 'oro_grid_view_user' table which represents relationship between grid views and * users who chosen this grid view as default. * * @param Schema $schema */ public static function createOroDefaultGridViewUsersTable(Schema $schema) { - $table = $schema->createTable('oro_grid_view_users'); + $table = $schema->createTable('oro_grid_view_user'); $table->addColumn('grid_view_id', 'integer', []); $table->addColumn('user_id', 'integer', []); $table->setPrimaryKey(['grid_view_id', 'user_id']); diff --git a/src/Oro/Bundle/DataGridBundle/Resources/config/services.yml b/src/Oro/Bundle/DataGridBundle/Resources/config/services.yml index 06667c8eae1..de1b16940d9 100644 --- a/src/Oro/Bundle/DataGridBundle/Resources/config/services.yml +++ b/src/Oro/Bundle/DataGridBundle/Resources/config/services.yml @@ -24,7 +24,7 @@ parameters: oro_datagrid.event_listener.grid_views_load.class: Oro\Bundle\DataGridBundle\EventListener\GridViewsLoadListener oro_datagrid.form.type.grid_view_type.class: Oro\Bundle\DataGridBundle\Form\Type\GridViewType oro_datagrid.form.type.sorting_type.class: Oro\Bundle\DataGridBundle\Form\Type\GridSortingType - oro_datagrid.grid_view.manager.api.class: Oro\Bundle\SoapBundle\Entity\Manager\ApiEntityManager + oro_datagrid.grid_view.manager.api.class: Oro\Bundle\DataGridBundle\Entity\Manager\GridViewApiEntityManager oro_datagrid.grid_view.entity.class: Oro\Bundle\DataGridBundle\Entity\GridView oro_datagrid.grid_view.form.handler.api.class: Oro\Bundle\DataGridBundle\Form\Handler\GridViewApiHandler oro_datagrid.columns.helper.class: Oro\Bundle\DataGridBundle\Tools\ColumnsHelper diff --git a/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/grid-views/view.js b/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/grid-views/view.js index 9a336c17a5e..4b291c4e4d8 100644 --- a/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/grid-views/view.js +++ b/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/grid-views/view.js @@ -394,7 +394,6 @@ define([ }), {}, function(response) { - // TODO how we can remove this fix with labels? // for now we need to get label from choices to save specifically built labels(shared, system) defaultModel.set({is_default:false, label: self._getView(defaultModel.get('name')).label}); @@ -435,6 +434,11 @@ define([ this.viewDirty = !this._isCurrentStateSynchronized(); } + if (model.get('is_default')) { + var systemModel = this._getDefaultSystemViewModel(); + systemModel.set({is_default:true, label: this._getView(this.DEFAULT_GRID_VIEW_ID).label}); + } + this.render(); }, @@ -580,7 +584,7 @@ define([ { label: __('oro.datagrid.action.use_as_default_grid_view'), name: 'use_as_default', - enabled: !currentView.get('is_default') + enabled: typeof currentView !== 'undefined' && !currentView.get('is_default') } ]; }, @@ -634,6 +638,17 @@ define([ return this._getCurrentView().value === this.DEFAULT_GRID_VIEW_ID; }, + /** + * @private + * + * @returns {undefined|GridViewModel} + */ + _getDefaultSystemViewModel: function() { + return this.viewsCollection.findWhere({ + name:this.DEFAULT_GRID_VIEW_ID + }); + }, + /** * @private * From 36ff22782d338f9403bec86f13812409c2c9ac55 Mon Sep 17 00:00:00 2001 From: zebimax Date: Mon, 28 Dec 2015 22:24:58 +0200 Subject: [PATCH 307/471] BAP-9577:Add option 'Use as default' to views options - fix merge conflicts --- .../Entity/Repository/GridViewRepository.php | 13 ++--- .../GridViews/GridViewsExtension.php | 47 +++++-------------- 2 files changed, 20 insertions(+), 40 deletions(-) diff --git a/src/Oro/Bundle/DataGridBundle/Entity/Repository/GridViewRepository.php b/src/Oro/Bundle/DataGridBundle/Entity/Repository/GridViewRepository.php index e2224fc41fd..c401cb85db1 100644 --- a/src/Oro/Bundle/DataGridBundle/Entity/Repository/GridViewRepository.php +++ b/src/Oro/Bundle/DataGridBundle/Entity/Repository/GridViewRepository.php @@ -40,12 +40,12 @@ public function findGridViews(AclHelper $aclHelper, UserInterface $user, $gridNa } /** - * @param UserInterface $user - * @param string $gridName + * @param User $user + * @param string $gridName * * @return GridView|null */ - public function findDefaultGridView(UserInterface $user, $gridName) + public function findDefaultGridView(User $user, $gridName) { $qb = $this->getFindDefaultGridViewQb($user, $gridName); @@ -55,6 +55,7 @@ public function findDefaultGridView(UserInterface $user, $gridName) /** * @param User $user * @param GridView $gridView + * * @return GridView[] */ public function findDefaultGridViews(User $user, GridView $gridView) @@ -69,12 +70,12 @@ public function findDefaultGridViews(User $user, GridView $gridView) } /** - * @param UserInterface $user - * @param string $gridName + * @param User $user + * @param string $gridName * * @return QueryBuilder */ - protected function getFindDefaultGridViewQb(UserInterface $user, $gridName) + protected function getFindDefaultGridViewQb(User $user, $gridName) { $qb = $this->createQueryBuilder('gv'); $qb->innerJoin('gv.users', 'u') diff --git a/src/Oro/Bundle/DataGridBundle/Extension/GridViews/GridViewsExtension.php b/src/Oro/Bundle/DataGridBundle/Extension/GridViews/GridViewsExtension.php index dc8645909e7..ca449fbb365 100644 --- a/src/Oro/Bundle/DataGridBundle/Extension/GridViews/GridViewsExtension.php +++ b/src/Oro/Bundle/DataGridBundle/Extension/GridViews/GridViewsExtension.php @@ -78,25 +78,8 @@ protected function isDisabled() */ public function visitMetadata(DatagridConfiguration $config, MetadataObject $data) { - $defaultViewId = $this->getDefaultViewId($config->getName()); - $params = $this->getParameters()->get(ParameterBag::ADDITIONAL_PARAMETERS, []); - - if (isset($params[self::VIEWS_PARAM_KEY])) { - $currentView = (int)$params[self::VIEWS_PARAM_KEY]; - } else { - $currentView = $defaultViewId; - $params[self::VIEWS_PARAM_KEY] = $defaultViewId; - $this->getParameters()->set(ParameterBag::ADDITIONAL_PARAMETERS, $params); - } - - $data->offsetAddToArray('initialState', ['gridView' => $defaultViewId]); - $data->offsetAddToArray('state', ['gridView' => $currentView]); - $allLabel = null; - if (isset($config['options']) - &&isset($config['options']['gridViews']) - && isset($config['options']['gridViews']['allLabel']) - ) { + if (isset($config['options'], $config['options']['gridViews'], $config['options']['gridViews']['allLabel'])) { $allLabel = $this->translator->trans($config['options']['gridViews']['allLabel']); } @@ -124,35 +107,31 @@ public function visitMetadata(DatagridConfiguration $config, MetadataObject $dat $this->eventDispatcher->dispatch(GridViewsLoadEvent::EVENT_NAME, $event); $gridViews = $event->getGridViews(); } - $hasDefault = false; + $defaultViewId = self::DEFAULT_VIEW_ID; foreach ($gridViews['views'] as $view) { if (!empty($view['is_default'])) { - $hasDefault = true; + $defaultViewId = $view['name']; break; } } - $systemAllView->setDefault(!$hasDefault); + $systemAllView->setDefault($defaultViewId === self::DEFAULT_VIEW_ID); $gridViews['gridName'] = $config->getName(); $gridViews['permissions'] = $this->getPermissions(); $gridViews['views'][] = $systemAllView->getMetadata(); $data->offsetAddToArray('gridViews', $gridViews); - } - /** - * @param string $gridName - * - * @return int|string - */ - protected function getDefaultViewId($gridName) - { - $defaultGridView = null; - if ($this->securityFacade->isGranted('oro_datagrid_gridview_view')) { - $repository = $this->registry->getRepository('OroDataGridBundle:GridView'); - $defaultGridView = $repository->findDefaultGridView($gridName, $this->securityFacade->getLoggedUser()); + $params = $this->getParameters()->get(ParameterBag::ADDITIONAL_PARAMETERS, []); + if (isset($params[self::VIEWS_PARAM_KEY])) { + $currentView = (int)$params[self::VIEWS_PARAM_KEY]; + } else { + $currentView = $defaultViewId; + $params[self::VIEWS_PARAM_KEY] = $defaultViewId; + $this->getParameters()->set(ParameterBag::ADDITIONAL_PARAMETERS, $params); } - return $defaultGridView ? $defaultGridView->getId() : self::DEFAULT_VIEW_ID; + $data->offsetAddToArray('initialState', ['gridView' => self::DEFAULT_VIEW_ID]); + $data->offsetAddToArray('state', ['gridView' => $currentView]); } /** From 30a20f4516da7a4db13b1abc4f9cf9e6aae72885 Mon Sep 17 00:00:00 2001 From: zebimax Date: Mon, 28 Dec 2015 22:30:05 +0200 Subject: [PATCH 308/471] BAP-9577:Add option 'Use as default' to views options - remove unused --- .../DataGridBundle/Controller/Api/Rest/GridViewController.php | 1 - .../DataGridBundle/Extension/GridViews/AbstractViewsList.php | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Oro/Bundle/DataGridBundle/Controller/Api/Rest/GridViewController.php b/src/Oro/Bundle/DataGridBundle/Controller/Api/Rest/GridViewController.php index 98edee243bd..ce54b905cae 100644 --- a/src/Oro/Bundle/DataGridBundle/Controller/Api/Rest/GridViewController.php +++ b/src/Oro/Bundle/DataGridBundle/Controller/Api/Rest/GridViewController.php @@ -18,7 +18,6 @@ use Oro\Bundle\SecurityBundle\Annotation\Acl; use Oro\Bundle\SecurityBundle\SecurityFacade; use Oro\Bundle\SoapBundle\Controller\Api\Rest\RestController; -use Oro\Bundle\DataGridBundle\Entity\Repository\GridViewRepository; /** * @Rest\NamePrefix("oro_datagrid_api_rest_gridview_") diff --git a/src/Oro/Bundle/DataGridBundle/Extension/GridViews/AbstractViewsList.php b/src/Oro/Bundle/DataGridBundle/Extension/GridViews/AbstractViewsList.php index ef27ddccaa9..bf5734c1785 100644 --- a/src/Oro/Bundle/DataGridBundle/Extension/GridViews/AbstractViewsList.php +++ b/src/Oro/Bundle/DataGridBundle/Extension/GridViews/AbstractViewsList.php @@ -100,8 +100,7 @@ function (View $view) { return [ 'choices' => $this->toChoiceList(), - 'views' => $result->toArray(), - 'default' => null + 'views' => $result->toArray() ]; } From 5ad84114c06e61f70f884ad783e70575c892546c Mon Sep 17 00:00:00 2001 From: Oleg Popadko Date: Fri, 25 Dec 2015 20:04:42 +0200 Subject: [PATCH 309/471] BAP-7696: Update jsTree library - update jsTree lib --- .../UIBundle/Resources/config/assets.yml | 2 +- .../UIBundle/Resources/config/requirejs.yml | 8 +- .../js/app/components/basic-tree-component.js | 142 + .../app/components/tree-manage-component.js | 166 + .../public/lib/jstree/jquery.cookie.js | 96 - .../public/lib/jstree/jquery.hotkeys.js | 111 - .../public/lib/jstree/jquery.jstree.js | 4562 ---------- .../Resources/public/lib/jstree/jstree.js | 7781 +++++++++++++++++ .../Resources/public/lib/jstree/jstree.min.js | 5 + .../public/lib/jstree/themes/apple/bg.jpg | Bin 331 -> 0 bytes .../public/lib/jstree/themes/apple/d.png | Bin 7765 -> 0 bytes .../lib/jstree/themes/apple/dot_for_ie.gif | Bin 43 -> 0 bytes .../public/lib/jstree/themes/apple/style.css | 61 - .../lib/jstree/themes/apple/throbber.gif | Bin 1849 -> 0 bytes .../public/lib/jstree/themes/classic/d.gif | Bin 3003 -> 0 bytes .../public/lib/jstree/themes/classic/d.png | Bin 7535 -> 0 bytes .../lib/jstree/themes/classic/dot_for_ie.gif | Bin 43 -> 0 bytes .../lib/jstree/themes/classic/style.css | 77 - .../lib/jstree/themes/classic/throbber.gif | Bin 1849 -> 0 bytes .../lib/jstree/themes/default-dark/32px.png | Bin 0 -> 1562 bytes .../lib/jstree/themes/default-dark/40px.png | Bin 0 -> 5717 bytes .../lib/jstree/themes/default-dark/style.css | 1105 +++ .../jstree/themes/default-dark/style.min.css | 1 + .../jstree/themes/default-dark/throbber.gif | Bin 0 -> 1720 bytes .../lib/jstree/themes/default-rtl/d.gif | Bin 2872 -> 0 bytes .../lib/jstree/themes/default-rtl/d.png | Bin 5437 -> 0 bytes .../lib/jstree/themes/default-rtl/dots.gif | Bin 132 -> 0 bytes .../lib/jstree/themes/default-rtl/style.css | 84 - .../jstree/themes/default-rtl/throbber.gif | Bin 1849 -> 0 bytes .../public/lib/jstree/themes/default/32px.png | Bin 0 -> 3121 bytes .../public/lib/jstree/themes/default/40px.png | Bin 0 -> 1880 bytes .../public/lib/jstree/themes/default/d.gif | Bin 2944 -> 0 bytes .../public/lib/jstree/themes/default/d.png | Bin 5437 -> 0 bytes .../lib/jstree/themes/default/style.css | 1138 ++- .../lib/jstree/themes/default/style.min.css | 1 + .../lib/jstree/themes/default/throbber.gif | Bin 1849 -> 1720 bytes .../Resources/translations/jsmessages.en.yml | 3 + 37 files changed, 10268 insertions(+), 5075 deletions(-) create mode 100644 src/Oro/Bundle/UIBundle/Resources/public/js/app/components/basic-tree-component.js create mode 100644 src/Oro/Bundle/UIBundle/Resources/public/js/app/components/tree-manage-component.js delete mode 100644 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/jquery.cookie.js delete mode 100644 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/jquery.hotkeys.js delete mode 100644 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/jquery.jstree.js create mode 100755 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/jstree.js create mode 100755 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/jstree.min.js delete mode 100644 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/themes/apple/bg.jpg delete mode 100644 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/themes/apple/d.png delete mode 100644 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/themes/apple/dot_for_ie.gif delete mode 100644 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/themes/apple/style.css delete mode 100644 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/themes/apple/throbber.gif delete mode 100644 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/themes/classic/d.gif delete mode 100644 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/themes/classic/d.png delete mode 100644 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/themes/classic/dot_for_ie.gif delete mode 100644 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/themes/classic/style.css delete mode 100644 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/themes/classic/throbber.gif create mode 100755 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/themes/default-dark/32px.png create mode 100755 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/themes/default-dark/40px.png create mode 100755 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/themes/default-dark/style.css create mode 100755 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/themes/default-dark/style.min.css create mode 100755 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/themes/default-dark/throbber.gif delete mode 100644 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/themes/default-rtl/d.gif delete mode 100644 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/themes/default-rtl/d.png delete mode 100644 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/themes/default-rtl/dots.gif delete mode 100644 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/themes/default-rtl/style.css delete mode 100644 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/themes/default-rtl/throbber.gif create mode 100755 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/themes/default/32px.png create mode 100755 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/themes/default/40px.png delete mode 100644 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/themes/default/d.gif delete mode 100644 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/themes/default/d.png mode change 100644 => 100755 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/themes/default/style.css create mode 100755 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/themes/default/style.min.css mode change 100644 => 100755 src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/themes/default/throbber.gif diff --git a/src/Oro/Bundle/UIBundle/Resources/config/assets.yml b/src/Oro/Bundle/UIBundle/Resources/config/assets.yml index a1c27b8e2c1..362422c6155 100644 --- a/src/Oro/Bundle/UIBundle/Resources/config/assets.yml +++ b/src/Oro/Bundle/UIBundle/Resources/config/assets.yml @@ -4,7 +4,7 @@ css: 'oroui': # libraries' styles - 'bundles/oroui/lib/bootstrap/bootstrap.less' - - 'bundles/oroui/lib/jstree/themes/default/style.css' + - 'bundles/oroui/lib/jstree/themes/default/style.min.css' - 'bundles/oroui/lib/font-awesome/css/font-awesome.css' - 'bundles/oroui/lib/simplecolorpicker/jquery.simplecolorpicker.css' - 'bundles/oroui/lib/simplecolorpicker/jquery.simplecolorpicker-fontawesome.css' diff --git a/src/Oro/Bundle/UIBundle/Resources/config/requirejs.yml b/src/Oro/Bundle/UIBundle/Resources/config/requirejs.yml index 567bebe0d03..370d6d0f0fa 100644 --- a/src/Oro/Bundle/UIBundle/Resources/config/requirejs.yml +++ b/src/Oro/Bundle/UIBundle/Resources/config/requirejs.yml @@ -26,9 +26,6 @@ config: 'jquery.cookie': deps: - 'jquery' - 'jstree/jquery.hotkeys': - deps: - - 'jquery.jstree' 'bootstrap': deps: - 'jquery' @@ -132,10 +129,8 @@ config: 'jquery.simplecolorpicker': 'bundles/oroui/lib/simplecolorpicker/jquery.simplecolorpicker.js' 'jquery.minicolors': 'bundles/oroui/lib/minicolors/jquery.minicolors.js' 'jquery.cookie': 'bundles/oroui/lib/jquery/jquery.cookie.js' - 'jquery.jstree': 'bundles/oroui/lib/jstree/jquery.jstree.js' + 'jquery.jstree': 'bundles/oroui/lib/jstree/jstree.min.js' 'jquery.ajax.queue': 'bundles/oroui/lib/jquery/jquery.ajaxQueue.min.js' - 'jstree/jquery.cookie': 'bundles/oroui/lib/jstree/jquery.cookie.js' - 'jstree/jquery.hotkeys': 'bundles/oroui/lib/jstree/jquery.hotkeys.js' 'bootstrap': 'bundles/oroui/lib/bootstrap.min.js' # 'bootstrap': 'bundles/oroui/js/bootstrap-dev.js' 'underscore': 'bundles/components/underscore/underscore.js' @@ -188,6 +183,7 @@ config: 'oroui/js/app/components/tabs-component': 'bundles/oroui/js/app/components/tabs-component.js' 'oroui/js/app/components/view-component': 'bundles/oroui/js/app/components/view-component.js' 'oroui/js/app/components/widget-component': 'bundles/oroui/js/app/components/widget-component.js' + 'oroui/js/app/components/basic-tree-component': 'bundles/oroui/js/app/components/basic-tree-component.js' 'oroui/js/widget-manager': 'bundles/oroui/js/widget/widget-manager.js' 'oroui/js/widget/abstract-widget': 'bundles/oroui/js/widget/abstract-widget.js' 'oro/block-widget': 'bundles/oroui/js/widget/block-widget.js' diff --git a/src/Oro/Bundle/UIBundle/Resources/public/js/app/components/basic-tree-component.js b/src/Oro/Bundle/UIBundle/Resources/public/js/app/components/basic-tree-component.js new file mode 100644 index 00000000000..acf353ae212 --- /dev/null +++ b/src/Oro/Bundle/UIBundle/Resources/public/js/app/components/basic-tree-component.js @@ -0,0 +1,142 @@ +define(function(require) { + 'use strict'; + + var BasicTreeComponent; + var $ = require('jquery'); + var _ = require('underscore'); + var mediator = require('oroui/js/mediator'); + var layout = require('oroui/js/layout'); + var BaseComponent = require('oroui/js/app/components/base/component'); + + require('jquery.jstree'); + + /** + * Options: + * - data - tree structure in jstree json format + * - nodeId - identifier of selected node + * + * @export oroui/js/app/components/basic-tree-component + * @extends oroui.app.components.base.Component + * @class oroui.app.components.BasicTreeComponent + */ + BasicTreeComponent = BaseComponent.extend({ + /** + * @property {Object} + */ + $tree: null, + + /** + * @property {Number} + */ + nodeId: null, + + /** + * @property {Boolean} + */ + initialization: false, + + /** + * @param {Object} options + */ + initialize: function(options) { + var nodeList = options.data; + if (!nodeList) { + return; + } + + this.$tree = $(options._sourceElement); + + var config = { + 'core': { + 'multiple': false, + 'data': nodeList, + 'check_callback': true + }, + 'state': { + 'key': options.key, + 'filter': _.bind(this.onFilter, this) + }, + 'plugins': ['state'] + }; + config = this.customizeTreeConfig(options, config); + + this.nodeId = options.nodeId; + + this._deferredInit(); + this.initialization = true; + + this.$tree.jstree(config); + + var self = this; + this.$tree.on('ready.jstree', function() { + self._resolveDeferredInit(); + self.initialization = false; + }); + + self._fixContainerHeight(); + }, + + /** + * Customize jstree config to add plugins, callbacks etc. + * + * @param {Object} options + * @param {Object} config + * @returns {Object} + */ + customizeTreeConfig: function(options, config) { + return config; + }, + + /** + * Filters tree state + * + * @param {Object} state + * @returns {Object} + */ + onFilter: function(state) { + if (this.nodeId) { + state.core.selected = [this.nodeId]; + } else { + state.core.selected = []; + } + return state; + }, + + /** + * Fix scrollable container height + * TODO: This method should be removed during fixing of https://magecore.atlassian.net/browse/BB-336 + * + */ + _fixContainerHeight: function() { + var tree = this.$tree.parent(); + if (!tree.hasClass('category-tree')) { + return; + } + + var container = tree.parent(); + if (!container.hasClass('category-container')) { + return; + } + + var fixHeight = function() { + var anchor = $('#bottom-anchor').position().top; + var container = container.position().top; + var debugBarHeight = $('.sf-toolbar:visible').height() || 0; + var footerHeight = $('#footer:visible').height() || 0; + var fixContent = 1; + + tree.height(anchor - container - debugBarHeight - footerHeight + fixContent); + }; + + layout.onPageRendered(fixHeight); + $(window).on('resize', _.debounce(fixHeight, 50)); + mediator.on('page:afterChange', fixHeight); + mediator.on('layout:adjustReloaded', fixHeight); + mediator.on('layout:adjustHeight', fixHeight); + + fixHeight(); + } + }); + + return BasicTreeComponent; +}); diff --git a/src/Oro/Bundle/UIBundle/Resources/public/js/app/components/tree-manage-component.js b/src/Oro/Bundle/UIBundle/Resources/public/js/app/components/tree-manage-component.js new file mode 100644 index 00000000000..2267ff0d35e --- /dev/null +++ b/src/Oro/Bundle/UIBundle/Resources/public/js/app/components/tree-manage-component.js @@ -0,0 +1,166 @@ +define(function(require) { + 'use strict'; + + var TreeManageComponent; + var $ = require('jquery'); + var _ = require('underscore'); + var __ = require('orotranslation/js/translator'); + var widgetManager = require('oroui/js/widget-manager'); + var mediator = require('oroui/js/mediator'); + var messenger = require('oroui/js/messenger'); + var routing = require('routing'); + var BasicTreeComponent = require('oroui/js/app/components/basic-tree-component'); + + /** + * @export oroui/js/app/components/tree-manage-component + * @extends oroui.app.components.BasicTreeComponent + * @class oroui.app.components.TreeManageComponent + */ + TreeManageComponent = BasicTreeComponent.extend({ + /** + * @property {Boolean} + */ + updateAllowed: false, + + /** + * @property {Boolean} + */ + moveTriggered: false, + + /** + * @property {String} + */ + reloadWidget: '', + + /** + * @property {String} + */ + onSelectRoute: '', + + /** + * @property {String} + */ + onMoveRoute: '', + + /** + * @param {Object} options + */ + initialize: function(options) { + TreeManageComponent.__super__.initialize.call(this, options); + if (!this.$tree) { + return; + } + + this.updateAllowed = options.updateAllowed; + this.reloadWidget = options.reloadWidget; + this.onSelectRoute = options.onSelectRoute; + this.onMoveRoute = options.onMoveRoute; + + this.$tree.on('select_node.jstree', _.bind(this.onSelect, this)); + this.$tree.on('move_node.jstree', _.bind(this.onMove, this)); + + this._fixContainerHeight(); + }, + + /** + * @param {Object} options + * @param {Object} config + * @returns {Object} + */ + customizeTreeConfig: function(options, config) { + if (options.updateAllowed) { + config.plugins.push('dnd'); + config.dnd = { + 'copy': false + }; + } + + return config; + }, + + /** + * Triggers after node selection in tree + * + * @param {Object} node + * @param {Object} selected + */ + onSelect: function(node, selected) { + if (this.initialization || !this.updateAllowed) { + return; + } + + var url = routing.generate(this.onSelectRoute, {id: selected.node.id}); + mediator.execute('redirectTo', {url: url}); + }, + + /** + * Triggers after node move + * + * @param {Object} e + * @param {Object} data + */ + onMove: function(e, data) { + if (this.moveTriggered) { + return; + } + + if (data.parent === '#' && data.old_parent === '#') { + this.rollback(data); + messenger.notificationFlashMessage('warning', __('oro.ui.jstree.move_root_page_warning')); + return; + } + + var self = this; + $.ajax({ + async: false, + type: 'PUT', + url: routing.generate(self.onMoveRoute), + data: { + id: data.node.id, + parent: data.parent, + position: data.position + }, + success: function(result) { + if (!result.status) { + self.rollback(data); + messenger.notificationFlashMessage( + 'error', + __('oro.ui.jstree.move_page_error', {nodeText: data.node.text}) + ); + } else if (self.reloadWidget) { + widgetManager.getWidgetInstanceByAlias(self.reloadWidget, function(widget) { + widget.render(); + }); + } + } + }); + }, + + /** + * Rollback node move + * + * @param {Object} data + */ + rollback: function(data) { + this.moveTriggered = true; + this.$tree.jstree('move_node', data.node, data.old_parent, data.old_position); + this.moveTriggered = false; + }, + + /** + * Off events + */ + dispose: function() { + if (this.disposed) { + return; + } + this.$tree + .off('select_node.jstree') + .off('move_node.jstree'); + + TreeManageComponent.__super__.dispose.call(this); + } + }); + + return TreeManageComponent; +}); diff --git a/src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/jquery.cookie.js b/src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/jquery.cookie.js deleted file mode 100644 index 6df1faca25f..00000000000 --- a/src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/jquery.cookie.js +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Cookie plugin - * - * Copyright (c) 2006 Klaus Hartl (stilbuero.de) - * Dual licensed under the MIT and GPL licenses: - * http://www.opensource.org/licenses/mit-license.php - * http://www.gnu.org/licenses/gpl.html - * - */ - -/** - * Create a cookie with the given name and value and other optional parameters. - * - * @example $.cookie('the_cookie', 'the_value'); - * @desc Set the value of a cookie. - * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true }); - * @desc Create a cookie with all available options. - * @example $.cookie('the_cookie', 'the_value'); - * @desc Create a session cookie. - * @example $.cookie('the_cookie', null); - * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain - * used when the cookie was set. - * - * @param String name The name of the cookie. - * @param String value The value of the cookie. - * @param Object options An object literal containing key/value pairs to provide optional cookie attributes. - * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object. - * If a negative value is specified (e.g. a date in the past), the cookie will be deleted. - * If set to null or omitted, the cookie will be a session cookie and will not be retained - * when the the browser exits. - * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie). - * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie). - * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will - * require a secure protocol (like HTTPS). - * @type undefined - * - * @name $.cookie - * @cat Plugins/Cookie - * @author Klaus Hartl/klaus.hartl@stilbuero.de - */ - -/** - * Get the value of a cookie with the given name. - * - * @example $.cookie('the_cookie'); - * @desc Get the value of a cookie. - * - * @param String name The name of the cookie. - * @return The value of the cookie. - * @type String - * - * @name $.cookie - * @cat Plugins/Cookie - * @author Klaus Hartl/klaus.hartl@stilbuero.de - */ -jQuery.cookie = function(name, value, options) { - if (typeof value != 'undefined') { // name and value given, set cookie - options = options || {}; - if (value === null) { - value = ''; - options.expires = -1; - } - var expires = ''; - if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) { - var date; - if (typeof options.expires == 'number') { - date = new Date(); - date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000)); - } else { - date = options.expires; - } - expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE - } - // CAUTION: Needed to parenthesize options.path and options.domain - // in the following expressions, otherwise they evaluate to undefined - // in the packed version for some reason... - var path = options.path ? '; path=' + (options.path) : ''; - var domain = options.domain ? '; domain=' + (options.domain) : ''; - var secure = options.secure ? '; secure' : ''; - document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join(''); - } else { // only name given, get cookie - var cookieValue = null; - if (document.cookie && document.cookie != '') { - var cookies = document.cookie.split(';'); - for (var i = 0; i < cookies.length; i++) { - var cookie = jQuery.trim(cookies[i]); - // Does this cookie string begin with the name we want? - if (cookie.substring(0, name.length + 1) == (name + '=')) { - cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); - break; - } - } - } - return cookieValue; - } -}; \ No newline at end of file diff --git a/src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/jquery.hotkeys.js b/src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/jquery.hotkeys.js deleted file mode 100644 index c240a7fd7f2..00000000000 --- a/src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/jquery.hotkeys.js +++ /dev/null @@ -1,111 +0,0 @@ -/* - * jQuery Hotkeys Plugin - * Copyright 2010, John Resig - * Dual licensed under the MIT or GPL Version 2 licenses. - * - * Based upon the plugin by Tzury Bar Yochay: - * http://github.com/tzuryby/hotkeys - * - * Original idea by: - * Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/ -*/ - -/* - * One small change is: now keys are passed by object { keys: '...' } - * Might be useful, when you want to pass some other data to your handler - */ - -(function(jQuery){ - - jQuery.hotkeys = { - version: "0.8", - - specialKeys: { - 8: "backspace", 9: "tab", 10: "return", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 19: "pause", - 20: "capslock", 27: "esc", 32: "space", 33: "pageup", 34: "pagedown", 35: "end", 36: "home", - 37: "left", 38: "up", 39: "right", 40: "down", 45: "insert", 46: "del", - 96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7", - 104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/", - 112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8", - 120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 186: ";", 191: "/", - 220: "\\", 222: "'", 224: "meta" - }, - - shiftNums: { - "`": "~", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&", - "8": "*", "9": "(", "0": ")", "-": "_", "=": "+", ";": ": ", "'": "\"", ",": "<", - ".": ">", "/": "?", "\\": "|" - } - }; - - function keyHandler( handleObj ) { - if ( typeof handleObj.data === "string" ) { - handleObj.data = { keys: handleObj.data }; - } - - // Only care when a possible input has been specified - if ( !handleObj.data || !handleObj.data.keys || typeof handleObj.data.keys !== "string" ) { - return; - } - - var origHandler = handleObj.handler, - keys = handleObj.data.keys.toLowerCase().split(" "), - textAcceptingInputTypes = ["text", "password", "number", "email", "url", "range", "date", "month", "week", "time", "datetime", "datetime-local", "search", "color", "tel"]; - - handleObj.handler = function( event ) { - // Don't fire in text-accepting inputs that we didn't directly bind to - if ( this !== event.target && (/textarea|select/i.test( event.target.nodeName ) || - jQuery.inArray(event.target.type, textAcceptingInputTypes) > -1 ) ) { - return; - } - - var special = jQuery.hotkeys.specialKeys[ event.keyCode ], - // character codes are available only in keypress - character = event.type === "keypress" && String.fromCharCode( event.which ).toLowerCase(), - modif = "", possible = {}; - - // check combinations (alt|ctrl|shift+anything) - if ( event.altKey && special !== "alt" ) { - modif += "alt+"; - } - - if ( event.ctrlKey && special !== "ctrl" ) { - modif += "ctrl+"; - } - - // TODO: Need to make sure this works consistently across platforms - if ( event.metaKey && !event.ctrlKey && special !== "meta" ) { - modif += "meta+"; - } - - if ( event.shiftKey && special !== "shift" ) { - modif += "shift+"; - } - - if ( special ) { - possible[ modif + special ] = true; - } - - if ( character ) { - possible[ modif + character ] = true; - possible[ modif + jQuery.hotkeys.shiftNums[ character ] ] = true; - - // "$" can be triggered as "Shift+4" or "Shift+$" or just "$" - if ( modif === "shift+" ) { - possible[ jQuery.hotkeys.shiftNums[ character ] ] = true; - } - } - - for ( var i = 0, l = keys.length; i < l; i++ ) { - if ( possible[ keys[i] ] ) { - return origHandler.apply( this, arguments ); - } - } - }; - } - - jQuery.each([ "keydown", "keyup", "keypress" ], function() { - jQuery.event.special[ this ] = { add: keyHandler }; - }); - -})( this.jQuery ); diff --git a/src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/jquery.jstree.js b/src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/jquery.jstree.js deleted file mode 100644 index 6b23c05f3e1..00000000000 --- a/src/Oro/Bundle/UIBundle/Resources/public/lib/jstree/jquery.jstree.js +++ /dev/null @@ -1,4562 +0,0 @@ -/* - * jsTree 1.0-rc3 - * http://jstree.com/ - * - * Copyright (c) 2010 Ivan Bozhanov (vakata.com) - * - * Licensed same as jquery - under the terms of either the MIT License or the GPL Version 2 License - * http://www.opensource.org/licenses/mit-license.php - * http://www.gnu.org/licenses/gpl.html - * - * $Date: 2011-02-09 01:17:14 +0200 (ср, 09 февр 2011) $ - * $Revision: 236 $ - */ - - -"use strict"; - -// top wrapper to prevent multiple inclusion (is this OK?) -(function () { if(jQuery && jQuery.jstree) { return; } - var is_ie6 = false, is_ie7 = false, is_ff2 = false; - -/* - * jsTree core - */ -(function ($) { - // Common functions not related to jsTree - // decided to move them to a `vakata` "namespace" - $.vakata = {}; - // CSS related functions - $.vakata.css = { - get_css : function(rule_name, delete_flag, sheet) { - rule_name = rule_name.toLowerCase(); - var css_rules = sheet.cssRules || sheet.rules, - j = 0; - do { - if(css_rules.length && j > css_rules.length + 5) { return false; } - if(css_rules[j].selectorText && css_rules[j].selectorText.toLowerCase() == rule_name) { - if(delete_flag === true) { - if(sheet.removeRule) { sheet.removeRule(j); } - if(sheet.deleteRule) { sheet.deleteRule(j); } - return true; - } - else { return css_rules[j]; } - } - } - while (css_rules[++j]); - return false; - }, - add_css : function(rule_name, sheet) { - if($.jstree.css.get_css(rule_name, false, sheet)) { return false; } - if(sheet.insertRule) { sheet.insertRule(rule_name + ' { }', 0); } else { sheet.addRule(rule_name, null, 0); } - return $.vakata.css.get_css(rule_name); - }, - remove_css : function(rule_name, sheet) { - return $.vakata.css.get_css(rule_name, true, sheet); - }, - add_sheet : function(opts) { - var tmp = false, is_new = true; - if(opts.str) { - if(opts.title) { tmp = $("style[id='" + opts.title + "-stylesheet']")[0]; } - if(tmp) { is_new = false; } - else { - tmp = document.createElement("style"); - tmp.setAttribute('type',"text/css"); - if(opts.title) { tmp.setAttribute("id", opts.title + "-stylesheet"); } - } - if(tmp.styleSheet) { - if(is_new) { - document.getElementsByTagName("head")[0].appendChild(tmp); - tmp.styleSheet.cssText = opts.str; - } - else { - tmp.styleSheet.cssText = tmp.styleSheet.cssText + " " + opts.str; - } - } - else { - tmp.appendChild(document.createTextNode(opts.str)); - document.getElementsByTagName("head")[0].appendChild(tmp); - } - return tmp.sheet || tmp.styleSheet; - } - if(opts.url) { - if(document.createStyleSheet) { - try { tmp = document.createStyleSheet(opts.url); } catch (e) { } - } - else { - tmp = document.createElement('link'); - tmp.rel = 'stylesheet'; - tmp.type = 'text/css'; - tmp.media = "all"; - tmp.href = opts.url; - document.getElementsByTagName("head")[0].appendChild(tmp); - return tmp.styleSheet; - } - } - } - }; - - // private variables - var instances = [], // instance array (used by $.jstree.reference/create/focused) - focused_instance = -1, // the index in the instance array of the currently focused instance - plugins = {}, // list of included plugins - prepared_move = {}; // for the move_node function - - // jQuery plugin wrapper (thanks to jquery UI widget function) - $.fn.jstree = function (settings) { - var isMethodCall = (typeof settings == 'string'), // is this a method call like $().jstree("open_node") - args = Array.prototype.slice.call(arguments, 1), - returnValue = this; - - // if a method call execute the method on all selected instances - if(isMethodCall) { - if(settings.substring(0, 1) == '_') { return returnValue; } - this.each(function() { - var instance = instances[$.data(this, "jstree_instance_id")], - methodValue = (instance && $.isFunction(instance[settings])) ? instance[settings].apply(instance, args) : instance; - if(typeof methodValue !== "undefined" && (settings.indexOf("is_") === 0 || (methodValue !== true && methodValue !== false))) { returnValue = methodValue; return false; } - }); - } - else { - this.each(function() { - // extend settings and allow for multiple hashes and $.data - var instance_id = $.data(this, "jstree_instance_id"), - a = [], - b = settings ? $.extend({}, true, settings) : {}, - c = $(this), - s = false, - t = []; - a = a.concat(args); - if(c.data("jstree")) { a.push(c.data("jstree")); } - b = a.length ? $.extend.apply(null, [true, b].concat(a)) : b; - - // if an instance already exists, destroy it first - if(typeof instance_id !== "undefined" && instances[instance_id]) { instances[instance_id].destroy(); } - // push a new empty object to the instances array - instance_id = parseInt(instances.push({}),10) - 1; - // store the jstree instance id to the container element - $.data(this, "jstree_instance_id", instance_id); - // clean up all plugins - b.plugins = $.isArray(b.plugins) ? b.plugins : $.jstree.defaults.plugins.slice(); - b.plugins.unshift("core"); - // only unique plugins - b.plugins = b.plugins.sort().join(",,").replace(/(,|^)([^,]+)(,,\2)+(,|$)/g,"$1$2$4").replace(/,,+/g,",").replace(/,$/,"").split(","); - - // extend defaults with passed data - s = $.extend(true, {}, $.jstree.defaults, b); - s.plugins = b.plugins; - $.each(plugins, function (i, val) { - if($.inArray(i, s.plugins) === -1) { s[i] = null; delete s[i]; } - else { t.push(i); } - }); - s.plugins = t; - - // push the new object to the instances array (at the same time set the default classes to the container) and init - instances[instance_id] = new $.jstree._instance(instance_id, $(this).addClass("jstree jstree-" + instance_id), s); - // init all activated plugins for this instance - $.each(instances[instance_id]._get_settings().plugins, function (i, val) { instances[instance_id].data[val] = {}; }); - $.each(instances[instance_id]._get_settings().plugins, function (i, val) { if(plugins[val]) { plugins[val].__init.apply(instances[instance_id]); } }); - // initialize the instance - setTimeout(function() { if(instances[instance_id]) { instances[instance_id].init(); } }, 0); - }); - } - // return the jquery selection (or if it was a method call that returned a value - the returned value) - return returnValue; - }; - // object to store exposed functions and objects - $.jstree = { - defaults : { - plugins : [] - }, - _focused : function () { return instances[focused_instance] || null; }, - _reference : function (needle) { - // get by instance id - if(instances[needle]) { return instances[needle]; } - // get by DOM (if still no luck - return null - var o = $(needle); - if(!o.length && typeof needle === "string") { o = $("#" + needle); } - if(!o.length) { return null; } - return instances[o.closest(".jstree").data("jstree_instance_id")] || null; - }, - _instance : function (index, container, settings) { - // for plugins to store data in - this.data = { core : {} }; - this.get_settings = function () { return $.extend(true, {}, settings); }; - this._get_settings = function () { return settings; }; - this.get_index = function () { return index; }; - this.get_container = function () { return container; }; - this.get_container_ul = function () { return container.children("ul:eq(0)"); }; - this._set_settings = function (s) { - settings = $.extend(true, {}, settings, s); - }; - }, - _fn : { }, - plugin : function (pname, pdata) { - pdata = $.extend({}, { - __init : $.noop, - __destroy : $.noop, - _fn : {}, - defaults : false - }, pdata); - plugins[pname] = pdata; - - $.jstree.defaults[pname] = pdata.defaults; - $.each(pdata._fn, function (i, val) { - val.plugin = pname; - val.old = $.jstree._fn[i]; - $.jstree._fn[i] = function () { - var rslt, - func = val, - args = Array.prototype.slice.call(arguments), - evnt = new $.Event("before.jstree"), - rlbk = false; - - if(this.data.core.locked === true && i !== "unlock" && i !== "is_locked") { return; } - - // Check if function belongs to the included plugins of this instance - do { - if(func && func.plugin && $.inArray(func.plugin, this._get_settings().plugins) !== -1) { break; } - func = func.old; - } while(func); - if(!func) { return; } - - // context and function to trigger events, then finally call the function - if(i.indexOf("_") === 0) { - rslt = func.apply(this, args); - } - else { - rslt = this.get_container().triggerHandler(evnt, { "func" : i, "inst" : this, "args" : args, "plugin" : func.plugin }); - if(rslt === false) { return; } - if(typeof rslt !== "undefined") { args = rslt; } - - rslt = func.apply( - $.extend({}, this, { - __callback : function (data) { - this.get_container().triggerHandler( i + '.jstree', { "inst" : this, "args" : args, "rslt" : data, "rlbk" : rlbk }); - }, - __rollback : function () { - rlbk = this.get_rollback(); - return rlbk; - }, - __call_old : function (replace_arguments) { - return func.old.apply(this, (replace_arguments ? Array.prototype.slice.call(arguments, 1) : args ) ); - } - }), args); - } - - // return the result - return rslt; - }; - $.jstree._fn[i].old = val.old; - $.jstree._fn[i].plugin = pname; - }); - }, - rollback : function (rb) { - if(rb) { - if(!$.isArray(rb)) { rb = [ rb ]; } - $.each(rb, function (i, val) { - instances[val.i].set_rollback(val.h, val.d); - }); - } - } - }; - // set the prototype for all instances - $.jstree._fn = $.jstree._instance.prototype = {}; - - // load the css when DOM is ready - $(function() { - // code is copied from jQuery ($.browser is deprecated + there is a bug in IE) - var u = navigator.userAgent.toLowerCase(), - v = (u.match( /.+?(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1], - css_string = '' + - '.jstree ul, .jstree li { display:block; margin:0 0 0 0; padding:0 0 0 0; list-style-type:none; } ' + - '.jstree li { display:block; min-height:18px; line-height:18px; white-space:nowrap; margin-left:18px; min-width:18px; } ' + - '.jstree-rtl li { margin-left:0; margin-right:18px; } ' + - '.jstree > ul > li { margin-left:0px; } ' + - '.jstree-rtl > ul > li { margin-right:0px; } ' + - '.jstree ins { display:inline-block; text-decoration:none; width:18px; height:18px; margin:0 0 0 0; padding:0; } ' + - '.jstree a { display:inline-block; line-height:16px; height:16px; color:black; white-space:nowrap; text-decoration:none; padding:1px 2px; margin:0; } ' + - '.jstree a:focus { outline: none; } ' + - '.jstree a > ins { height:16px; width:16px; } ' + - '.jstree a > .jstree-icon { margin-right:3px; } ' + - '.jstree-rtl a > .jstree-icon { margin-left:3px; margin-right:0; } ' + - 'li.jstree-open > ul { display:block; } ' + - 'li.jstree-closed > ul { display:none; } '; - // Correct IE 6 (does not support the > CSS selector) - if(/msie/.test(u) && parseInt(v, 10) == 6) { - is_ie6 = true; - - // fix image flicker and lack of caching - try { - document.execCommand("BackgroundImageCache", false, true); - } catch (err) { } - - css_string += '' + - '.jstree li { height:18px; margin-left:0; margin-right:0; } ' + - '.jstree li li { margin-left:18px; } ' + - '.jstree-rtl li li { margin-left:0px; margin-right:18px; } ' + - 'li.jstree-open ul { display:block; } ' + - 'li.jstree-closed ul { display:none !important; } ' + - '.jstree li a { display:inline; border-width:0 !important; padding:0px 2px !important; } ' + - '.jstree li a ins { height:16px; width:16px; margin-right:3px; } ' + - '.jstree-rtl li a ins { margin-right:0px; margin-left:3px; } '; - } - // Correct IE 7 (shifts anchor nodes onhover) - if(/msie/.test(u) && parseInt(v, 10) == 7) { - is_ie7 = true; - css_string += '.jstree li a { border-width:0 !important; padding:0px 2px !important; } '; - } - // correct ff2 lack of display:inline-block - if(!/compatible/.test(u) && /mozilla/.test(u) && parseFloat(v, 10) < 1.9) { - is_ff2 = true; - css_string += '' + - '.jstree ins { display:-moz-inline-box; } ' + - '.jstree li { line-height:12px; } ' + // WHY?? - '.jstree a { display:-moz-inline-box; } ' + - '.jstree .jstree-no-icons .jstree-checkbox { display:-moz-inline-stack !important; } '; - /* this shouldn't be here as it is theme specific */ - } - // the default stylesheet - $.vakata.css.add_sheet({ str : css_string, title : "jstree" }); - }); - - // core functions (open, close, create, update, delete) - $.jstree.plugin("core", { - __init : function () { - this.data.core.locked = false; - this.data.core.to_open = this.get_settings().core.initially_open; - this.data.core.to_load = this.get_settings().core.initially_load; - }, - defaults : { - html_titles : false, - animation : 500, - initially_open : [], - initially_load : [], - open_parents : true, - notify_plugins : true, - rtl : false, - load_open : false, - strings : { - loading : "Loading ...", - new_node : "New node", - multiple_selection : "Multiple selection" - } - }, - _fn : { - init : function () { - this.set_focus(); - if(this._get_settings().core.rtl) { - this.get_container().addClass("jstree-rtl").css("direction", "rtl"); - } - this.get_container().html(""); - this.data.core.li_height = this.get_container_ul().find("li.jstree-closed, li.jstree-leaf").eq(0).height() || 18; - - this.get_container() - .delegate("li > ins", "click.jstree", $.proxy(function (event) { - var trgt = $(event.target); - // if(trgt.is("ins") && event.pageY - trgt.offset().top < this.data.core.li_height) { this.toggle_node(trgt); } - this.toggle_node(trgt); - }, this)) - .bind("mousedown.jstree", $.proxy(function () { - this.set_focus(); // This used to be setTimeout(set_focus,0) - why? - }, this)) - .bind("dblclick.jstree", function (event) { - var sel; - if(document.selection && document.selection.empty) { document.selection.empty(); } - else { - if(window.getSelection) { - sel = window.getSelection(); - try { - sel.removeAllRanges(); - sel.collapse(); - } catch (err) { } - } - } - }); - if(this._get_settings().core.notify_plugins) { - this.get_container() - .bind("load_node.jstree", $.proxy(function (e, data) { - var o = this._get_node(data.rslt.obj), - t = this; - if(o === -1) { o = this.get_container_ul(); } - if(!o.length) { return; } - o.find("li").each(function () { - var th = $(this); - if(th.data("jstree")) { - $.each(th.data("jstree"), function (plugin, values) { - if(t.data[plugin] && $.isFunction(t["_" + plugin + "_notify"])) { - t["_" + plugin + "_notify"].call(t, th, values); - } - }); - } - }); - }, this)); - } - if(this._get_settings().core.load_open) { - this.get_container() - .bind("load_node.jstree", $.proxy(function (e, data) { - var o = this._get_node(data.rslt.obj), - t = this; - if(o === -1) { o = this.get_container_ul(); } - if(!o.length) { return; } - o.find("li.jstree-open:not(:has(ul))").each(function () { - t.load_node(this, $.noop, $.noop); - }); - }, this)); - } - this.__callback(); - this.load_node(-1, function () { this.loaded(); this.reload_nodes(); }); - }, - destroy : function () { - var i, - n = this.get_index(), - s = this._get_settings(), - _this = this; - - $.each(s.plugins, function (i, val) { - try { plugins[val].__destroy.apply(_this); } catch(err) { } - }); - this.__callback(); - // set focus to another instance if this one is focused - if(this.is_focused()) { - for(i in instances) { - if(instances.hasOwnProperty(i) && i != n) { - instances[i].set_focus(); - break; - } - } - } - // if no other instance found - if(n === focused_instance) { focused_instance = -1; } - // remove all traces of jstree in the DOM (only the ones set using jstree*) and cleans all events - this.get_container() - .unbind(".jstree") - .undelegate(".jstree") - .removeData("jstree_instance_id") - .find("[class^='jstree']") - .addBack() - .attr("class", function () { return this.className.replace(/jstree[^ ]*|$/ig,''); }); - $(document) - .unbind(".jstree-" + n) - .undelegate(".jstree-" + n); - // remove the actual data - instances[n] = null; - delete instances[n]; - }, - - _core_notify : function (n, data) { - if(data.opened) { - this.open_node(n, false, true); - } - }, - - lock : function () { - this.data.core.locked = true; - this.get_container().children("ul").addClass("jstree-locked").css("opacity","0.7"); - this.__callback({}); - }, - unlock : function () { - this.data.core.locked = false; - this.get_container().children("ul").removeClass("jstree-locked").css("opacity","1"); - this.__callback({}); - }, - is_locked : function () { return this.data.core.locked; }, - save_opened : function () { - var _this = this; - this.data.core.to_open = []; - this.get_container_ul().find("li.jstree-open").each(function () { - if(this.id) { _this.data.core.to_open.push("#" + this.id.toString().replace(/^#/,"").replace(/\\\//g,"/").replace(/\//g,"\\\/").replace(/\\\./g,".").replace(/\./g,"\\.").replace(/\:/g,"\\:")); } - }); - this.__callback(_this.data.core.to_open); - }, - save_loaded : function () { }, - reload_nodes : function (is_callback) { - var _this = this, - done = true, - current = [], - remaining = []; - if(!is_callback) { - this.data.core.reopen = false; - this.data.core.refreshing = true; - this.data.core.to_open = $.map($.makeArray(this.data.core.to_open), function (n) { return "#" + n.toString().replace(/^#/,"").replace(/\\\//g,"/").replace(/\//g,"\\\/").replace(/\\\./g,".").replace(/\./g,"\\.").replace(/\:/g,"\\:"); }); - this.data.core.to_load = $.map($.makeArray(this.data.core.to_load), function (n) { return "#" + n.toString().replace(/^#/,"").replace(/\\\//g,"/").replace(/\//g,"\\\/").replace(/\\\./g,".").replace(/\./g,"\\.").replace(/\:/g,"\\:"); }); - if(this.data.core.to_open.length) { - this.data.core.to_load = this.data.core.to_load.concat(this.data.core.to_open); - } - } - if(this.data.core.to_load.length) { - $.each(this.data.core.to_load, function (i, val) { - if(val == "#") { return true; } - if($(val).length) { current.push(val); } - else { remaining.push(val); } - }); - if(current.length) { - this.data.core.to_load = remaining; - $.each(current, function (i, val) { - if(!_this._is_loaded(val)) { - _this.load_node(val, function () { _this.reload_nodes(true); }, function () { _this.reload_nodes(true); }); - done = false; - } - }); - } - } - if(this.data.core.to_open.length) { - $.each(this.data.core.to_open, function (i, val) { - _this.open_node(val, false, true); - }); - } - if(done) { - // TODO: find a more elegant approach to syncronizing returning requests - if(this.data.core.reopen) { clearTimeout(this.data.core.reopen); } - this.data.core.reopen = setTimeout(function () { _this.__callback({}, _this); }, 50); - this.data.core.refreshing = false; - this.reopen(); - } - }, - reopen : function () { - var _this = this; - if(this.data.core.to_open.length) { - $.each(this.data.core.to_open, function (i, val) { - _this.open_node(val, false, true); - }); - } - this.__callback({}); - }, - refresh : function (obj) { - var _this = this; - this.save_opened(); - if(!obj) { obj = -1; } - obj = this._get_node(obj); - if(!obj) { obj = -1; } - if(obj !== -1) { obj.children("UL").remove(); } - else { this.get_container_ul().empty(); } - this.load_node(obj, function () { _this.__callback({ "obj" : obj}); _this.reload_nodes(); }); - }, - // Dummy function to fire after the first load (so that there is a jstree.loaded event) - loaded : function () { - this.__callback(); - }, - // deal with focus - set_focus : function () { - if(this.is_focused()) { return; } - var f = $.jstree._focused(); - if(f) { f.unset_focus(); } - - this.get_container().addClass("jstree-focused"); - focused_instance = this.get_index(); - this.__callback(); - }, - is_focused : function () { - return focused_instance == this.get_index(); - }, - unset_focus : function () { - if(this.is_focused()) { - this.get_container().removeClass("jstree-focused"); - focused_instance = -1; - } - this.__callback(); - }, - - // traverse - _get_node : function (obj) { - var $obj = $(obj, this.get_container()); - if($obj.is(".jstree") || obj == -1) { return -1; } - $obj = $obj.closest("li", this.get_container()); - return $obj.length ? $obj : false; - }, - _get_next : function (obj, strict) { - obj = this._get_node(obj); - if(obj === -1) { return this.get_container().find("> ul > li:first-child"); } - if(!obj.length) { return false; } - if(strict) { return (obj.nextAll("li").size() > 0) ? obj.nextAll("li:eq(0)") : false; } - - if(obj.hasClass("jstree-open")) { return obj.find("li:eq(0)"); } - else if(obj.nextAll("li").size() > 0) { return obj.nextAll("li:eq(0)"); } - else { return obj.parentsUntil(".jstree","li").next("li").eq(0); } - }, - _get_prev : function (obj, strict) { - obj = this._get_node(obj); - if(obj === -1) { return this.get_container().find("> ul > li:last-child"); } - if(!obj.length) { return false; } - if(strict) { return (obj.prevAll("li").length > 0) ? obj.prevAll("li:eq(0)") : false; } - - if(obj.prev("li").length) { - obj = obj.prev("li").eq(0); - while(obj.hasClass("jstree-open")) { obj = obj.children("ul:eq(0)").children("li:last"); } - return obj; - } - else { var o = obj.parentsUntil(".jstree","li:eq(0)"); return o.length ? o : false; } - }, - _get_parent : function (obj) { - obj = this._get_node(obj); - if(obj == -1 || !obj.length) { return false; } - var o = obj.parentsUntil(".jstree", "li:eq(0)"); - return o.length ? o : -1; - }, - _get_children : function (obj) { - obj = this._get_node(obj); - if(obj === -1) { return this.get_container().children("ul:eq(0)").children("li"); } - if(!obj.length) { return false; } - return obj.children("ul:eq(0)").children("li"); - }, - get_path : function (obj, id_mode) { - var p = [], - _this = this; - obj = this._get_node(obj); - if(obj === -1 || !obj || !obj.length) { return false; } - obj.parentsUntil(".jstree", "li").each(function () { - p.push( id_mode ? this.id : _this.get_text(this) ); - }); - p.reverse(); - p.push( id_mode ? obj.attr("id") : this.get_text(obj) ); - return p; - }, - - // string functions - _get_string : function (key) { - return this._get_settings().core.strings[key] || key; - }, - - is_open : function (obj) { obj = this._get_node(obj); return obj && obj !== -1 && obj.hasClass("jstree-open"); }, - is_closed : function (obj) { obj = this._get_node(obj); return obj && obj !== -1 && obj.hasClass("jstree-closed"); }, - is_leaf : function (obj) { obj = this._get_node(obj); return obj && obj !== -1 && obj.hasClass("jstree-leaf"); }, - correct_state : function (obj) { - obj = this._get_node(obj); - if(!obj || obj === -1) { return false; } - obj.removeClass("jstree-closed jstree-open").addClass("jstree-leaf").children("ul").remove(); - this.__callback({ "obj" : obj }); - }, - // open/close - open_node : function (obj, callback, skip_animation) { - obj = this._get_node(obj); - if(!obj.length) { return false; } - if(!obj.hasClass("jstree-closed")) { if(callback) { callback.call(); } return false; } - var s = skip_animation || is_ie6 ? 0 : this._get_settings().core.animation, - t = this; - if(!this._is_loaded(obj)) { - obj.children("a").addClass("jstree-loading"); - this.load_node(obj, function () { t.open_node(obj, callback, skip_animation); }, callback); - } - else { - if(this._get_settings().core.open_parents) { - obj.parentsUntil(".jstree",".jstree-closed").each(function () { - t.open_node(this, false, true); - }); - } - if(s) { obj.children("ul").css("display","none"); } - obj.removeClass("jstree-closed").addClass("jstree-open").children("a").removeClass("jstree-loading"); - if(s) { obj.children("ul").stop(true, true).slideDown(s, function () { this.style.display = ""; t.after_open(obj); }); } - else { t.after_open(obj); } - this.__callback({ "obj" : obj }); - if(callback) { callback.call(); } - } - }, - after_open : function (obj) { this.__callback({ "obj" : obj }); }, - close_node : function (obj, skip_animation) { - obj = this._get_node(obj); - var s = skip_animation || is_ie6 ? 0 : this._get_settings().core.animation, - t = this; - if(!obj.length || !obj.hasClass("jstree-open")) { return false; } - if(s) { obj.children("ul").attr("style","display:block !important"); } - obj.removeClass("jstree-open").addClass("jstree-closed"); - if(s) { obj.children("ul").stop(true, true).slideUp(s, function () { this.style.display = ""; t.after_close(obj); }); } - else { t.after_close(obj); } - this.__callback({ "obj" : obj }); - }, - after_close : function (obj) { this.__callback({ "obj" : obj }); }, - toggle_node : function (obj) { - obj = this._get_node(obj); - if(obj.hasClass("jstree-closed")) { return this.open_node(obj); } - if(obj.hasClass("jstree-open")) { return this.close_node(obj); } - }, - open_all : function (obj, do_animation, original_obj) { - obj = obj ? this._get_node(obj) : -1; - if(!obj || obj === -1) { obj = this.get_container_ul(); } - if(original_obj) { - obj = obj.find("li.jstree-closed"); - } - else { - original_obj = obj; - if(obj.is(".jstree-closed")) { obj = obj.find("li.jstree-closed").addBack(); } - else { obj = obj.find("li.jstree-closed"); } - } - var _this = this; - obj.each(function () { - var __this = this; - if(!_this._is_loaded(this)) { _this.open_node(this, function() { _this.open_all(__this, do_animation, original_obj); }, !do_animation); } - else { _this.open_node(this, false, !do_animation); } - }); - // so that callback is fired AFTER all nodes are open - if(original_obj.find('li.jstree-closed').length === 0) { this.__callback({ "obj" : original_obj }); } - }, - close_all : function (obj, do_animation) { - var _this = this; - obj = obj ? this._get_node(obj) : this.get_container(); - if(!obj || obj === -1) { obj = this.get_container_ul(); } - obj.find("li.jstree-open").addBack().each(function () { _this.close_node(this, !do_animation); }); - this.__callback({ "obj" : obj }); - }, - clean_node : function (obj) { - obj = obj && obj != -1 ? $(obj) : this.get_container_ul(); - obj = obj.is("li") ? obj.find("li").addBack() : obj.find("li"); - obj.removeClass("jstree-last") - .filter("li:last-child").addClass("jstree-last").end() - .filter(":has(li)") - .not(".jstree-open").removeClass("jstree-leaf").addClass("jstree-closed"); - obj.not(".jstree-open, .jstree-closed").addClass("jstree-leaf").children("ul").remove(); - this.__callback({ "obj" : obj }); - }, - // rollback - get_rollback : function () { - this.__callback(); - return { i : this.get_index(), h : this.get_container().children("ul").clone(true), d : this.data }; - }, - set_rollback : function (html, data) { - this.get_container().empty().append(html); - this.data = data; - this.__callback(); - }, - // Dummy functions to be overwritten by any datastore plugin included - load_node : function (obj, s_call, e_call) { this.__callback({ "obj" : obj }); }, - _is_loaded : function (obj) { return true; }, - - // Basic operations: create - create_node : function (obj, position, js, callback, is_loaded) { - obj = this._get_node(obj); - position = typeof position === "undefined" ? "last" : position; - var d = $("
          • "), - s = this._get_settings().core, - tmp; - - if(obj !== -1 && !obj.length) { return false; } - if(!is_loaded && !this._is_loaded(obj)) { this.load_node(obj, function () { this.create_node(obj, position, js, callback, true); }); return false; } - - this.__rollback(); - - if(typeof js === "string") { js = { "data" : js }; } - if(!js) { js = {}; } - if(js.attr) { d.attr(js.attr); } - if(js.metadata) { d.data(js.metadata); } - if(js.state) { d.addClass("jstree-" + js.state); } - if(!js.data) { js.data = this._get_string("new_node"); } - if(!$.isArray(js.data)) { tmp = js.data; js.data = []; js.data.push(tmp); } - $.each(js.data, function (i, m) { - tmp = $(""); - if($.isFunction(m)) { m = m.call(this, js); } - if(typeof m == "string") { tmp.attr('href','#')[ s.html_titles ? "html" : "text" ](m); } - else { - if(!m.attr) { m.attr = {}; } - if(!m.attr.href) { m.attr.href = '#'; } - tmp.attr(m.attr)[ s.html_titles ? "html" : "text" ](m.title); - if(m.language) { tmp.addClass(m.language); } - } - tmp.prepend(" "); - if(!m.icon && js.icon) { m.icon = js.icon; } - if(m.icon) { - if(m.icon.indexOf("/") === -1) { tmp.children("ins").addClass(m.icon); } - else { tmp.children("ins").css("background","url('" + m.icon + "') center center no-repeat"); } - } - d.append(tmp); - }); - d.prepend(" "); - if(obj === -1) { - obj = this.get_container(); - if(position === "before") { position = "first"; } - if(position === "after") { position = "last"; } - } - switch(position) { - case "before": obj.before(d); tmp = this._get_parent(obj); break; - case "after" : obj.after(d); tmp = this._get_parent(obj); break; - case "inside": - case "first" : - if(!obj.children("ul").length) { obj.append("