Browse Source

Fixes #4298 - Show RSS feeds in knowledge base.

Mantas Masalskis 2 years ago
parent
commit
a5e00ba240

+ 5 - 0
LICENSE-ICONS-3RD-PARTY.json

@@ -639,6 +639,11 @@
         "url": "",
         "license": "MIT"
     },
+    "rss.svg": {
+        "author": "Zammad",
+        "url": "",
+        "license": "MIT"
+    },
     "saml-button.svg": {
         "author": "Zammad",
         "url": "",

+ 5 - 2
app/assets/javascripts/app/controllers/_application_controller/_base.coffee

@@ -108,10 +108,13 @@ class App.Controller extends Spine.Controller
     document.title = documentTitle
     App.Event.trigger('window-title-set', documentTitle)
 
-  copyToClipboardWithTooltip: (text, selector, container) ->
+  copyToClipboardWithTooltip: (text, selector, container, noPrefix) ->
     clipboard.writeText(text)
 
-    tooltipCopied = @el.find(selector + ' > .ticketNumberCopy-icon').tooltip(
+    if !noPrefix
+      selector += ' > .ticketNumberCopy-icon'
+
+    tooltipCopied = @el.find(selector).tooltip(
       trigger:   'manual'
       placement: 'bottom'
       container: container

+ 76 - 0
app/assets/javascripts/app/controllers/knowledge_base/feed_dialog.coffee

@@ -0,0 +1,76 @@
+class App.KnowledgeBaseFeedDialog extends App.ControllerModal
+  events:
+    'click .js-renew':    'clickedRenew'
+    'click .js-copy': 'clickedCopy'
+
+  head: __('Knowledge Base Feed')
+  leftButtons: [
+    {
+      text: __('Renew Access Token'),
+      className: 'js-renew'
+    }
+  ]
+  buttonSubmit: false
+
+  clickedCopy: (e) =>
+    e.preventDefault()
+
+    url = $(e.target).prev().attr('href')
+
+    @copyToClipboardWithTooltip(url, e.target,'.modal-body', true)
+
+  clickedRenew: (e) =>
+    e.preventDefault()
+
+    @token = null
+    @update()
+
+    @$('.form-control-visible-readonly').removeClass('form-control-visible-readonly')
+
+    @ajax(
+      id:          'knowledge_base_feed_token_renew'
+      type:        'patch'
+      url:         App.KnowledgeBase.generateURL('feed_tokens')
+      processData: true
+      success:     (data, status, xhr) =>
+        @token = data.token
+        @update()
+      error:       (xhr) =>
+        @showAlert(xhr.responseJSON?.error || __('Changes could not be loaded.'))
+    )
+
+  constructor: (params) ->
+    super
+
+    @load()
+
+  load: =>
+    @ajax(
+      id:          'knowledge_base_feed_token'
+      type:        'get'
+      url:         App.KnowledgeBase.generateURL('feed_tokens')
+      processData: true
+      success:     (data, status, xhr) =>
+        @token = data.token
+        @update()
+      error:       (xhr) =>
+        @showAlert(xhr.responseJSON?.error || __('Changes could not be loaded.'))
+    )
+
+  content: =>
+    return if !@token
+
+    category = switch @object?.constructor
+      when App.KnowledgeBaseAnswer
+        @object.category()
+      when App.KnowledgeBaseCategory
+        @object
+      else
+        null
+
+    App.view('knowledge_base/feed_dialog')(
+      kb_url: @kb.privateFeedUrl(@kb_locale, @token)
+      kb_title: @kb.guaranteedTitle(@kb_locale)
+      category_url: category?.privateFeedUrl(@kb_locale, @token)
+      category_title: category?.guaranteedTitle(@kb_locale)
+    )

+ 13 - 0
app/assets/javascripts/app/controllers/knowledge_base/navigation.coffee

@@ -3,6 +3,7 @@ class App.KnowledgeBaseNavigation extends App.Controller
 
   events:
     'click .js-search': 'clickedToggleSearch'
+    'click .js-feed':   'clickedFeed'
 
   elements:
     '.js-edit': 'editButton'
@@ -88,6 +89,7 @@ class App.KnowledgeBaseNavigation extends App.Controller
       kbLocales:   @kbLocaleOptions(object, kb_locale, action)
       search:      @searchOptions(object, kb_locale, action)
       edit:        @editOptions(object, kb_locale, action)
+      feed:        @feedOptions(object, kb_locale, action)
       externalUrl: @externalUrl(object, kb_locale, action)
       iconset:     @parentController.getKnowledgeBase().iconset
     )
@@ -120,6 +122,10 @@ class App.KnowledgeBaseNavigation extends App.Controller
       available: @parentController.isEditor()
     }
 
+  feedOptions: (object, kb_locale, action) ->
+    {
+      available: @parentController.getKnowledgeBase().show_feed_icon
+    }
 
   externalUrl: (object, kb_locale, action) ->
     if action and action != 'edit'
@@ -146,6 +152,13 @@ class App.KnowledgeBaseNavigation extends App.Controller
 
     @toggleSearchSource = location.hash
 
+  clickedFeed: ->
+    new App.KnowledgeBaseFeedDialog(
+      kb:        @parentController.getKnowledgeBase(),
+      kb_locale: @parentController.kb_locale()
+      object:    @savedParams
+    )
+
   breadcrumbTo: (object) ->
     if !object
       return []

+ 24 - 1
app/assets/javascripts/app/models/knowledge_base.coffee

@@ -1,5 +1,5 @@
 class App.KnowledgeBase extends App.Model
-  @configure 'KnowledgeBase', 'iconset', 'color_highlight', 'color_header', 'color_header_link', 'translation_ids', 'locale_ids', 'homepage_layout', 'category_layout', 'custom_address'
+  @configure 'KnowledgeBase', 'iconset', 'color_highlight', 'color_header', 'color_header_link', 'translation_ids', 'locale_ids', 'homepage_layout', 'category_layout', 'custom_address', 'show_feed_icon'
   @extend Spine.Model.Ajax
   @extend App.KnowledgeBaseActions
   @extend App.KnowledgeBaseAccess
@@ -24,6 +24,18 @@ class App.KnowledgeBase extends App.Model
 
     App.Utils.joinUrlComponents components
 
+  privateFeedUrl: (kb_locale, token) ->
+    components = [
+      App.Utils.baseUrl(),
+      App.Config.get('api_path'),
+      'knowledge_bases',
+      @id,
+      kb_locale.systemLocale().locale,
+      'feed'
+    ]
+
+    App.Utils.joinUrlComponents(components) + '?token=' + token
+
   uiUrl: (kb_locale, suffix = undefined) ->
     App.Utils.joinUrlComponents @uiUrlComponent(), kb_locale.urlSuffix(), suffix
 
@@ -195,6 +207,17 @@ class App.KnowledgeBase extends App.Model
           display:    false
           horizontal: true
           shown:      true
+    }, {
+      name: 'show_feed_icon'
+      display: __('Show Feed Icon')
+      tag: 'boolean'
+      style: 'block'
+      null: false
+      screen:
+        admin_style_feed:
+          display:    false
+          horizontal: true
+          shown:      true
     # Layout picker is disabled in V1
     #}, {
     #  name:     'homepage_layout'

+ 14 - 0
app/assets/javascripts/app/models/knowledge_base_category.coffee

@@ -101,6 +101,20 @@ class App.KnowledgeBaseCategory extends App.Model
     return null if @isNew()
     App.Utils.joinUrlComponents [@knowledge_base().publicBaseUrl(kb_locale), @id]
 
+  privateFeedUrl: (kb_locale, token) ->
+    components = [
+      App.Utils.baseUrl(),
+      App.Config.get('api_path'),
+      'knowledge_bases',
+      @knowledge_base_id,
+      'categories',
+      @id
+      kb_locale.systemLocale().locale,
+      'feed'
+    ]
+
+    App.Utils.joinUrlComponents(components) + '?token=' + token
+
   @translatableClass: -> App.KnowledgeBaseCategoryTranslation
   @translatableForeignKey: -> 'category_id'
   @extend App.KnowledgeBaseTranslatable

+ 17 - 0
app/assets/javascripts/app/views/knowledge_base/feed_dialog.jst.eco

@@ -0,0 +1,17 @@
+<p>
+  <%- @T('Internal Knowledge Base feed:') %>
+  <br>
+  <a href="<%= @kb_url %>" target="_blank"><%= @kb_title %></a>
+
+  <%- @Icon('clipboard', 'u-clickable js-copy') %>
+</p>
+
+<% if @category_url: %>
+  <p>
+    <%- @T('This category including sub-categories feed:') %>
+    <br>
+    <a href="<%= @category_url %>" target="_blank"><%= @category_title %></a>
+
+    <%- @Icon('clipboard', 'u-clickable js-copy') %>
+  </p>
+<% end %>

+ 7 - 0
app/assets/javascripts/app/views/knowledge_base/navigation.jst.eco

@@ -23,6 +23,13 @@
      >
     <%- @Icon('external') %>
   </a>
+  <% if @feed.available: %>
+  <div
+     class="btn btn--action btn--only-icon js-feed"
+     >
+    <%- @Icon('rss') %>
+  </div>
+  <% end %>
   <% if @edit.available: %>
     <a href="<%= @edit.url %>"
       class="btn btn--action js-edit <% if @edit.enabled: %>btn--active<% end %>"

+ 26 - 7
app/assets/javascripts/knowledge_base_public/dropdown.js

@@ -1,28 +1,47 @@
 (function() {
   document.addEventListener('DOMContentLoaded', function(event) {
     document
-      .querySelector('[data-toggle="dropdown"]')
-      .addEventListener('click', toggleDropdown)
+      .querySelectorAll('[data-toggle="dropdown"]')
+      .forEach(function(elem) {
+        elem.addEventListener('click', toggleDropdown)
+      })
 
     document
-      .querySelector('.dropdown-menu')
-      .addEventListener('click', function(event) { event.stopPropagation() })
+      .querySelectorAll('.dropdown-menu')
+      .forEach(function(elem) {
+        elem.addEventListener('click', function(event) { event.stopPropagation() })
+      })
   })
 
   function toggleDropdown(event){
     event.stopPropagation()
     event.preventDefault()
 
-    var elem = document.querySelector('.dropdown-menu')
+    var elem = event.target.closest('div').querySelector('.dropdown-menu')
     var open = elem.classList.toggle('is-open')
 
     if(elem.setAttribute) // not supported by IE11
       elem.setAttribute('aria-expanded', open ? 'true' : 'false')
 
     if(open) {
-      window.addEventListener('click', toggleDropdown)
+      window.addEventListener('click', globalCloseDropdown)
     } else {
-      window.removeEventListener('click', toggleDropdown)
+      window.removeEventListener('click', globalCloseDropdown)
     }
   }
+
+  function globalCloseDropdown(event){
+    event.stopPropagation()
+    event.preventDefault()
+
+    event
+      .target
+      .querySelectorAll('.dropdown-menu.is-open')
+      .forEach(function(elem) {
+        elem.classList.remove('is-open')
+        elem.setAttribute('aria-expanded', 'false')
+      })
+
+    window.removeEventListener('click', globalCloseDropdown)
+  }
 }())

+ 15 - 1
app/assets/stylesheets/knowledge_base.scss

@@ -1150,14 +1150,28 @@ b {
 
   .menu {
     justify-content: center;
+    flex-grow: 1;
   }
 }
 
-.language-picker {
+.dropdown-picker {
   display: flex;
   position: relative;
 }
 
+.feed-picker {
+  > a {
+    position: relative;
+    top: 2px;
+    margin-right: 1em;
+  }
+
+  svg {
+    height: 25px;
+    fill: currentColor;
+  }
+}
+
 .not-published-note {
   margin: 0.1em 0 0;
   font-size: 0.65em;

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