Browse Source

refactored user blocks using ajax loaded drupal's html as template for vue

Bachir Soussi Chiadmi 5 years ago
parent
commit
d21bd5ef4e

+ 1 - 1
config/sync/block.block.languageswitcher.yml

@@ -9,7 +9,7 @@ dependencies:
 id: languageswitcher
 theme: materiotheme
 region: header_right
-weight: 0
+weight: -1
 provider: null
 plugin: 'language_block:language_interface'
 settings:

+ 13 - 5
config/sync/block.block.userblock.yml

@@ -1,20 +1,28 @@
-uuid: f6ac7bc7-be23-4375-a237-1d9a36edb17e
+uuid: 7d7e2454-1d8e-4e95-8110-985088634608
 langcode: en
 status: true
 dependencies:
   module:
-    - user_block
+    - materio_user
+    - user
   theme:
     - materiotheme
 id: userblock
 theme: materiotheme
 region: header_right
-weight: -1
+weight: -2
 provider: null
 plugin: user_block
 settings:
   id: user_block
   label: 'User block'
-  provider: user_block
+  provider: materio_user
   label_display: visible
-visibility: {  }
+visibility:
+  user_role:
+    id: user_role
+    roles:
+      authenticated: authenticated
+    negate: false
+    context_mapping:
+      user: '@user.current_user_context:current_user'

+ 1 - 1
config/sync/block.block.userlogin.yml

@@ -9,7 +9,7 @@ dependencies:
 id: userlogin
 theme: materiotheme
 region: header_right
-weight: -2
+weight: -3
 provider: null
 plugin: user_login_block
 settings:

+ 1 - 1
config/sync/core.extension.yml

@@ -96,6 +96,7 @@ module:
   materio_migrate: 0
   materio_samples: 0
   materio_sapi: 0
+  materio_user: 0
   matomo: 0
   maxlength: 0
   menu_block: 0
@@ -146,7 +147,6 @@ module:
   update: 0
   url_to_video_filter: 0
   user: 0
-  user_block: 0
   video_embed_field: 0
   video_embed_wysiwyg: 0
   views_bulk_edit: 0

+ 1 - 2
web/modules/custom/materio_sapi/materio_sapi.routing.yml

@@ -13,7 +13,7 @@ materio_sapi.search_form:
     _format: json
   requirements:
     _permission: 'access materio search'
-#
+
 # materio_sapi.materio_sapi_search_form:
 #   path: '/materio_sapi/form/materio_sapi_search'
 #   defaults:
@@ -21,4 +21,3 @@ materio_sapi.search_form:
 #     _title: 'MaterioSapiSearchForm'
 #   requirements:
 #     _access: 'TRUE'
-#

+ 5 - 0
web/modules/custom/materio_user/materio_user.info.yml

@@ -0,0 +1,5 @@
+name: 'materio_user'
+type: module
+description: ''
+core: 8.x
+package: 'Materio'

+ 57 - 0
web/modules/custom/materio_user/materio_user.module

@@ -0,0 +1,57 @@
+<?php
+
+/**
+ * @file
+ * Contains materio_user.module.
+ */
+
+use \Drupal\Core\Form\FormStateInterface;
+use \Drupal\Core\Block\BlockPluginInterface;
+
+/**
+ * implements hook_form_FORM_ID_alter()
+ *
+ */
+function materio_user_form_user_login_form_alter(&$form, FormStateInterface $form_state, $form_id) {
+  // Drupal::logger('materio_user')->notice(print_r($form, true));
+  $form['name']['#attributes'] += array(
+    "v-model" => "mail",
+    "@keyup.enter" => "login"
+  );
+
+  $form['pass']['#attributes'] = array(
+    "v-model" => "password",
+    "@keyup.enter" => "login"
+  );
+
+  $form['actions']['submit']['#attributes'] = array(
+    "@click.prevent" => "login"
+  );
+
+}
+
+/**
+ * implements hook_block_view_BASE_BLOCK_ID_alter()
+ *
+ * https://www.drupal.org/project/drupal/issues/2626224
+ */
+function materio_user_block_view_user_login_block_alter(array &$build, BlockPluginInterface $block) {
+  $build['#pre_render'][] = '_materio_user_user_login_block_pre_render';
+}
+
+function _materio_user_user_login_block_pre_render(array $build){
+  $user_links = &$build['content']['user_links'];
+  $items = &$user_links['#items'];
+  // ksm($items);
+  $items['create_account']['#url']->mergeOptions(array(
+    "attributes" => array(
+      "@click.prevent" => "create_account"
+    )
+  ));
+  $items['request_password']['#url']->mergeOptions(array(
+    'attributes' => array(
+      "@click.prevent" => "request_password"
+    )
+  ));
+  return $build;
+}

