diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..485dee6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0b21851 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2019-present FriendsOfOro, Aligent Consulting, and other contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..12eea89 --- /dev/null +++ b/README.md @@ -0,0 +1,99 @@ + Oro ReCAPTCHA Bundle +============================== +This bundle adds [Google ReCAPTCHA](https://developers.google.com/recaptcha/) protection for various Oro features. + +Features which can currently be protected are: +* Registration Form +* Contact Us Form + +Extends the Symfony [EWZRecaptchaBundle by excelwebzone](https://github.com/excelwebzone/EWZRecaptchaBundle) + +Requirements +------------------- +* Oro Platform 3.X + +Installation and Usage +------------------- +**NOTE: Adjust instructions as needed for your local environment** + +1. Install via Composer: + ```bash + composer require friendsoforo/oro-recaptcha-bundle + ``` +1. Update your config.yml: + ```yaml + # app/config/config.yml + + ewz_recaptcha: + public_key: here_is_your_public_key + private_key: here_is_your_private_key + # Not needed as "%kernel.default_locale%" is the default value for the locale key + locale_key: %kernel.default_locale% + ``` +1. Purge Oro cache: + ```bash + php bin/console cache:clear --env=prod + ``` +1. Login to Oro Admin +1. Navigate to **System Configuration => Integrations => ReCAPTCHA** +1. Configure the ReCAPTCHA widget and enabled/disable Protected Features +1. Save the configuration and verify that it is now appearing on the frontend website + +Testing in Development +------------------- +Copy the `config.yml` values into `config_dev.yml` and replace the public/private keys with the test keys provided by Google: +https://developers.google.com/recaptcha/docs/faq#id-like-to-run-automated-tests-with-recaptcha-what-should-i-do + +The widget should render on the forms, but will be overlaid with the text: + +_"This reCAPTCHA is for testing purposes only. Please report to the site admin if you are seeing this. +"_ + +Adding to new Form Types +------------------- +1. Create a new Form Type Extension which extends `HackOro\RecaptchaBundle\Form\Extension\AbstractRecaptchaTypeExtension` + ```php + + * @copyright 2019 Aligent Consulting & Friends of Oro + * @license http://opensource.org/licenses/MIT MIT + */ + +namespace HackOro\RecaptchaBundle\DependencyInjection; + +use Oro\Bundle\ConfigBundle\Config\ConfigManager; +use Oro\Bundle\ConfigBundle\DependencyInjection\SettingsBuilder; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; + +class Configuration implements ConfigurationInterface +{ + const PUBLIC_KEY = 'public_key'; + const PRIVATE_KEY = 'private_key'; + const THEME = 'theme'; + const SIZE = 'size'; + const PROTECT_REGISTRATION = 'protect_registration'; + const PROTECT_CONTACT_FORM = 'protect_contact_form'; + + /** + * Generates the configuration tree builder. + * + * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder + */ + public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root(HackOroRecaptchaExtension::ALIAS); + + SettingsBuilder::append( + $rootNode, + [ + self::PUBLIC_KEY => ['type' => 'text', 'value' => ''], + self::PRIVATE_KEY => ['type' => 'text', 'value' => ''], + self::THEME => ['type' => 'scalar', 'value' => 'light'], // light/dark (default light) + self::SIZE => ['type' => 'scalar', 'value' => 'normal'], // normal/compact (default normal) + self::PROTECT_REGISTRATION => ['type' => 'boolean', 'value' => true], + self::PROTECT_CONTACT_FORM=> ['type' => 'boolean', 'value' => true], + ] + ); + return $treeBuilder; + } + + /** + * @param string $key + * @return string + */ + public static function getConfigKeyByName($key) + { + return implode(ConfigManager::SECTION_MODEL_SEPARATOR, [HackOroRecaptchaExtension::ALIAS, $key]); + } +} diff --git a/src/DependencyInjection/HackOroRecaptchaExtension.php b/src/DependencyInjection/HackOroRecaptchaExtension.php new file mode 100644 index 0000000..2550b84 --- /dev/null +++ b/src/DependencyInjection/HackOroRecaptchaExtension.php @@ -0,0 +1,33 @@ + + * @copyright 2019 Aligent Consulting & Friends of Oro + * @license http://opensource.org/licenses/MIT MIT + */ + +namespace HackOro\RecaptchaBundle\DependencyInjection; + +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader; +use Symfony\Component\HttpKernel\DependencyInjection\Extension; + +class HackOroRecaptchaExtension extends Extension +{ + const ALIAS = 'hack_oro_recaptcha'; + + /** + * @inheritdoc + */ + public function load(array $configs, ContainerBuilder $container) + { + $configuration = new Configuration(); + $config = $this->processConfiguration($configuration, $configs); + $container->prependExtensionConfig($this->getAlias(), $config); + + $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); + $loader->load('services.yml'); + } +} diff --git a/src/Form/Extension/AbstractRecaptchaTypeExtension.php b/src/Form/Extension/AbstractRecaptchaTypeExtension.php new file mode 100644 index 0000000..423283f --- /dev/null +++ b/src/Form/Extension/AbstractRecaptchaTypeExtension.php @@ -0,0 +1,94 @@ + + * @copyright 2019 Aligent Consulting & Friends of Oro + * @license http://opensource.org/licenses/MIT MIT + */ + +namespace HackOro\RecaptchaBundle\Form\Extension; + +use EWZ\Bundle\RecaptchaBundle\Form\Type\EWZRecaptchaType; +use EWZ\Bundle\RecaptchaBundle\Validator\Constraints\IsTrue as RecaptchaTrue; +use HackOro\RecaptchaBundle\DependencyInjection\Configuration; +use Oro\Bundle\ConfigBundle\Config\ConfigManager; +use Symfony\Component\Form\AbstractTypeExtension; +use Symfony\Component\Form\FormBuilderInterface; + +abstract class AbstractRecaptchaTypeExtension extends AbstractTypeExtension +{ + const DEFAULT_THEME = 'light'; + const DEFAULT_SIZE = 'normal'; + + /** @var ConfigManager */ + protected $configManager; + + public function buildForm(FormBuilderInterface $builder, array $options) + { + if ($this->isProtected()) { + + $builder->add('recaptcha', EWZRecaptchaType::class, [ + 'attr' => [ + 'options' => [ + 'theme' => $this->getTheme(), + 'type' => 'image', + 'size' => $this->getSize(), + ] + ], + 'mapped' => false, + 'constraints' => [ + new RecaptchaTrue(), + ], + ]); + } + } + + /** + * @param ConfigManager $configManager + */ + public function setConfigManager(ConfigManager $configManager) + { + $this->configManager = $configManager; + } + + /** + * @return mixed + */ + protected function getTheme() + { + return $this->getConfiguration(Configuration::THEME, self::DEFAULT_THEME); + } + + /** + * @return mixed + */ + protected function getSize() + { + return $this->getConfiguration(Configuration::SIZE, self::DEFAULT_SIZE); + } + + /** + * Get configuration option + * @param string $key + * @param $default + * @return mixed + */ + protected function getConfiguration(string $key, $default) + { + return $this->configManager->get(Configuration::getConfigKeyByName($key), $default); + } + + /** + * @return boolean + */ + abstract public function isProtected(); + + /** + * Returns the name of the type being extended. + * + * @return string The name of the type being extended + */ + abstract public function getExtendedType(); + +} \ No newline at end of file diff --git a/src/Form/Extension/ContactUsTypeExtension.php b/src/Form/Extension/ContactUsTypeExtension.php new file mode 100644 index 0000000..7bf7a55 --- /dev/null +++ b/src/Form/Extension/ContactUsTypeExtension.php @@ -0,0 +1,30 @@ + + * @copyright 2019 Aligent Consulting & Friends of Oro + * @license http://opensource.org/licenses/MIT MIT + */ + +namespace HackOro\RecaptchaBundle\Form\Extension; + +use HackOro\RecaptchaBundle\DependencyInjection\Configuration; +use Oro\Bundle\ContactUsBundle\Form\Type\ContactRequestType; + +class ContactUsTypeExtension extends AbstractRecaptchaTypeExtension +{ + public function getExtendedType() + { + return ContactRequestType::class; + } + + /** + * Protect the Contact Us Form? + * @return boolean + */ + public function isProtected() + { + return $this->getConfiguration(Configuration::PROTECT_CONTACT_FORM, false); + } +} diff --git a/src/Form/Extension/RegistrationTypeExtension.php b/src/Form/Extension/RegistrationTypeExtension.php new file mode 100644 index 0000000..061fc2e --- /dev/null +++ b/src/Form/Extension/RegistrationTypeExtension.php @@ -0,0 +1,30 @@ + + * @copyright 2019 Aligent Consulting & Friends of Oro + * @license http://opensource.org/licenses/MIT MIT + */ + +namespace HackOro\RecaptchaBundle\Form\Extension; + +use HackOro\RecaptchaBundle\DependencyInjection\Configuration; +use Oro\Bundle\CustomerBundle\Form\Type\FrontendCustomerUserRegistrationType; + +class RegistrationTypeExtension extends AbstractRecaptchaTypeExtension +{ + public function getExtendedType() + { + return FrontendCustomerUserRegistrationType::class; + } + + /** + * Protect the Registration Form? + * @return boolean + */ + public function isProtected() + { + return $this->getConfiguration(Configuration::PROTECT_REGISTRATION, false); + } +} diff --git a/src/HackOroRecaptchaBundle.php b/src/HackOroRecaptchaBundle.php new file mode 100644 index 0000000..198ab9f --- /dev/null +++ b/src/HackOroRecaptchaBundle.php @@ -0,0 +1,16 @@ + + * @copyright 2019 Aligent Consulting & Friends of Oro + * @license http://opensource.org/licenses/MIT MIT + */ + +namespace HackOro\RecaptchaBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class HackOroRecaptchaBundle extends Bundle +{ +} diff --git a/src/Resources/config/oro/bundles.yml b/src/Resources/config/oro/bundles.yml new file mode 100644 index 0000000..aed266e --- /dev/null +++ b/src/Resources/config/oro/bundles.yml @@ -0,0 +1,3 @@ +bundles: + - { name: EWZ\Bundle\RecaptchaBundle\EWZRecaptchaBundle, priority: 40 } + - { name: HackOro\RecaptchaBundle\HackOroRecaptchaBundle, priority: 50 } diff --git a/src/Resources/config/oro/system_configuration.yml b/src/Resources/config/oro/system_configuration.yml new file mode 100644 index 0000000..0041e98 --- /dev/null +++ b/src/Resources/config/oro/system_configuration.yml @@ -0,0 +1,64 @@ +system_configuration: + groups: + hack_oro: + title: 'hack_oro.label' + hack_oro_recaptcha: + icon: 'fa-id-card' + title: 'hack_oro.frontend.recaptcha.label' + recaptcha: + title: 'hack_oro.frontend.recaptcha.configuration.label' + protected_features: + title: 'hack_oro.frontend.recaptcha.protected_features_group.label' + fields: + hack_oro_recaptcha.theme: + data_type: string + type: Symfony\Component\Form\Extension\Core\Type\ChoiceType + options: + label: 'hack_oro.frontend.recaptcha.theme.label' + choices: + Light: light + Dark: dark + required: true + constraints: + - NotBlank: ~ + hack_oro_recaptcha.size: + data_type: string + type: Symfony\Component\Form\Extension\Core\Type\ChoiceType + options: + label: 'hack_oro.frontend.recaptcha.size.label' + choices: + Normal: normal + Compact: compact + required: true + constraints: + - NotBlank: ~ + hack_oro_recaptcha.protect_registration: + data_type: boolean + type: Oro\Bundle\ConfigBundle\Form\Type\ConfigCheckbox + options: + label: 'hack_oro.frontend.recaptcha.protect_registration.label' + required: false + resettable: false + hack_oro_recaptcha.protect_contact_form: + data_type: boolean + type: Oro\Bundle\ConfigBundle\Form\Type\ConfigCheckbox + options: + label: 'hack_oro.frontend.recaptcha.protect_contact_form.label' + required: false + resettable: false + tree: + system_configuration: + platform: + children: + integrations: + children: + hack_oro_recaptcha: + children: + recaptcha: + children: + - hack_oro_recaptcha.theme + - hack_oro_recaptcha.size + protected_features: + children: + - hack_oro_recaptcha.protect_registration + - hack_oro_recaptcha.protect_contact_form \ No newline at end of file diff --git a/src/Resources/config/services.yml b/src/Resources/config/services.yml new file mode 100644 index 0000000..5a1dc9d --- /dev/null +++ b/src/Resources/config/services.yml @@ -0,0 +1,14 @@ +services: + hack_oro_recaptcha.form.contactus_form_type_extension: + class: HackOro\RecaptchaBundle\Form\Extension\ContactUsTypeExtension + calls: + - [setConfigManager, ['@oro_config.user']] + tags: + - { name: form.type_extension, extended_type: Oro\Bundle\ContactUsBundle\Form\Type\ContactRequestType } + + hack_oro_recaptcha.form.registration_form_type_extension: + class: HackOro\RecaptchaBundle\Form\Extension\RegistrationTypeExtension + calls: + - [setConfigManager, ['@oro_config.user']] + tags: + - { name: form.type_extension, extended_type: Oro\Bundle\CustomerBundle\Form\Type\FrontendCustomerUserRegistrationType } \ No newline at end of file diff --git a/src/Resources/translations/messages.en.yml b/src/Resources/translations/messages.en.yml new file mode 100644 index 0000000..cfdd9c5 --- /dev/null +++ b/src/Resources/translations/messages.en.yml @@ -0,0 +1,12 @@ +hack_oro: + label: HackOro + frontend: + recaptcha: + label: ReCAPTCHA + configuration: + label: Configuration + theme.label: Theme + size.label: Size + protected_features_group.label: Protected Features + protect_registration.label: Protect Registration Form + protect_contact_form.label: Protect Contact Us Form \ No newline at end of file