André Bauer 8 лет назад
Родитель
Сommit
7a8559e678

+ 1 - 5
.codeclimate.yml

@@ -14,14 +14,12 @@ engines:
       languages:
       - ruby
       - javascript
-      - python
-      - php
   eslint:
     enabled: true
   fixme:
     enabled: true
   phpmd:
-    enabled: true
+    enabled: false
   rubocop:
     enabled: true
 ratings:
@@ -38,8 +36,6 @@ ratings:
   - "**.js"
   - "**.jsx"
   - "**.module"
-  - "**.php"
-  - "**.py"
 exclude_paths:
 - config/
 - db/

+ 2 - 0
.csslintrc

@@ -0,0 +1,2 @@
+--exclude-exts=.min.css
+--ignore=adjoining-classes,box-model,ids,order-alphabetical,unqualified-attributes

+ 1 - 0
.eslintignore

@@ -0,0 +1 @@
+**/*{.,-}min.js

+ 213 - 0
.eslintrc

@@ -0,0 +1,213 @@
+ecmaFeatures:
+  modules: true
+  jsx: true
+
+env:
+  amd: true
+  browser: true
+  es6: true
+  jquery: true
+  node: true
+
+# http://eslint.org/docs/rules/
+rules:
+  # Possible Errors
+  comma-dangle: [2, never]
+  no-cond-assign: 2
+  no-console: 0
+  no-constant-condition: 2
+  no-control-regex: 2
+  no-debugger: 2
+  no-dupe-args: 2
+  no-dupe-keys: 2
+  no-duplicate-case: 2
+  no-empty: 2
+  no-empty-character-class: 2
+  no-ex-assign: 2
+  no-extra-boolean-cast: 2
+  no-extra-parens: 0
+  no-extra-semi: 2
+  no-func-assign: 2
+  no-inner-declarations: [2, functions]
+  no-invalid-regexp: 2
+  no-irregular-whitespace: 2
+  no-negated-in-lhs: 2
+  no-obj-calls: 2
+  no-regex-spaces: 2
+  no-sparse-arrays: 2
+  no-unexpected-multiline: 2
+  no-unreachable: 2
+  use-isnan: 2
+  valid-jsdoc: 0
+  valid-typeof: 2
+
+  # Best Practices
+  accessor-pairs: 2
+  block-scoped-var: 0
+  complexity: [2, 6]
+  consistent-return: 0
+  curly: 0
+  default-case: 0
+  dot-location: 0
+  dot-notation: 0
+  eqeqeq: 2
+  guard-for-in: 2
+  no-alert: 2
+  no-caller: 2
+  no-case-declarations: 2
+  no-div-regex: 2
+  no-else-return: 0
+  no-empty-label: 2
+  no-empty-pattern: 2
+  no-eq-null: 2
+  no-eval: 2
+  no-extend-native: 2
+  no-extra-bind: 2
+  no-fallthrough: 2
+  no-floating-decimal: 0
+  no-implicit-coercion: 0
+  no-implied-eval: 2
+  no-invalid-this: 0
+  no-iterator: 2
+  no-labels: 0
+  no-lone-blocks: 2
+  no-loop-func: 2
+  no-magic-number: 0
+  no-multi-spaces: 0
+  no-multi-str: 0
+  no-native-reassign: 2
+  no-new-func: 2
+  no-new-wrappers: 2
+  no-new: 2
+  no-octal-escape: 2
+  no-octal: 2
+  no-proto: 2
+  no-redeclare: 2
+  no-return-assign: 2
+  no-script-url: 2
+  no-self-compare: 2
+  no-sequences: 0
+  no-throw-literal: 0
+  no-unused-expressions: 2
+  no-useless-call: 2
+  no-useless-concat: 2
+  no-void: 2
+  no-warning-comments: 0
+  no-with: 2
+  radix: 2
+  vars-on-top: 0
+  wrap-iife: 2
+  yoda: 0
+
+  # Strict
+  strict: 0
+
+  # Variables
+  init-declarations: 0
+  no-catch-shadow: 2
+  no-delete-var: 2
+  no-label-var: 2
+  no-shadow-restricted-names: 2
+  no-shadow: 0
+  no-undef-init: 2
+  no-undef: 0
+  no-undefined: 0
+  no-unused-vars: 0
+  no-use-before-define: 0
+
+  # Node.js and CommonJS
+  callback-return: 2
+  global-require: 2
+  handle-callback-err: 2
+  no-mixed-requires: 0
+  no-new-require: 0
+  no-path-concat: 2
+  no-process-exit: 2
+  no-restricted-modules: 0
+  no-sync: 0
+
+  # Stylistic Issues
+  array-bracket-spacing: 0
+  block-spacing: 0
+  brace-style: 0
+  camelcase: 0
+  comma-spacing: 0
+  comma-style: 0
+  computed-property-spacing: 0
+  consistent-this: 0
+  eol-last: 0
+  func-names: 0
+  func-style: 0
+  id-length: 0
+  id-match: 0
+  indent: 0
+  jsx-quotes: 0
+  key-spacing: 0
+  linebreak-style: 0
+  lines-around-comment: 0
+  max-depth: 0
+  max-len: 0
+  max-nested-callbacks: 0
+  max-params: 0
+  max-statements: [2, 30]
+  new-cap: 0
+  new-parens: 0
+  newline-after-var: 0
+  no-array-constructor: 0
+  no-bitwise: 0
+  no-continue: 0
+  no-inline-comments: 0
+  no-lonely-if: 0
+  no-mixed-spaces-and-tabs: 0
+  no-multiple-empty-lines: 0
+  no-negated-condition: 0
+  no-nested-ternary: 0
+  no-new-object: 0
+  no-plusplus: 0
+  no-restricted-syntax: 0
+  no-spaced-func: 0
+  no-ternary: 0
+  no-trailing-spaces: 0
+  no-underscore-dangle: 0
+  no-unneeded-ternary: 0
+  object-curly-spacing: 0
+  one-var: 0
+  operator-assignment: 0
+  operator-linebreak: 0
+  padded-blocks: 0
+  quote-props: 0
+  quotes: 0
+  require-jsdoc: 0
+  semi-spacing: 0
+  semi: 0
+  sort-vars: 0
+  space-after-keywords: 0
+  space-before-blocks: 0
+  space-before-function-paren: 0
+  space-before-keywords: 0
+  space-in-parens: 0
+  space-infix-ops: 0
+  space-return-throw-case: 0
+  space-unary-ops: 0
+  spaced-comment: 0
+  wrap-regex: 0
+
+  # ECMAScript 6
+  arrow-body-style: 0
+  arrow-parens: 0
+  arrow-spacing: 0
+  constructor-super: 0
+  generator-star-spacing: 0
+  no-arrow-condition: 0
+  no-class-assign: 0
+  no-const-assign: 0
+  no-dupe-class-members: 0
+  no-this-before-super: 0
+  no-var: 0
+  object-shorthand: 0
+  prefer-arrow-callback: 0
+  prefer-const: 0
+  prefer-reflect: 0
+  prefer-spread: 0
+  prefer-template: 0
+  require-yield: 0

+ 2 - 2
.github_changelog_generator

@@ -5,6 +5,6 @@ since-tag=1.0.0
 bug-labels=bug,minor bug,security
 enhancement-labels=enhancement,feature
 issues-wo-labels=false
-include-labels=security,trigger,UX/UI,admin area,API,blocker,browser,bug,channel,chat,duplicate,enhancement,feature,import,minor bug,notification,email filter,ticket templates
-issue-line-labels=enhancement,feature,bug,minor bug,security
+include-labels=security,trigger,UX/UI,admin area,API,browser,bug,channel,chat,email filter,enhancement,feature,import,minor bug,notification,email filter,ticket templates,
+issue-line-labels=enhancement,feature,bug,minor bug,notification,reporting,security,ticket templates,translation,trigger,UX/UI
 exclude-tags=9.0.1,0.9.1,v0.1.8,v0.1.7,unittests_passed

+ 29 - 4
.travis.yml

@@ -1,14 +1,20 @@
 dist: trusty
 sudo: required
 notifications:
-  email: false
+  email:
+    - me+tv@zammad.com
+env:
+  - DB=mysql
+  - DB=postgresql
 addons:
+  postgresql: "9.4"
   apt:
     packages:
     - mysql-server-5.6
     - mysql-client-core-5.6
     - mysql-client-5.6
 services:
+  - postgresql
   - mysql
 language: ruby
 rvm:
@@ -18,12 +24,17 @@ before_install:
   - sudo apt-get install -y curl git-core patch build-essential bison zlib1g-dev libssl-dev libxml2-dev libxml2-dev sqlite3 libsqlite3-dev autotools-dev libxslt1-dev libyaml-0-2 autoconf automake libreadline6-dev libyaml-dev libtool libgmp-dev libgdbm-dev libncurses5-dev pkg-config libffi-dev libmysqlclient-dev postfix
   - mysql -u root -e "CREATE USER 'some_user'@'localhost' IDENTIFIED BY 'some_pass';"
   - mysql -u root -e "GRANT ALL PRIVILEGES ON *.* TO 'some_user'@'localhost';"
+  - if [ "${DB}" = "mysql" ]; then mysql -u root -e "CREATE USER 'some_user'@'localhost' IDENTIFIED BY 'some_pass';"; fi
+  - if [ "${DB}" = "mysql" ]; then mysql -u root -e "GRANT ALL PRIVILEGES ON *.* TO 'some_user'@'localhost';"; fi
+  - if [ "${DB}" = "mysql" ]; then cp config/database.yml.test-mysql config/database.yml; fi
+  - if [ "${DB}" = "postgresql" ]; then psql -c 'create database zammad_test;' -U postgres; fi
+  - if [ "${DB}" = "postgresql" ]; then cp config/database.yml.test-postgresql config/database.yml; fi
   - export RAILS_ENV=test
-  - export EMAILHELPER_MAILBOX_1='unittestemailhelper01@znuny.com:somepass'
-  - cp config/database.yml.test config/database.yml
 install:
-- bundle install --without postgres
+  - if [ "${DB}" = "mysql" ]; then bundle install --without postgres; fi
+  - if [ "${DB}" = "postgresql" ]; then bundle install; fi
 script:
+- bundle exec rubocop
 - rake db:create
 - rake db:migrate
 - rake db:seed
@@ -31,3 +42,17 @@ script:
 - rake test:controllers
 - rake assets:precompile
 - rake db:drop
+- rake db:create
+- rake db:migrate
+- rake db:seed
+- ruby -I test/ test/integration/auto_wizard_test.rb
+- rake db:drop
+- rake db:create
+- rake db:migrate
+- rake db:seed
+- ruby -I test/ test/integration/geo_location_test.rb
+- ruby -I test/ test/integration/geo_calendar_test.rb
+- ruby -I test/ test/integration/user_agent_test.rb
+- ruby -I test/ test/integration/user_device_controller_test.rb
+- ruby -I test/ test/integration/sipgate_controller_test.rb
+- rake db:drop

+ 5 - 1
README.md

@@ -9,17 +9,20 @@ with a team of agents?
 
 You're going to love Zammad!
 
+
 ## Statusbadges
 
 - Build: [![Build Status](https://travis-ci.org/zammad/zammad.svg?branch=develop)](https://travis-ci.org/zammad/zammad)
 - Code: [![Code Climate](https://codeclimate.com/github/zammad/zammad/badges/gpa.svg)](https://codeclimate.com/github/zammad/zammad)
 - Docs: [![Documentation Status](https://readthedocs.org/projects/zammad/badge/?version=latest)](https://docs.zammad.org)
-- Docker Image: [![](https://images.microbadger.com/badges/image/zammad/zammad.svg)](https://microbadger.com/images/zammad/zammad) [![](https://images.microbadger.com/badges/version/zammad/zammad.svg)](https://microbadger.com/images/zammad/zammad)
+- Docker Image: [![](https://images.microbadger.com/badges/image/zammad/zammad.svg)](https://microbadger.com/images/zammad/zammad) [![](https://images.microbadger.com/badges/version/zammad/zammad.svg)](https://hub.docker.com/r/zammad/zammad/)
+
 
 ## Installing & Getting Started
 
 https://docs.zammad.org
 
+
 ## Screenshots
 
 https://zammad.org/screenshots
@@ -38,3 +41,4 @@ https://zammad.org/participate
 Thanks! ❤️ ❤️ ❤️
 
  Your Zammad Team
+

+ 12 - 9
app/assets/javascripts/app/controllers/navigation.coffee

@@ -25,6 +25,10 @@ class App.Navigation extends App.ControllerWidgetPermanent
 
     @throttledSearch = _.throttle @search, 200
 
+    @globalSearch = new App.GlobalSearch(
+      render: @renderResult
+    )
+
     # rerender view, e. g. on langauge change
     @bind 'ui:rerender', =>
       @renderMenu()
@@ -127,15 +131,15 @@ class App.Navigation extends App.ControllerWidgetPermanent
     items = @getItems(navbar: @Config.get('NavBarRight'))
 
     # get open tabs to repopen on rerender
-    open_tab = {}
+    openTab = {}
     @$('.open').children('a').each( (i,d) ->
       href = $(d).attr('href')
-      open_tab[href] = true
+      openTab[href] = true
     )
 
     @$('.navbar-items-personal').html App.view('navigation/personal')(
-      items:    items
-      open_tab: open_tab
+      items:   items
+      openTab: openTab
     )
 
     # only start avatar widget on existing session
@@ -151,6 +155,7 @@ class App.Navigation extends App.ControllerWidgetPermanent
     # remove result if not result exists
     if _.isEmpty(result)
       @searchContainer.removeClass('open')
+      @globalSearch.close()
       @searchResult.html('')
       return
 
@@ -275,6 +280,7 @@ class App.Navigation extends App.ControllerWidgetPermanent
   emptyAndClose: =>
     @searchInput.val('')
     @searchContainer.removeClass('filled').removeClass('open').removeClass('focused')
+    @globalSearch.close()
 
     # remove not needed popovers
     @delay(@anyPopoversDestroy, 100, 'removePopovers')
@@ -282,6 +288,7 @@ class App.Navigation extends App.ControllerWidgetPermanent
   andClose: =>
     @searchInput.blur()
     @searchContainer.removeClass('open')
+    @globalSearch.close()
     @delay(@anyPopoversDestroy, 100, 'removePopovers')
 
   search: =>
@@ -290,11 +297,7 @@ class App.Navigation extends App.ControllerWidgetPermanent
     return if query is @query
     @query = query
     @searchContainer.toggleClass('filled', !!@query)
-
-    App.GlobalSearch.execute(
-      query: @query
-      render: @renderResult
-    )
+    @globalSearch.search(query: @query)
 
   getItems: (data) ->
     navbar =  _.values(data.navbar)

+ 8 - 7
app/assets/javascripts/app/controllers/search.coffee

@@ -1,5 +1,4 @@
 class App.Search extends App.Controller
-  searchResultCache: {}
   elements:
     '.js-search': 'searchInput'
 
@@ -25,6 +24,11 @@ class App.Search extends App.Controller
 
     @throttledSearch = _.throttle @search, 200
 
+    @globalSearch = new App.GlobalSearch(
+      render: @renderResult
+      limit: 50
+    )
+
     @render()
 
     # rerender view, e. g. on langauge change
@@ -52,10 +56,11 @@ class App.Search extends App.Controller
     @navupdate(url: '#search', type: 'menu')
     return if _.isEmpty(params.query)
     @$('.js-search').val(params.query).trigger('change')
+    return if @shown
     @throttledSearch(true)
 
   hide: ->
-    # nothing
+    @shown = false
 
   changed: ->
     # nothing
@@ -117,11 +122,7 @@ class App.Search extends App.Controller
     @query = query
     @updateTask()
 
-    App.GlobalSearch.execute(
-      query: @query
-      render: @renderResult
-      limit: 50
-    )
+    @globalSearch.search(query: @query)
 
   renderResult: (result = []) =>
     @result = result

+ 21 - 28
app/assets/javascripts/app/lib/app_post/global_search.coffee

@@ -1,37 +1,27 @@
-class App.GlobalSearch
-  _instance = undefined
-
-  @execute: (args) ->
-    if _instance == undefined
-      _instance ?= new _globalSearchSingleton
-    _instance.execute(args)
-
-class _globalSearchSingleton extends Spine.Module
-
+class App.GlobalSearch extends App.Controller
   constructor: ->
-    @searchResultCache = undefined
-    @searchResultCacheByKey = {}
+    super
+    @searchResultCache = {}
+    @lastQuery = undefined
     @apiPath = App.Config.get('api_path')
+    @ajaxId = "search-#{Math.floor( Math.random() * 999999 )}"
 
-  execute: (params) ->
-    query    = params.query
-    render   = params.render
-    limit    = params.limit || 10
-    cacheKey = "#{query}_#{limit}"
+  search: (params) =>
+    query = params.query
 
     # use cache for search result
     currentTime = new Date
-    if @searchResultCacheByKey[cacheKey] && @searchResultCacheByKey[cacheKey].time > currentTime.setSeconds(currentTime.getSeconds() - 20)
-      @renderTry(render, @searchResultCacheByKey[cacheKey].result, cacheKey)
+    if @searchResultCache[query] && @searchResultCache[query].time > currentTime.setSeconds(currentTime.getSeconds() - 20)
+      @renderTry(@searchResultCache[query].result, query)
       return
 
     App.Ajax.request(
-      id:    'search'
+      id:    @ajaxId
       type:  'GET'
       url:   "#{@apiPath}/search"
       data:
         query: query
-        limit: limit
+        limit: @limit || 10
       processData: true
       success: (data, status, xhr) =>
         App.Collection.loadAssets(data.assets)
@@ -49,21 +39,24 @@ class _globalSearchSingleton extends Spine.Module
           else
             App.Log.error('_globalSearchSingleton', "No such model App.#{item.type}")
 
-        @renderTry(render, result, cacheKey)
+        @renderTry(result, query)
     )
 
-  renderTry: (render, result, cacheKey) =>
+  renderTry: (result, query) =>
 
     # if result hasn't changed, do not rerender
     diff = false
-    if @searchResultCache
-      diff = difference(@searchResultCache, result)
+    if @lastQuery is query && @searchResultCache[query]
+      diff = difference(@searchResultCache[query].result, result)
     return if diff isnt false && _.isEmpty(diff)
+    @lastQuery = query
 
     # cache search result
-    @searchResultCache = result
-    @searchResultCacheByKey[cacheKey] =
+    @searchResultCache[query] =
       result: result
       time: new Date
 
-    render(result)
+    @render(result)
+
+  close: =>
+    @lastQuery = undefined

Некоторые файлы не были показаны из-за большого количества измененных файлов