+ 15 - 0
web/modules/custom/materio_user/materio_user.routing.yml

@@ -0,0 +1,15 @@
+materio_user.login_form:
+  path: '/materio_user/login_form'
+  defaults:
+    _controller: '\Drupal\materio_user\Controller\AjaxLoginForm::getForm'
+    _format: json
+  requirements:
+    _access: 'TRUE'
+
+materio_user.login_block:
+  path: '/materio_user/login_block'
+  defaults:
+    _controller: '\Drupal\materio_user\Controller\AjaxLoginBlock::getBlock'
+    _format: json
+  requirements:
+    _access: 'TRUE'

+ 54 - 0
web/modules/custom/materio_user/src/Controller/AjaxLoginBlock.php

@@ -0,0 +1,54 @@
+<?php
+
+namespace Drupal\materio_user\Controller;
+
+use Drupal\Core\Controller\ControllerBase;
+use Symfony\Component\HttpFoundation\Request;
+use Drupal\block\Entity\Block;
+use Symfony\Component\HttpFoundation\JsonResponse;
+// use Drupal\Core\Cache\CacheableJsonResponse;
+// use Drupal\Core\Cache\CacheableMetadata;
+// use Drupal\core\render\RenderContext;
+
+
+/**
+ * Defines a route controller.
+ */
+class AjaxLoginBlock extends ControllerBase {
+
+  private function getBlockDefinition(){
+    // $language = \Drupal::languageManager()->getCurrentLanguage()->getId();
+    // \Drupal::logger('materio_user')->notice($language);
+    $this->bid = "userlogin";
+    $this->block = Block::load($this->bid);
+    $this->block_builded = \Drupal::entityManager()->getViewBuilder('block')->view($this->block);
+  }
+
+  /**
+   * Handler for getBlock request.
+   */
+  public function getBlock(Request $request) {
+
+    $this->getBlockDefinition();
+
+    $rendered = \Drupal::service('renderer')->renderRoot($this->block_builded);
+    $data = [
+      'rendered' => $rendered,
+      // '#cache' => [
+      //   'max-age' => \Drupal\Core\Cache\Cache::PERMANENT,
+      //   'tags' => [
+      //     'materio_sapi-search_form-cache',
+      //   ]
+      // ]
+    ];
+
+    $response = new JsonResponse();
+    $response->setData($data);
+    // $response = new CacheableJsonResponse($data);
+    // $response->addCacheableDependency(CacheableMetadata::createFromRenderArray($data));
+
+    return $response;
+  }
+
+
+}

+ 82 - 0
web/modules/custom/materio_user/src/Controller/AjaxLoginForm.php

