diff --git a/CHANGELOG.txt b/CHANGELOG.txt new file mode 100755 index 0000000..cab8e6b --- /dev/null +++ b/CHANGELOG.txt @@ -0,0 +1,26 @@ +--- version 2.0.0 --- + Ported base functions and logic + Added libraries + Attached JS with behaviour + Implemented Drupal coding standards + +--- version 1.0.4 --- + Added license routing + Added license form + Added licence number validation + Added user login and license number to default configurations + +--- version 1.0.3 --- + Added configuration + Remove configuration on module uninstalling + +--- version 1.0.2 --- + All defines replaced by const + Added settings form + +--- version 1.0.1 --- + Added permissions + Added routing for settings form + +--- version 1.0.0 --- + Added composer.json with information diff --git a/README.txt b/README.txt new file mode 100755 index 0000000..cf74868 --- /dev/null +++ b/README.txt @@ -0,0 +1,12 @@ +Installing LiveChat in Drupal: +1. Upload `livechat` directory to `/sites/all/modules/`. +2. Open your Drupal website. +3. Go to "Administration > Modules" and activate `LiveChat` +module (under `Other` category). +4. Go to "Administration > Configuration > Services > LiveChat" +and follow the instructions. + +--- + +For further information, please visit our website: +www.livechatinc.com diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..0df75c2 --- /dev/null +++ b/composer.json @@ -0,0 +1,10 @@ +{ + "name": "drupal/livechat", + "description": "LiveChat integration.", + "type": "drupal-module", + "homepage": "https://www.drupal.org/project/livechat", + "support": { + "issues": "https://www.drupal.org/project/issues/livechat" + }, + "license": "GPL-2.0" +} diff --git a/config/install/livechat.configuration.yml b/config/install/livechat.configuration.yml new file mode 100644 index 0000000..9f27425 --- /dev/null +++ b/config/install/livechat.configuration.yml @@ -0,0 +1,7 @@ +livechat_visibility: 0 +livechat_pages: "/admin\n/admin/*\n/node/add*\n/node/*/*\n/user/*/*\n" +livechat_exclude_system_paths: 1 +livechat_group: '' +livechat_enabled: 1 +livechat_login: '' +livechat_license: '' diff --git a/js/livechat.js b/js/livechat.js new file mode 100755 index 0000000..fbefb16 --- /dev/null +++ b/js/livechat.js @@ -0,0 +1,18 @@ +(function ($, Drupal, drupalSettings) { + 'use strict'; + + Drupal.behaviors.initLiveChat = { + attach: function (context, settings) { + if (drupalSettings.LiveChat) { + $('body').once('initLiveChat').each(function() { + window.__lc = drupalSettings.LiveChat || {}; + + var lc = document.createElement('script'); lc.type = 'text/javascript'; lc.async = true; + lc.src = ('https:' === document.location.protocol ? 'https://' : 'http://') + 'cdn.livechatinc.com/tracking.js'; + var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(lc, s); + }); + } + } + }; + +})(jQuery, Drupal, drupalSettings); diff --git a/livechat.info.yml b/livechat.info.yml new file mode 100644 index 0000000..a8a186b --- /dev/null +++ b/livechat.info.yml @@ -0,0 +1,5 @@ +name: LiveChat +description: LiveChat integration. +core: 8.x +type: module +configure: livechat.settings \ No newline at end of file diff --git a/livechat.install b/livechat.install new file mode 100755 index 0000000..2baa63d --- /dev/null +++ b/livechat.install @@ -0,0 +1,36 @@ + t('LiveChat'), + 'description' => t('A LiveChat license has not been installed. Please go to LiveChat settings.', [ + '@url' => Url::fromRoute('livechat.license')->toString(), + ]), + 'severity' => REQUIREMENT_ERROR, + 'value' => t('Not configured'), + ]; + } + } + + return $requirements; +} + +/** + * Implements hook_uninstall(). + */ +function livechat_uninstall() { + $config_factory = \Drupal::configFactory(); + $config_factory->getEditable('livechat.configuration')->delete(); +} diff --git a/livechat.libraries.yml b/livechat.libraries.yml new file mode 100644 index 0000000..74b91ab --- /dev/null +++ b/livechat.libraries.yml @@ -0,0 +1,4 @@ +livechat: + version: 1.x + js: + js/livechat.js: {} diff --git a/livechat.links.menu.yml b/livechat.links.menu.yml new file mode 100644 index 0000000..33dde99 --- /dev/null +++ b/livechat.links.menu.yml @@ -0,0 +1,5 @@ +livechat.settings: + title: LiveChat + description: 'Integrate LiveChat with your website.' + route_name: livechat.settings + parent: system.admin_config_services diff --git a/livechat.links.task.yml b/livechat.links.task.yml new file mode 100644 index 0000000..0a1e4ef --- /dev/null +++ b/livechat.links.task.yml @@ -0,0 +1,9 @@ +livechat.settings_tab: + route_name: livechat.settings + title: 'Settings' + base_route: livechat.settings + +livechat.license_tab: + route_name: livechat.license + title: 'License' + base_route: livechat.settings diff --git a/livechat.module b/livechat.module new file mode 100644 index 0000000..dec4902 --- /dev/null +++ b/livechat.module @@ -0,0 +1,176 @@ +get('livechat_enabled'))) { + // Validate permissions for current user and visibility for current path. + $user = \Drupal::currentUser(); + if ($user->hasPermission('use livechat') && livechat_check_visibility()) { + $license = $configs->get('livechat_license'); + $group_id = $configs->get('livechat_group'); + $settings = livechat_attach_js_settings($license, $group_id); + + if (!empty($settings)) { + $attachments['#attached']['drupalSettings']['LiveChat'] = $settings; + $attachments['#attached']['library'][] = 'livechat/livechat'; + } + } + } +} + +/** + * Return settings for attaching LiveChat tracking code. + * + * @param string $license + * LiveChat account license number. + * @param string $group_id + * Id of the website for multi-site. + * + * @return array + */ +function livechat_attach_js_settings($license, $group_id) { + $settings = []; + + if (!empty($license)) { + // Allow modules to add custom parameters and visitor information. + $params = []; + $visitor = []; + \Drupal::moduleHandler()->alter('livechat', $params, $visitor); + + $settings = [ + 'license' => $license, + 'params' => $params, + 'visitor' => $visitor, + ]; + + // Add in the group id if it has been set. + if (!empty($group_id)) { + $settings['LiveChat']['group'] = $group_id; + } + } + + return $settings; +} + +/** + * Checks whether LiveChat is correctly set up. + * + * @return mixed + * License number or FALSE. + */ +function livechat_is_installed() { + $configs = livechat_configs(); + $license = $configs->get('livechat_license'); + if (empty($license)) { + return FALSE; + } + + return livechat_validate_license($license); +} + +/** + * Validates a LiveChat license. + */ +function livechat_validate_license($license) { + if (empty($license)) { + return FALSE; + } + + return preg_match('/^[0-9]{1,20}$/', $license); +} + +/** + * LiveChat configurations. + * + * @return \Drupal\Core\Config\ImmutableConfig + * An immutable configuration object. + */ +function livechat_configs() { + $configs = \Drupal::config(LIVECHAT_CONFIGURATION_NAME); + + return $configs; +} + +/** + * Checks whether LiveChat should be visible on a path, according to settings. + * + * @param string $path + * The raw URI. + * + * @return bool + * Display or not LiveChat popup. + */ +function livechat_check_visibility($path = NULL) { + $configs = livechat_configs(); + + // Default to the current path. + if (!isset($path)) { + // Important: in Drupal 8 any path returns with trailing slash. + // 'currentPath' !== '/currentPath in matchPath result. + // Thanks for this pattern with ^. + $path = \Drupal::service('path.current')->getPath(); + } + + // Visibility settings. + $visibility = $configs->get('livechat_visibility'); + $pages = Unicode::strtolower($configs->get('livechat_pages')); + + // If $visibility is set to LIVECHAT_VISIBILITY_NOTLISTED and the setting for + // excluding system paths is enabled, add system paths to the list of pages. + $exclude_system_paths = $configs->get('livechat_exclude_system_paths'); + if ($visibility == LIVECHAT_VISIBILITY_NOTLISTED && $exclude_system_paths) { + $pages .= "\n" . LIVECHAT_VISIBILITY_SYSTEM_PATHS; + } + + // Get the path alias as lowercase. + $path_alias = \Drupal::service('path.alias_manager')->getAliasByPath($path); + $expanded_path = Unicode::strtolower($path_alias); + + // Compare the lowercase internal and lowercase path alias (if any). + $page_match = \Drupal::service('path.matcher')->matchPath($expanded_path, $pages); + if ($expanded_path != $path) { + $page_match = $page_match || \Drupal::service('path.matcher')->matchPath($path, $pages); + } + + // When $visibility has a value of 0 (LIVECHAT_VISIBILITY_NOTLISTED), the + // widget should be displayed on all pages except those listed. When set to 1 + // (LIVECHAT_VISIBILITY_LISTED), it should only be displayed on listed pages. + return !($visibility xor $page_match); +} diff --git a/livechat.permissions.yml b/livechat.permissions.yml new file mode 100644 index 0000000..ec9e662 --- /dev/null +++ b/livechat.permissions.yml @@ -0,0 +1,7 @@ +use livechat: + title: 'Use LiveChat' + description: 'Access the LiveChat widget' + +administer livechat: + title: 'Administer LiveChat' + description: 'Administer the LiveChat settings' diff --git a/livechat.routing.yml b/livechat.routing.yml new file mode 100644 index 0000000..fd17cb9 --- /dev/null +++ b/livechat.routing.yml @@ -0,0 +1,15 @@ +livechat.settings: + path: '/admin/config/services/livechat' + defaults: + _form: 'Drupal\livechat\Form\LiveChatSettingsForm' + _title: 'LiveChat' + requirements: + _permission: 'administer livechat' + +livechat.license: + path: '/admin/config/services/livechat/license' + defaults: + _form: 'Drupal\livechat\Form\LiveChatLicenseForm' + _title: 'License' + requirements: + _permission: 'administer livechat' diff --git a/src/Form/LiveChatLicenseForm.php b/src/Form/LiveChatLicenseForm.php new file mode 100644 index 0000000..a8e6487 --- /dev/null +++ b/src/Form/LiveChatLicenseForm.php @@ -0,0 +1,140 @@ +httpClient = $http_client; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('config.factory'), + $container->get('http_client') + ); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'livechat_license_form'; + } + + /** + * {@inheritdoc} + */ + protected function getEditableConfigNames() { + return [ + LIVECHAT_CONFIGURATION_NAME, + ]; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + $config = $this->config(LIVECHAT_CONFIGURATION_NAME); + + $form['account'] = [ + '#type' => 'details', + '#open' => TRUE, + '#title' => $this->t('Account details'), + ]; + $form['account']['login'] = [ + '#type' => 'email', + '#title' => $this->t('LiveChat login'), + '#description' => $this->t('User account login: my@email.com'), + '#default_value' => $config->get('livechat_login'), + '#required' => TRUE, + ]; + if (!empty($config->get('livechat_login')) && !empty($config->get('livechat_license'))) { + $form['account']['license_number'] = [ + '#type' => 'textfield', + '#title' => $this->t('License number'), + '#default_value' => $config->get('livechat_license'), + '#required' => TRUE, + '#attributes' => [ + 'disabled' => 'disabled', + ], + ]; + } + + return parent::buildForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function validateForm(array &$form, FormStateInterface $form_state) { + // Validate the license number. + $login = $form_state->getValue('login'); + if (!empty($login)) { + try { + $license_number_request = LIVECHAT_API_LICENSE_NUMBER . '/' . $login; + $response = $this->httpClient->get($license_number_request); + $data = $response->getBody()->getContents(); + if (!empty($data)) { + $json = Json::decode($data); + if (!empty($json['number'])) { + if (livechat_validate_license($json['number'])) { + $form_state->setValue('license_number', $json['number']); + } + else { + $form_state->setErrorByName('login', $this->t('LiveChat license number is incorrect.')); + } + } + elseif (!empty($json['error'])) { + $form_state->setErrorByName('login', $json['error']); + } + } + } + catch (RequestException $e) { + watchdog_exception('LiveChat', $e, $e->getMessage(), [], RfcLogLevel::ERROR, $license_number_request); + $form_state->setErrorByName('login', $e->getMessage()); + } + } + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $values = $form_state->getValues(); + $this->config(LIVECHAT_CONFIGURATION_NAME) + ->set('livechat_login', $values['login']) + ->set('livechat_license', $values['license_number']) + ->save(); + + parent::submitForm($form, $form_state); + } + +} diff --git a/src/Form/LiveChatSettingsForm.php b/src/Form/LiveChatSettingsForm.php new file mode 100644 index 0000000..1184aca --- /dev/null +++ b/src/Form/LiveChatSettingsForm.php @@ -0,0 +1,108 @@ +config(LIVECHAT_CONFIGURATION_NAME); + + $form['path'] = [ + '#type' => 'details', + '#title' => $this->t('Visibility'), + '#open' => TRUE, + ]; + + $form['path']['livechat_visibility'] = [ + '#type' => 'radios', + '#title' => $this->t('Show LiveChat on'), + '#options' => array( + LIVECHAT_VISIBILITY_NOTLISTED => $this->t('All pages except those listed'), + LIVECHAT_VISIBILITY_LISTED => $this->t('Only the listed pages'), + ), + '#default_value' => $config->get('livechat_visibility'), + ]; + + $form['path']['livechat_pages'] = [ + '#type' => 'textarea', + '#default_value' => $config->get('livechat_pages'), + '#description' => $this->t("Specify pages by using their paths. Enter one path per + line. The '*' character is a wildcard. Example paths are %blog for the blog + page and %blog-wildcard for every personal blog. %front is the + front page.", [ + '%blog' => 'blog', + '%blog-wildcard' => 'blog/*', + '%front' => '', + ]), + ]; + + $form['path']['livechat_exclude_system_paths'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Disable LiveChat on common system paths (recommended)'), + '#description' => $this->t('LiveChat will not trigger on the following paths: %paths', [ + '%paths' => str_replace("\n", ', ', LIVECHAT_VISIBILITY_SYSTEM_PATHS), + ]), + '#default_value' => $config->get('livechat_exclude_system_paths'), + ]; + + $form['livechat_group'] = [ + '#type' => 'textfield', + '#title' => $this->t('Group Id'), + '#description' => $this->t('If you are using LiveChat on more than one website, + enter the id of the website here'), + '#default_value' => $config->get('livechat_group'), + ]; + + $form['livechat_enabled'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Enable LiveChat'), + '#description' => $this->t('Uncheck this box to disable LiveChat.'), + '#default_value' => $config->get('livechat_enabled'), + ]; + + + return parent::buildForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $values = $form_state->getValues(); + $this->config(LIVECHAT_CONFIGURATION_NAME) + ->set('livechat_visibility', $values['livechat_visibility']) + ->set('livechat_pages', $values['livechat_pages']) + ->set('livechat_exclude_system_paths', $values['livechat_exclude_system_paths']) + ->set('livechat_group', $values['livechat_group']) + ->set('livechat_enabled', $values['livechat_enabled']) + ->save(); + + parent::submitForm($form, $form_state); + } + +}