@@ -0,0 +1,82 @@
+<?php
+// https://www.qed42.com/blog/autocomplete-drupal-8
+// https://www.drupal.org/docs/8/modules/search-api/developer-documentation/executing-a-search-in-code
+
+namespace Drupal\materio_user\Controller;
+
+use Drupal\Core\Controller\ControllerBase;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Form\FormBuilder;
+use Drupal\Core\Form\FormState;
+use Symfony\Component\HttpFoundation\JsonResponse;
+use Symfony\Component\HttpFoundation\Request;
+// use Drupal\Core\Cache\CacheableJsonResponse;
+// use Drupal\Core\Cache\CacheableMetadata;
+use Drupal\core\render\RenderContext;
+
+
+/**
+ * Defines a route controller for entity autocomplete form elements.
+ */
+class AjaxLoginForm extends ControllerBase {
+
+  /**
+   * The form builder.
+   *
+   * @var \Drupal\Core\Form\FormBuilder
+   */
+  protected $formBuilder;
+
+  /**
+   * Construct.
+   */
+  public function __construct(FormBuilder $formBuilder) {
+    $this->formBuilder = $formBuilder;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('form_builder')
+    );
+  }
+
+  private function getFormDefinition(){
+    // $language = \Drupal::languageManager()->getCurrentLanguage()->getId();
+    // \Drupal::logger('materio_user')->notice($language);
+    $this->form_builded = $this->formBuilder->getForm('Drupal\user\Form\UserLoginForm');
+  }
+
+  /**
+   * Handler for getform request.
+   */
+  public function getForm(Request $request) {
+
+    $this->getFormDefinition();
+
+    $rendered = render($this->form_builded);
+    // $form_builded = $this->form_builded;
+    // $rendered = \Drupal::service('renderer')->executeInRenderContext(new RenderContext(), function () use ($form_builded) {
+    //     return render($form_builded);
+    //   });
+    $data = [
+      'rendered' => $rendered,
+      // '#cache' => [
+      //   'max-age' => \Drupal\Core\Cache\Cache::PERMANENT,
+      //   'tags' => [
+      //     'materio_sapi-search_form-cache',
+      //   ]
+      // ]
+    ];
+
+    $response = new JsonResponse();
+    $response->setData($data);
+    // $response = new CacheableJsonResponse($data);
+    // $response->addCacheableDependency(CacheableMetadata::createFromRenderArray($data));
+
+    return $response;
+  }
+
+}

+ 1 - 1
web/modules/custom/user_block/src/Plugin/Block/UserBlock.php → web/modules/custom/materio_user/src/Plugin/Block/UserBlock.php

@@ -1,6 +1,6 @@
 <?php
 
-namespace Drupal\user_block\Plugin\Block;
+namespace Drupal\materio_user\Plugin\Block;
 
 use Drupal\Core\Session\AccountProxy;
 use Drupal\Core\Session\AccountProxyInterface;

+ 0 - 5
web/modules/custom/user_block/user_block.info.yml

@@ -1,5 +0,0 @@
-name: 'user_block'
-type: module
-description: 'Provide a block with user name/email and logout link'
-core: 8.x
-package: 'Custom'

+ 0 - 24
web/modules/custom/user_block/user_block.module

@@ -1,24 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains user_block.module.
- */
-
-use Drupal\Core\Routing\RouteMatchInterface;
-
-/**
- * Implements hook_help().
- */
-// function user_block_help($route_name, RouteMatchInterface $route_match) {
-//   switch ($route_name) {
-//     // Main module help for the user_block module.
-//     case 'help.page.user_block':
-//       $output = '';
-//       $output .= '<h3>' . t('About') . '</h3>';
-//       $output .= '<p>' . t('Provide a block with user name/email and logout link') . '</p>';
-//       return $output;
-//
-//     default:
-//   }
-// }

File diff suppressed because it is too large
+ 8 - 0
web/themes/custom/materiotheme/assets/dist/main.js


+ 12 - 29
web/themes/custom/materiotheme/assets/scripts/main.js

@@ -30,45 +30,27 @@ import 'theme/assets/styles/main.scss'
     }
 
     function initVues(){
-      initUserVBlock()
+      initVUserBlock()
       initVMainContent()
       initVSearchBlock()
     }
 
-    function initUserVBlock(){
-      let mount_point = drupalSettings.user.uid !== 0 ? '#block-userblock' : '#block-userlogin';
+    function initVUserBlock(){
+      let mount_point = drupalSettings.user.uid !== 0 ? 'block-userblock' : 'block-userlogin';
       let props = {
         title: "",
-        form:""
+        loginblock:""
       };
       switch (mount_point) {
-        case '#block-userlogin':
-          // let $form = document.getElementById('user-login-form');
-          // console.log('login form html', $form);
-          // props.form = $form.outerHTML
-          let $block = document.querySelector(mount_point);
-          props = {
-            title: $block.querySelector('h2').textContent,
-            form: {
-              ph_email: $block.querySelector('input#edit-name').getAttribute('placeholder'),
-              ph_pass: $block.querySelector('input#edit-pass').getAttribute('placeholder'),
-              btn_value: $block.querySelector('input#edit-submit').getAttribute('value'),
-              register: {
-                title: $block.querySelector('a.create-account-link').textContent,
-                href: $block.querySelector('a.create-account-link').getAttribute('href')
-              },
-              reset: {
-                title: $block.querySelector('a.request-password-link').textContent,
-                href: $block.querySelector('a.request-password-link').getAttribute('href')
-              }
-            }
-          }
+        case 'block-userlogin':
+          let $block = document.getElementById(mount_point);
+          console.log('initVUserBlock login form html', $block);
+          props.loginblock = $block.outerHTML.trim()
           break;
-        case '#block-userblock':
+        case 'block-userblock':
         default:
           break;
       }
-      // console.log(props);
 
       _v_user_block = new Vue({
         store,
@@ -78,14 +60,15 @@ import 'theme/assets/styles/main.scss'
         //   })
         // },
         created () {
+          // if already loggedin, call store.user to get the user infos
           if(drupalSettings.user.uid !== 0){
             this.$store.commit('User/setUid', drupalSettings.user.uid)
             this.$store.dispatch('User/getUser')
           }
         },
         render: h => h(VUserBlock, {props:props})
-      }).$mount(mount_point)
-      // console.log('initUserVBlock', _v_user_block);
+      }).$mount('#'+mount_point)
+      // console.log('initVUserBlock', _v_user_block);
     }
 
     function initVMainContent(){

+ 44 - 1
web/themes/custom/materiotheme/assets/styles/main.scss

@@ -52,7 +52,7 @@ header[role="banner"]{
     &>section{
       background-color: #fff;
       overflow: hidden;
-      width:14em;
+      width:11em;
       height:1px;
       padding:0.01em 1em;
       // margin:0 0 0 -1em;
@@ -63,6 +63,7 @@ header[role="banner"]{
       position: absolute;
       right:0;
       top:1.7em;
+      box-sizing: content-box;
     }
     &:hover{
       &>section{
@@ -72,6 +73,48 @@ header[role="banner"]{
         box-shadow: 0 0 10px #ccc;
       }
     }
+    .form-item{
+      margin:0;
+      position: relative;
+      width:100%;
+      &.form-item-name{
+        margin:2px 0 0.5em 0;
+        input{
+          width:90%;
+          padding:0;
+          box-sizing: content-box;
+        }
+      }
+      &.form-item-pass{
+        margin:0 0 0.5em 0;
+        input{
+          width:90%;
+          padding:0;
+          box-sizing: content-box;
+        }
+      }
+      &.form-item-persistent-login{
+        font-size: 0.756em;
+        label{
+          vertical-align: top;
+        }
+      }
+    }
+    #edit-actions{
+      margin:0;
+    }
+    .item-list{
+      ul{
+        margin:0;
+        li{
+          list-style: none;
+          margin:0;
+          a{
+            font-size: 0.756em;
+          }
+        }
+      }
+    }
   }
 
   #block-languageswitcher{

+ 2 - 2
web/themes/custom/materiotheme/vuejs/api/msapi-axios.js → web/themes/custom/materiotheme/vuejs/api/ma-axios.js

@@ -4,8 +4,8 @@ import axios from 'axios'
 
 // console.log('drupalSettings', drupalSettings);
 
-export const MSAPI = axios.create({
-  baseURL: window.location.origin + '/' + drupalSettings.path.pathPrefix + `materio_sapi`,
+export const MA = axios.create({
+  baseURL: window.location.origin + '/' + drupalSettings.path.pathPrefix,
   withCredentials: true,
   headers: {
     "Content-Type": "application/json"

+ 84 - 0
web/themes/custom/materiotheme/vuejs/components/Block/LoginBlock.vue

@@ -0,0 +1,84 @@
+<script>
+
+import Vue from 'vue'
+import { mapState, mapActions } from 'vuex'
+
+export default {
+  props: ['title', 'block'],
+  data () {
+    return {
+      template: null,
+      mail: '',
+      password: ''
+    }
+  },
+  computed: {
+    ...mapState(['User'])
+  },
+  methods: {
+    ...mapActions({
+      userLogin: 'User/userLogin'
+    }),
+    login () {
+      this.userLogin({
+        mail: this.mail,
+        pass: this.password
+      })
+    },
+    request_password () {
+      console.log('request_password');
+    },
+    create_account () {
+      console.log('create_account');
+    }
+  },
+  beforeMount() {
+    console.log('LoginBlock beforeMount', this._props.block);
+    if(this._props.block){
+      // console.log('LoginBlock beforeMount if this._props.block ok');
+      this.template = Vue.compile(this._props.block)
+      this.$options.staticRenderFns = [];
+      this._staticTrees = [];
+      this.template.staticRenderFns.map(fn => (this.$options.staticRenderFns.push(fn)));
+    }
+  },
+  mounted(){
+    // console.log('LoginBlock mounted');
+    Drupal.attachBehaviors(this.$el);
+  },
+  render(h) {
+    // console.log('LoginBlock render');
+    if(!this.template){
+      // console.log('LoginBlock render NAN');
+      return h('span', 'Loading ...')
+    }else{
+      // console.log('LoginBlock render template');
+      return this.template.render.call(this)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+  // section{
+  //   max-width:300px;
+  // }
+  // input{
+  //   display:block;
+  //   max-width:100%;
+  //   margin:0.5em 0 0 0;
+  // }
+  // button{
+  //   margin:0.5em 0 0 0;
+  // }
+  // ul{
+  //   margin:0; padding:0.5em 0 0 0;
+  // }
+  // li{
+  //   list-style:none;
+  //   margin:0.5em 0 0 0; padding:0;
+  //   font-size:0.882em;
+  // }
+  // a{
+  // }
+</style>

+ 6 - 6
web/themes/custom/materiotheme/vuejs/components/Block/SearchBlock.vue

@@ -9,7 +9,7 @@
 <script>
 import SearchForm from 'vuejs/components/Form/SearchForm'
 import { mapState, mapActions } from 'vuex'
-import { MSAPI } from 'vuejs/api/msapi-axios'
+import { MA } from 'vuejs/api/ma-axios'
 
 export default {
   props: ['blockid', 'formhtml'],
@@ -23,17 +23,17 @@ export default {
       canSearch: state => state.User.canSearch
     }),
     displayform(){
-      console.log('computed displayform');
+      // console.log('computed displayform');
       return this.canSearch && this.form
     }
   },
   beforeMount() {
-    console.log('SearchBlock beforeMount');
+    // console.log('SearchBlock beforeMount');
     this.form = this.formhtml
   },
   watch: {
     canSearch(new_value, old_value) {
-      console.log('canSearch changed, old: '+old_value+", new: "+new_value);
+      // console.log('canSearch changed, old: '+old_value+", new: "+new_value);
       if(new_value && !this.form){
         this.getSearchForm()
       }
@@ -44,9 +44,9 @@ export default {
   },
   methods: {
     getSearchForm(){
-      MSAPI.get(`/search_form`)
+      MA.get(`/materio_sapi/search_form`)
         .then(({data}) => {
-          console.log("getSearchForm");
+          // console.log("getSearchForm");
           this.form = data.rendered
         })
         .catch(( error ) => {

+ 32 - 4
web/themes/custom/materiotheme/vuejs/components/Block/UserBlock.vue

@@ -1,23 +1,51 @@
 <template lang="html">
   <UserTools v-if="isloggedin" />
-  <Login v-bind:title="title" v-bind:form="form" v-else />
+  <LoginBlock v-bind:title="title" v-bind:block="block" v-else/>
 </template>
 
 <script>
 import { mapState, mapActions } from 'vuex'
 
-import Login from 'vuejs/components/User/Login'
+import LoginBlock from 'vuejs/components/Block/LoginBlock'
 import UserTools from 'vuejs/components/User/UserTools'
 
+import { MA } from 'vuejs/api/ma-axios'
+
 export default {
-  props: ['title', 'form'],
+  props: ['title', 'loginblock'],
+  data(){
+    return {
+      block: null
+    }
+  },
   computed: {
     ...mapState({
       isloggedin: state => state.User.isloggedin
     })
   },
+  beforeMount() {
+    console.log('UserBlock beforeMount');
+    if(this.loginblock){
+      this.block = this.loginblock
+    }else{
+      this.getLoginBlock()
+    }
+
+  },
+  methods: {
+    getLoginBlock(){
+      MA.get(`/materio_user/login_block`)
+        .then(({data}) => {
+          // console.log("getLoginBlock data", data);
+          this.block = data.rendered
+        })
+        .catch(( error ) => {
+          console.warn('Issue with getLoginBlock', error)
+        })
+    }
+  },
   components: {
-    Login,
+    LoginBlock,
     UserTools
   }
 }

+ 0 - 0
web/themes/custom/materiotheme/vuejs/components/User/Login.vue → web/themes/custom/materiotheme/vuejs/components/Form/LoginForm.back.vue


+ 12 - 50
web/themes/custom/materiotheme/vuejs/components/Form/SearchForm.vue

@@ -1,16 +1,15 @@
 <script>
 
 import Vue from 'vue'
-import { MSAPI } from 'vuejs/api/msapi-axios'
 
 export default {
+  props: ['form'],
   data() {
     return {
       template: null,
       typed: ""
     }
   },
-  props: ['form'],
   methods: {
     keyup() {
       console.log("search typed", this.typed);
@@ -20,66 +19,29 @@ export default {
     }
   },
   beforeMount() {
-    console.log('SearchForm beforeMount');
-    if(this._props.form){
-      console.log('SearchForm beforeMount if this._props.form ok');
-      this.template = Vue.compile(this._props.form).render
-    }
-  },
-  beforeUpdate() {
-    console.log('SearchForm beforeUpdate');
+    // console.log('SearchForm beforeMount');
     if(this._props.form){
-      console.log('SearchForm beforeUpdate if this._props.form ok');
-      this.template = Vue.compile(this._props.form).render
+      // console.log('SearchForm beforeMount if this._props.form ok');
+      this.template = Vue.compile(this._props.form)
+      // https://github.com/vuejs/vue/issues/9911
+      this.$options.staticRenderFns = [];
+      this._staticTrees = [];
+      this.template.staticRenderFns.map(fn => (this.$options.staticRenderFns.push(fn)));
     }
   },
   mounted(){
-    console.log('SearchForm mounted');
-    // Drupal.attachBehaviors(this.$el);
-  },
-  updated(){
-    console.log('SearchForm updated');
-    // Drupal.attachBehaviors(this.$el);
+    // console.log('SearchForm mounted');
+    Drupal.attachBehaviors(this.$el);
   },
   render(h) {
-    console.log('searchForm render');
+    // console.log('searchForm render');
     if(!this.template){
       return h('span', 'Loading ...')
     }else{
-      return this.template()
+      return this.template.render.call(this)
     }
   }
 }
-// searchform: (resolve, reject) => (
-//   MSAPI.get(`/search_form`)
-//     .then(({data}) => {
-//       // console.log("materiosapisearchform", data);
-//       resolve({
-//         data() {
-//           return {
-//             typed:""
-//           }
-//         },
-//         methods: {
-//           keyup() {
-//             console.log("search typed", this.typed);
-//           },
-//           submit() {
-//             console.log("search clicked");
-//           }
-//         },
-//         mounted() {
-//           Drupal.attachBehaviors(this.$el);
-//         },
-//         template: data.rendered
-//       })
-//     })
-//     .catch(( error ) => {
-//         console.warn('Issue with get searchform', error)
-//         Promise.reject(error)
-//     })
-//
-// )
 </script>
 
 <style lang="scss" scoped>

Some files were not shown because too many files changed in this diff