ditty 0.10.2 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0ef6903491ad20b85916f196652705811820c13f3ed14552fc9b35687dbe7309
4
- data.tar.gz: 1431f00aee5e7093757936e5b12231d091d41bcfb798dda53e534d289f43c5dc
3
+ metadata.gz: 0ecdd202f458e55d205e3e77b295a3c8908f7d3ecaf3d894fa9ae09c62d8b4fd
4
+ data.tar.gz: bcc5967235a668d896cee25228130b1ab2d98761dc2fb2125d5f7f2d2912881b
5
5
  SHA512:
6
- metadata.gz: b8383f78d50a4b1a2379b34f5ea81969fc019130dd953e7218d499a7596217ea12eef13d804de7b888da7145fc865bb3e798453f664597951f953df5a06eec00
7
- data.tar.gz: c36d004df0b9ce15aba2fbf99f376fef6fa5f70a2cc4338163cd360c730f2398ab4a5b3b0c29362ba20a90502397475aa4b4a1769b3acb9f50965eae371b680a
6
+ metadata.gz: 45c0c4fb7acb614ce8cbfda366908fcaee02482d9601c26fe797ff5012a7bbeff0e81897141a33ce2c3d31064529401ea0c35f194852969262b263a306760b3c
7
+ data.tar.gz: c471989b26228ad7a08f933e8c1c747a8f5fd9ec21fd0fc1515a496c63d98446a2a7505802d01c4110bbcb514d6876e85b0c611dc4c438750aaff4e507948f64
data/.travis.yml CHANGED
@@ -1,9 +1,10 @@
1
1
  sudo: false
2
2
  language: ruby
3
3
  rvm:
4
+ - 3.2
5
+ - 3.1
4
6
  - 3.0
5
7
  - 2.7
6
- - 2.6
7
8
  gemfile: Gemfile.ci
8
9
  env:
9
10
  matrix:
@@ -17,7 +18,7 @@ before_script:
17
18
  - bundle exec rake ditty:prep
18
19
  script:
19
20
  - bundle exec rake
20
- - bundle exec rubocop --fail-level W lib views spec
21
+ - bundle exec rubocop -c .rubocop.yml --fail-level W lib views spec
21
22
  after_script:
22
23
  - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
23
24
  after_success:
data/Readme.md CHANGED
@@ -1,4 +1,4 @@
1
- [![Build Status](https://travis-ci.org/EagerELK/ditty.svg?branch=master)](https://travis-ci.org/EagerELK/ditty)
1
+ [![Build Status](https://app.travis-ci.com/EagerELK/ditty.svg?branch=master)](https://app.travis-ci.com/github/EagerELK/ditty)
2
2
  [![Code Climate](https://codeclimate.com/github/EagerELK/ditty/badges/gpa.svg)](https://codeclimate.com/github/EagerELK/ditty)
3
3
  [![Test Coverage](https://codeclimate.com/github/EagerELK/ditty/badges/coverage.svg)](https://codeclimate.com/github/EagerELK/ditty/coverage)
4
4
  [![Inline docs](http://inch-ci.org/github/EagerELK/ditty.svg?branch=master)](http://inch-ci.org/github/EagerELK/ditty)
data/ditty.gemspec CHANGED
@@ -41,13 +41,13 @@ Gem::Specification.new do |spec|
41
41
  spec.add_dependency 'bcrypt', '>= 3.1'
42
42
  spec.add_dependency 'browser', '>= 5.3'
43
43
  spec.add_dependency 'dotenv', '>= 2'
44
- spec.add_dependency 'haml', '>= 5.1.2'
44
+ spec.add_dependency 'haml', '~> 5.1', '>= 5.1.2' # Going to 6 creates a `undefined method capture_html` and breaks a lot of other stuff
45
45
  spec.add_dependency 'logger', '>= 1.0'
46
46
  spec.add_dependency 'mail', '>= 1.7'
47
47
  spec.add_dependency 'oga', '>= 2.14'
48
- spec.add_dependency 'omniauth', '~> 1.0'
49
- spec.add_dependency 'omniauth-identity', '~> 1.0'
50
- spec.add_dependency 'pundit', '~> 1.0'
48
+ spec.add_dependency 'omniauth', '>= 1.0'
49
+ spec.add_dependency 'omniauth-identity', '>= 1.0'
50
+ spec.add_dependency 'pundit', '>= 2.0'
51
51
  spec.add_dependency 'rack-contrib', '>= 2.0'
52
52
  spec.add_dependency 'rack_csrf', '>= 2.0'
53
53
  spec.add_dependency 'rake', '>= 13.0'
@@ -59,5 +59,5 @@ Gem::Specification.new do |spec|
59
59
  spec.add_dependency 'thor', '>= 0.20'
60
60
  spec.add_dependency 'tilt', '>= 2'
61
61
  spec.add_dependency 'will_paginate', '>= 3.1'
62
- spec.add_dependency 'wisper', '~> 2.0'
62
+ spec.add_dependency 'wisper', '>= 2.0'
63
63
  end
data/lib/ditty/cli.rb CHANGED
@@ -52,7 +52,7 @@ module Ditty
52
52
  environment: ENV['APP_ENV'] || 'development',
53
53
  Port: ENV['APP_PORT'] || 9292,
54
54
  Host: ENV['APP_HOST'] || '0.0.0.0',
55
- config: "config.ru"
55
+ config: 'config.ru'
56
56
  }
57
57
  puts 'Starting the Ditty Server'
58
58
  Rack::Server.start(rack_opts)
@@ -66,8 +66,9 @@ module Ditty
66
66
  load
67
67
 
68
68
  sa = ::Ditty::Role.find_or_create(name: 'super_admin')
69
- admin = ::Ditty::Role.find_or_create(name: 'admin') { |e| e.parent = sa }
70
- ::Ditty::Role.find_or_create(name: 'user') { |e| e.parent = admin }
69
+ admin = ::Ditty::Role.find_or_create(name: 'admin') { |rec| rec.parent = sa }
70
+ ::Ditty::Role.find_or_create(name: 'user') { |rec| rec.parent = admin }
71
+ ::Ditty::Role.find_or_create(name: 'anonymous')
71
72
  end
72
73
  end
73
74
 
@@ -39,9 +39,7 @@ module Ditty
39
39
  after do
40
40
  return if settings.environment == 'production'
41
41
 
42
- if (response.successful? || response.redirection?) && @skip_verify == false && (settings.environment != 'production')
43
- verify_authorized
44
- end
42
+ verify_authorized if (response.successful? || response.redirection?) && @skip_verify == false
45
43
  end
46
44
 
47
45
  after '/' do
@@ -67,7 +65,7 @@ module Ditty
67
65
 
68
66
  entity = settings.model_class.new(permitted_parameters(settings.model_class, :create))
69
67
  haml :"#{view_location}/new",
70
- locals: { entity: entity, title: heading(:new) },
68
+ locals: { entity: entity, title: heading(:new), actions: actions(action: :new) },
71
69
  layout: layout
72
70
  end
73
71
 
@@ -100,7 +98,7 @@ module Ditty
100
98
 
101
99
  flash[:redirect_to] = "#{base_path}/#{entity.display_id}" unless flash.keep(:redirect_to)
102
100
  haml :"#{view_location}/edit",
103
- locals: { entity: entity, title: heading(:edit) },
101
+ locals: { entity: entity, title: heading(:edit), actions: actions(entity: entity, action: :edit) },
104
102
  layout: layout
105
103
  end
106
104
 
@@ -15,10 +15,10 @@ module Ditty
15
15
  set track_actions: true
16
16
 
17
17
  # New
18
- get '/new' do
18
+ get '/new/?' do
19
19
  authorize settings.model_class, :create
20
20
 
21
- locals = { title: heading(:new), entity: User.new, identity: Identity.new }
21
+ locals = { title: heading(:new), entity: User.new, identity: Identity.new, actions: actions(action: :new) }
22
22
  haml :"#{view_location}/new", locals: locals
23
23
  end
24
24
 
@@ -60,7 +60,7 @@ module Ditty
60
60
  end
61
61
 
62
62
  # Update
63
- put '/:id' do |id|
63
+ put '/:id/?' do |id|
64
64
  entity = dataset.first(settings.model_class.primary_key => id)
65
65
  halt 404 unless entity
66
66
  authorize entity, :update
@@ -80,7 +80,7 @@ module Ditty
80
80
  update_response(entity)
81
81
  end
82
82
 
83
- put '/:id/identity' do |id|
83
+ put '/:id/identity/?' do |id|
84
84
  entity = dataset.first(settings.model_class.primary_key => id)
85
85
  halt 404 unless entity
86
86
  authorize entity, :update
@@ -88,13 +88,13 @@ module Ditty
88
88
  identity = entity.identity.first
89
89
  identity_params = params['identity']
90
90
 
91
- unless current_user.super_admin? || identity.authenticate(identity_params['old_password'])
91
+ if (current_user.super_admin? == false || current_user_id == entity.id) && identity.authenticate(identity_params['old_password']) == false
92
92
  broadcast(:identity_update_password_failed, target: self)
93
93
  flash[:danger] = 'Old Password didn\'t match'
94
- return redirect back
94
+ return redirect(with_layout(params[:redirect_to] || flash[:redirect_to] || back))
95
95
  end
96
96
 
97
- values = permitted_parameters(Identity, :create)
97
+ values = permitted_parameters(Identity, :update)
98
98
  identity.set values
99
99
  if identity.valid?
100
100
  identity.save_changes
@@ -111,7 +111,7 @@ module Ditty
111
111
  end
112
112
 
113
113
  # Delete
114
- delete '/:id', provides: %i[html json] do |id|
114
+ delete '/:id/?', provides: %i[html json] do |id|
115
115
  entity = dataset.first(settings.model_class.primary_key => id)
116
116
  halt 404 unless entity
117
117
  authorize entity, :delete
@@ -125,13 +125,13 @@ module Ditty
125
125
  end
126
126
 
127
127
  # Profile
128
- get '/profile' do
128
+ get '/profile/?' do
129
129
  entity = current_user
130
130
  halt 404 unless entity
131
131
  authorize entity, :read
132
132
 
133
133
  flash[:redirect_to] = request.path
134
- haml :"#{view_location}/profile", locals: { entity: entity, identity: entity.identity.first, title: 'My Account' }
134
+ haml :"#{view_location}/profile", locals: { entity: entity, identity: entity.identity.first, title: 'My Account', actions: actions(entity: entity, action: :read) }
135
135
  end
136
136
  end
137
137
  end
data/lib/ditty/db.rb CHANGED
@@ -8,7 +8,7 @@ require 'active_support/core_ext/object/blank'
8
8
  pool_timeout = (ENV['DB_POOL_TIMEOUT'] || 5).to_i
9
9
 
10
10
  if defined? DB
11
- ::Ditty::Services::Logger.warn 'Database connection already set up'
11
+ ::Ditty::Services::Logger.warn '** Database connection already set up **'
12
12
  elsif ENV['DATABASE_URL'].blank? == false
13
13
  # Delete DATABASE_URL from the environment, so it isn't accidently
14
14
  # passed to subprocesses. DATABASE_URL may contain passwords.
@@ -29,5 +29,5 @@ elsif ENV['DATABASE_URL'].blank? == false
29
29
  Sequel::Model.plugin :update_or_create
30
30
  Sequel::Model.plugin :validation_helpers
31
31
  else
32
- ::Ditty::Services::Logger.error 'No database connection set up'
32
+ ::Ditty::Services::Logger.error '*** NO DATABASE CONNECTION SET UP ***'
33
33
  end
@@ -41,9 +41,9 @@ module Ditty
41
41
  copy_file 'sidekiq.rb', './config/sidekiq.rb'
42
42
  copy_file 'sidekiq.yml', './config/sidekiq.yml'
43
43
 
44
- copy_file 'spec_helper.rb', './specs/spec_helper.rb'
45
- copy_file '../../../spec/support/api_shared_examples.rb', './specs/support/api_shared_examples.rb'
46
- copy_file '../../../spec/support/crud_shared_examples.rb', './specs/support/crud_shared_examples.rb'
44
+ copy_file 'spec_helper.rb', './spec/spec_helper.rb'
45
+ copy_file '../../../spec/support/api_shared_examples.rb', './spec/support/api_shared_examples.rb'
46
+ copy_file '../../../spec/support/crud_shared_examples.rb', './spec/support/crud_shared_examples.rb'
47
47
 
48
48
  template 'settings.yml.erb', './config/settings.yml'
49
49
  end
@@ -9,6 +9,10 @@ module Ditty
9
9
  return nil if current_user_id.nil?
10
10
 
11
11
  @current_user ||= User[current_user_id]
12
+ rescue Sequel::DatabaseError => e
13
+ Services::Logger.warn "Could not fetch current user: #{e.message} / #{current_user_id}"
14
+ Services::Logger.debug e
15
+ nil
12
16
  end
13
17
 
14
18
  def current_user=(user)
@@ -5,7 +5,7 @@ require 'pundit'
5
5
  module Ditty
6
6
  module Helpers
7
7
  module Pundit
8
- include ::Pundit
8
+ include ::Pundit::Authorization
9
9
 
10
10
  def authorize(record, query)
11
11
  query = :"#{query}?" unless query[-1] == '?'
@@ -24,7 +24,7 @@ module Ditty
24
24
  end
25
25
 
26
26
  def permitted_parameters(record, action = nil)
27
- param_key = PolicyFinder.new(record).param_key
27
+ param_key = ::Pundit::PolicyFinder.new(record).param_key
28
28
  policy_fields = permitted_attributes(record, action)
29
29
  request.params.fetch(param_key, {}).select do |key, _value|
30
30
  policy_fields.include? key.to_sym
@@ -6,10 +6,9 @@ module Ditty
6
6
  module Helpers
7
7
  module Response
8
8
  def list_response(result, view: 'index')
9
+ actions = actions(action: :list)
9
10
  respond_to do |format|
10
11
  format.html do
11
- actions = {}
12
- actions["#{base_path}/new"] = "New #{heading}" if policy(settings.model_class).create?
13
12
  haml :"#{view_location}/#{view}",
14
13
  locals: { list: result, title: heading(:list), actions: actions },
15
14
  layout: layout
@@ -49,15 +48,16 @@ module Ditty
49
48
  end
50
49
  end
51
50
 
52
- def actions(entity = nil)
51
+ def actions(entity: nil, action: nil)
53
52
  actions = {}
54
- actions["#{base_path}/#{entity.display_id}/edit"] = "Edit #{heading}" if entity && policy(entity).update?
55
- actions["#{base_path}/new"] = "New #{heading}" if policy(settings.model_class).create?
53
+ actions["#{base_path}/#{entity.display_id}/edit"] = "Edit #{heading}" if entity && policy(entity).update? && action != :edit
54
+ actions[base_path] = "List #{heading(:list)}" if policy(entity || settings.model_class).list? && action != :list
55
+ actions["#{base_path}/new"] = "New #{heading}" if policy(entity || settings.model_class).create? && action != :new
56
56
  actions
57
57
  end
58
58
 
59
59
  def read_response(entity)
60
- actions = actions(entity)
60
+ actions = actions(entity: entity, action: :read)
61
61
  respond_to do |format|
62
62
  format.html do
63
63
  title = heading(:read) + (entity.respond_to?(:name) ? ": #{entity.name}" : '')
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'ditty/services/pagination_wrapper'
4
+ require 'digest/sha2'
4
5
 
5
6
  module Ditty
6
7
  module Helpers
@@ -156,6 +157,14 @@ module Ditty
156
157
 
157
158
  'fa-sort-down'
158
159
  end
160
+
161
+ def static_headers(view)
162
+ view_path = "views/#{view}.haml"
163
+ return unless File.exist?(view_path)
164
+
165
+ last_modified File.mtime(view_path)
166
+ etag Digest::SHA2.hexdigest(File.read(view_path))
167
+ end
159
168
  end
160
169
  end
161
170
  end
@@ -70,11 +70,14 @@ module Ditty
70
70
  end
71
71
 
72
72
  def log_action(values)
73
- values[:user] ||= values[:target].current_user if values[:target]
73
+ require 'ditty/models/audit_log'
74
+
75
+ values[:user] ||= values[:target].current_user if values[:target] && values[:target].respond_to?(:current_user)
74
76
  @mutex.synchronize { ::Ditty::AuditLog.create values }
75
77
  end
76
78
 
77
79
  def user_traits(target)
80
+ return {} unless target.respond_to?(:current_user) && target.respond_to?(:browser)
78
81
  {
79
82
  user_id: target.current_user&.id,
80
83
  platform: target.browser.platform.name,
@@ -21,9 +21,9 @@ module Ditty
21
21
  end
22
22
 
23
23
  def authenticate(unencrypted)
24
- return false if crypted_password.blank?
24
+ return false if crypted_password.blank? || ::BCrypt::Password.new(crypted_password) != unencrypted
25
25
 
26
- self if ::BCrypt::Password.new(crypted_password) == unencrypted
26
+ self
27
27
  end
28
28
 
29
29
  def persisted?
@@ -69,7 +69,7 @@ module Ditty
69
69
  end
70
70
 
71
71
  # Callbacks
72
- def before_save
72
+ def before_validation
73
73
  super
74
74
  encrypt_password unless password == '' || password.nil?
75
75
  end
@@ -81,7 +81,7 @@ module Ditty
81
81
  end
82
82
 
83
83
  def password_required
84
- crypted_password.blank? || !password.blank?
84
+ crypted_password.blank? || password.present?
85
85
  end
86
86
  end
87
87
  end
@@ -4,6 +4,7 @@ require 'ditty/models/base'
4
4
  require 'digest/md5'
5
5
  require 'active_support'
6
6
  require 'active_support/core_ext/object/blank'
7
+ require 'ditty/models/role'
7
8
 
8
9
  # Why not store this in Elasticsearch?
9
10
  module Ditty
@@ -88,20 +89,5 @@ module Ditty
88
89
  def display_name
89
90
  name || username
90
91
  end
91
-
92
- class << self
93
- def anonymous_user
94
- role = ::Ditty::Role.find_or_create(name: 'anonymous')
95
- ::Ditty::User.where(roles: role).first
96
- end
97
-
98
- def create_anonymous_user(email = 'anonymous@ditty.io')
99
- return if anonymous_user
100
-
101
- user = ::Ditty::User.find_or_create(email: email)
102
- user.remove_role ::Ditty::Role.find_or_create(name: 'user')
103
- user.add_role ::Ditty::Role.find_or_create(name: 'anonymous') unless user.role?('anonymous')
104
- end
105
- end
106
92
  end
107
93
  end
@@ -75,7 +75,7 @@ module Ditty
75
75
  else
76
76
  return (0..0) if list.current_page > page_count
77
77
 
78
- a = 1 + (list.current_page - 1) * page_size
78
+ a = 1 + ((list.current_page - 1) * page_size)
79
79
  b = a + page_size - 1
80
80
  b = pagination_record_count if b > pagination_record_count
81
81
  a..b
@@ -55,7 +55,11 @@ module Ditty
55
55
  attr_writer :values
56
56
 
57
57
  def read(filename)
58
- base = YAML.safe_load(ERB.new(File.read(filename)).result).deep_symbolize_keys
58
+ base = if Gem::Version.new(Psych::VERSION) >= Gem::Version.new('3.1.0.pre1')
59
+ ::YAML.safe_load(ERB.new(File.read(filename)).result, permitted_classes: [Symbol])
60
+ else
61
+ ::YAML.safe_load(ERB.new(File.read(filename)).result, [Symbol])
62
+ end.deep_symbolize_keys
59
63
  base.deep_merge!(base[ENV['APP_ENV'].to_sym]) unless ENV['APP_ENV'].nil? || base[ENV['APP_ENV'].to_sym].nil?
60
64
  base
61
65
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'fileutils'
4
+
3
5
  namespace :ditty do
4
6
  desc 'Run the ditty console'
5
7
  task :console do
@@ -34,17 +36,37 @@ namespace :ditty do
34
36
  end
35
37
 
36
38
  namespace :prep do
39
+ desc 'Create a user'
40
+ task :user, [:email, :password] do |_t, args|
41
+ require 'ditty/listener'
42
+
43
+ identity = Ditty::Identity.new(username: args[:email], password: args[:password])
44
+ identity.password_confirmation = identity.password
45
+ user = Ditty::User.new(email: identity.username)
46
+ begin
47
+ identity.valid?
48
+ DB.transaction do
49
+ user.save_changes
50
+ user.add_identity identity
51
+ Ditty::Listener.new.user_register(target: self, values: { user: user })
52
+ end
53
+ rescue StandardError => e
54
+ Ditty::Services::Logger.error "Could not regster super user: #{e.message}"
55
+ Ditty::Services::Logger.debug e
56
+ end
57
+ end
58
+
37
59
  desc 'Check that the required Ditty folders are present'
38
60
  task :folders do
39
61
  puts 'Prepare the Ditty folders'
40
- Dir.mkdir 'pids' unless File.exist?('pids')
41
- Dir.mkdir 'logs' unless File.exist?('logs')
62
+ FileUtils.mkdir_p 'pids'
63
+ FileUtils.mkdir_p 'logs'
42
64
  end
43
65
 
44
66
  desc 'Check that the public folder is present and populated'
45
67
  task :public do
46
68
  puts 'Preparing the Ditty public folder'
47
- Dir.mkdir 'public' unless File.exist?('public')
69
+ FileUtils.mkdir_p 'public'
48
70
  ::Ditty::Components.public_folder.each do |path|
49
71
  puts "Checking #{path}"
50
72
  path = "#{path}/."
@@ -55,7 +77,7 @@ namespace :ditty do
55
77
  desc 'Check that the migrations folder is present and populated'
56
78
  task :migrations do
57
79
  puts 'Preparing the Ditty migrations folder'
58
- Dir.mkdir 'migrations' unless File.exist?('migrations')
80
+ FileUtils.mkdir_p 'migrations'
59
81
  ::Ditty::Components.migrations.each do |path|
60
82
  path = "#{path}/."
61
83
  FileUtils.cp_r path, 'migrations' unless File.expand_path(path).eql? File.expand_path('migrations')
@@ -1,29 +1,31 @@
1
1
  .row
2
2
  .col-md-12
3
3
  = haml :'partials/search'
4
- %table.table.table-striped.table-bordered.table-hover
5
- %thead.thead-dark
6
- %tr
7
- <%- columns.each do |col| -%>
8
- %th <%= col.to_s.titleize %>
9
- <%- end %>
10
- %th
11
- %tbody
12
- - if list.count > 0
13
- - list.all.each do |entity|
14
- %tr
15
- <%- columns.each do |col| -%>
16
- %td= entity.<%= col %>
17
- <%- end %>
18
- %td
19
- %a{ href: "#{base_path}/#{entity.display_id}", title: 'Show' }
20
- %i.fa.fa-search
21
- - if policy(entity).update?
22
- %a{ href: "#{base_path}/#{entity.display_id}/edit", title: 'Edit' }
23
- %i.fa.fa-edit
24
- - else
4
+ .card.card-default.shadow.table-responsive
5
+ %table.table.table-striped.table-bordered.table-hover.mb-0
6
+ %thead.thead-dark
25
7
  %tr
26
- %td.text-center{ colspan: <%= columns.count + 1 %> } No <%= model_name.pluralize %>
8
+ <%- columns.each do |col| -%>
9
+ %th <%= col.to_s.titleize %>
10
+ <%- end %>
11
+ %th
12
+ %tbody
13
+ - if list.count > 0
14
+ - list.all.each do |entity|
15
+ %tr
16
+ <%- columns.each do |col| -%>
17
+ %td= entity.<%= col %>
18
+ <%- end %>
19
+ %td
20
+ %a{ href: "#{base_path}/#{entity.display_id}", title: 'Show' }
21
+ %i.fa.fa-search
22
+ - if policy(entity).update?
23
+ %a{ href: "#{base_path}/#{entity.display_id}/edit", title: 'Edit' }
24
+ %i.fa.fa-edit
25
+ - else
26
+ %tr
27
+ %td.text-center{ colspan: <%= columns.count + 1 %> } No <%= model_name.pluralize %>
27
28
 
28
- - if list.count > 0
29
- = pagination(list, base_path)
29
+ - if list.count > 0
30
+ .card-body
31
+ = pagination(list, base_path)
data/lib/ditty/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ditty
4
- VERSION = '0.10.2'
4
+ VERSION = '0.11.1'
5
5
  end
@@ -1,36 +1,38 @@
1
1
  .row
2
2
  .col-md-12
3
3
  = haml :'partials/search'
4
- %table.table.table-striped.table-bordered.table-hover
5
- %thead.thead-dark
6
- %tr
7
- %th= "User&nbsp;#{sort_ui(:user)}"
8
- %th= "Action&nbsp;#{sort_ui(:action)}"
9
- %th= "Details&nbsp;#{sort_ui(:details)}"
10
- %th= "IP Address&nbsp;#{sort_ui(:ip_address)}"
11
- %th= "Browser&nbsp;#{sort_ui(:browser)}"
12
- %th= "Device&nbsp;#{sort_ui(:device)}"
13
- %th= "Platform&nbsp;#{sort_ui(:platform)}"
14
- %th= "Created&nbsp;At&nbsp;#{sort_ui(:created_at)}"
15
- %tbody
16
- - if list.count > 0
17
- - list.all.each do |entity|
18
- %tr
19
- %td
20
- -if entity.user
21
- %a{ href: "#{settings.map_path}/users/#{entity.user.id}" }= entity.user.email
22
- -else
23
- None
24
- %td= entity.action
25
- %td= entity.details
26
- %td= entity.ip_address || 'Unknown'
27
- %td= entity.browser || 'Unknown'
28
- %td= entity.device || 'Unknown'
29
- %td= entity.platform || 'Unknown'
30
- %td= entity.created_at&.strftime('%Y-%m-%d %H:%M:%S') || 'Unknown'
31
- - else
4
+ .card.card-default.shadow.table-responsive
5
+ %table.table.table-striped.table-bordered.table-hover.mb-0
6
+ %thead.thead-dark
32
7
  %tr
33
- %td.text-center{ colspan: 4 } No records
8
+ %th= "User&nbsp;#{sort_ui(:user)}"
9
+ %th= "Action&nbsp;#{sort_ui(:action)}"
10
+ %th= "Details&nbsp;#{sort_ui(:details)}"
11
+ %th= "IP Address&nbsp;#{sort_ui(:ip_address)}"
12
+ %th= "Browser&nbsp;#{sort_ui(:browser)}"
13
+ %th= "Device&nbsp;#{sort_ui(:device)}"
14
+ %th= "Platform&nbsp;#{sort_ui(:platform)}"
15
+ %th= "Created&nbsp;At&nbsp;#{sort_ui(:created_at)}"
16
+ %tbody
17
+ - if list.count > 0
18
+ - list.all.each do |entity|
19
+ %tr
20
+ %td
21
+ -if entity.user
22
+ %a{ href: "#{settings.map_path}/users/#{entity.user.id}" }= entity.user.email
23
+ -else
24
+ None
25
+ %td= entity.action
26
+ %td= entity.details
27
+ %td= entity.ip_address || 'Unknown'
28
+ %td= entity.browser || 'Unknown'
29
+ %td= entity.device || 'Unknown'
30
+ %td= entity.platform || 'Unknown'
31
+ %td= entity.created_at&.strftime('%Y-%m-%d %H:%M:%S') || 'Unknown'
32
+ - else
33
+ %tr
34
+ %td.text-center{ colspan: 4 } No records
34
35
 
35
- - if list.count > 0
36
- = pagination(list, base_path)
36
+ - if list.count > 0
37
+ .card-body
38
+ = pagination(list, base_path)
@@ -1,8 +1,8 @@
1
1
  = form_tag("#{settings.map_path}/auth/identity/callback", attributes: { class: 'user' }) do
2
2
  .form-group
3
- %input.form-control.form-control-user{ name: 'username', type: 'email', 'aria-describedby': 'emailHelp', placeholder: 'Enter Email Address...' }
3
+ %input.form-control.form-control-user{ name: 'username', type: 'email', 'aria-describedby': 'emailHelp', placeholder: 'Enter Email Address...', required: true }
4
4
  .form-group
5
- %input.form-control.form-control-user{ name: 'password', type: 'password', placeholder: 'Password' }
5
+ %input.form-control.form-control-user{ name: 'password', type: 'password', placeholder: 'Password', required: true }
6
6
  / .form-group
7
7
  / .custom-control.custom-checkbox.small
8
8
  / <input type="checkbox" class="custom-control-input" id="customCheck">
data/views/layout.haml CHANGED
@@ -45,11 +45,14 @@
45
45
 
46
46
  #content-wrapper.d-flex.flex-column
47
47
  #content
48
- = haml :'partials/topbar'
48
+ = haml :'partials/topbar', locals: { title: (defined?(title) ? title : nil) }
49
49
  .container-fluid
50
- / TODO
51
- / .row
52
- / .col-md-12
50
+ - if (defined?(hide_title).nil? || hide_title == false) && defined?(title)
51
+ .row.mb-2
52
+ .col-md-9.my-auto
53
+ %h1.text-dark= title
54
+ .col.md-3.text-right.my-auto
55
+ = haml :'partials/actions', locals: { actions: defined?(actions) ? actions : {} }
53
56
  = haml :'partials/notifications'
54
57
 
55
58
  = yield
@@ -1,15 +1,13 @@
1
- - if defined?(actions)
1
+ - if defined?(actions) && actions != nil
2
2
  - if actions.count > 1
3
3
  - link, text = actions.shift
4
4
  .btn-group.text-right
5
5
  %a.btn.btn-primary{ href: link }= text
6
- %button.btn.btn-primary.dropdown-toggle{ type: 'button', id: 'actions-toggle', data: { toggle: 'dropdown' } }
6
+ %button.btn.btn-primary.dropdown-toggle{ type: 'button', id: 'actions-toggle', data: { toggle: 'dropdown' }, 'aria-haspopup': 'true', 'aria-expanded': 'false' }
7
7
  %span.caret
8
8
  %span.sr-only Toggle Dropdown
9
- %ul.dropdown-menu{ 'aria-labelledby': 'actions-toggle' }
10
- -actions.each do |k, v|
11
- %li
12
- %a{ href: k }= v
13
- - elsif actions.count > 0
14
- -actions.each do |k, v|
15
- %a.btn.btn-primary{ href: k }= v
9
+ .dropdown-menu
10
+ - actions.each do |k, v|
11
+ %a.dropdown-item{ href: k }= v
12
+ - elsif actions.count == 1
13
+ %a.btn.btn-primary{ href: actions.keys.first }= actions.values.first
@@ -1,27 +1,29 @@
1
1
  .row
2
2
  .col-md-12
3
3
  = haml :'partials/search'
4
- %table.table.table-striped.table-bordered.table-hover
5
- %thead.thead-dark
6
- %tr
7
- %th= "Name#{sort_ui(:name)}"
8
- %th Parent
9
- %th
10
- %tbody
11
- - if list.count > 0
12
- - list.all.each do |entity|
13
- %tr
14
- %td
15
- %a{ href: "#{base_path}/#{entity.display_id}" }= entity.name
16
- %td
17
- %a{ href: "#{base_path}/#{entity.parent_id}" }= entity.parent&.name || '(None)'
18
- %td
19
- - if policy(entity).update?
20
- %a{ href: "#{base_path}/#{entity.display_id}/edit", title: 'Edit' }
21
- %i.fa.fa-edit
22
- - else
4
+ .card.card-default.shadow.table-responsive
5
+ %table.table.table-striped.table-bordered.table-hover.mb-0
6
+ %thead.thead-dark
23
7
  %tr
24
- %td.text-center{ colspan: 3 } No Roles
8
+ %th= "Name#{sort_ui(:name)}"
9
+ %th Parent
10
+ %th
11
+ %tbody
12
+ - if list.count > 0
13
+ - list.all.each do |entity|
14
+ %tr
15
+ %td
16
+ %a{ href: "#{base_path}/#{entity.display_id}" }= entity.name
17
+ %td
18
+ %a{ href: "#{base_path}/#{entity.parent_id}" }= entity.parent&.name || '(None)'
19
+ %td
20
+ - if policy(entity).update?
21
+ %a{ href: "#{base_path}/#{entity.display_id}/edit", title: 'Edit' }
22
+ %i.fa.fa-edit
23
+ - else
24
+ %tr
25
+ %td.text-center{ colspan: 3 } No Roles
25
26
 
26
- - if list.count > 0
27
- = pagination(list, base_path)
27
+ - if list.count > 0
28
+ .card-body
29
+ = pagination(list, base_path)
@@ -1,28 +1,31 @@
1
1
  .row
2
2
  .col-md-12
3
3
  = haml :'partials/search'
4
- %table.table.table-striped.table-bordered.table-hover
5
- %thead.thead-dark
6
- %tr
7
- %th= "User&nbsp;#{sort_ui(:user_id)}"
8
- %th= "IP Address&nbsp;#{sort_ui(:ip_address)}"
9
- %th= "Device&nbsp;#{sort_ui(:device)}"
10
- %th= "Platform&nbsp;#{sort_ui(:platform)}"
11
- %th= "Browser&nbsp;#{sort_ui(:browser)}"
12
- %th= "Last Seen&nbsp;#{sort_ui(:updated_at)}"
13
- %tbody
14
- - if list.count.positive?
15
- - list.all.each do |entity|
16
- %tr
17
- %td= entity.user&.email || 'Unknown'
18
- %td
19
- %a{ href: "#{base_path}/#{entity.display_id}" }= entity.ip_address || 'Unknown'
20
- %td= entity.device || 'Unknown'
21
- %td= entity.platform || 'Unknown'
22
- %td= entity.browser || 'Unknown'
23
- %td= entity.updated_at
24
- - else
4
+ .card.card-default.shadow.table-responsive
5
+ %table.table.table-striped.table-bordered.table-hover.mb-0
6
+ %thead.thead-dark
25
7
  %tr
26
- %td.text-center{ colspan: 6 } No records
8
+ %th= "User&nbsp;#{sort_ui(:user_id)}"
9
+ %th= "IP Address&nbsp;#{sort_ui(:ip_address)}"
10
+ %th= "Device&nbsp;#{sort_ui(:device)}"
11
+ %th= "Platform&nbsp;#{sort_ui(:platform)}"
12
+ %th= "Browser&nbsp;#{sort_ui(:browser)}"
13
+ %th= "Last Seen&nbsp;#{sort_ui(:updated_at)}"
14
+ %tbody
15
+ - if list.count.positive?
16
+ - list.all.each do |entity|
17
+ %tr
18
+ %td= entity.user&.email || 'Unknown'
19
+ %td
20
+ %a{ href: "#{base_path}/#{entity.display_id}" }= entity.ip_address || 'Unknown'
21
+ %td= entity.device || 'Unknown'
22
+ %td= entity.platform || 'Unknown'
23
+ %td= entity.browser || 'Unknown'
24
+ %td= entity.updated_at
25
+ - else
26
+ %tr
27
+ %td.text-center{ colspan: 6 } No records
27
28
 
28
- =pagination(list, base_path)
29
+ - if list.count > 0
30
+ .card-body
31
+ = pagination(list, base_path)
@@ -1,35 +1,37 @@
1
1
  .row
2
2
  .col-md-12
3
3
  = haml :'partials/search'
4
- %table.table.table-striped.table-bordered.table-hover
5
- %thead.thead-dark
6
- %tr
7
- %th= "Email&nbsp;#{sort_ui(:email)}"
8
- %th= "Name&nbsp;#{sort_ui(:name)}"
9
- %th= "Surname&nbsp;#{sort_ui(:surname)}"
10
- %th Roles
11
- %th= "Signed&nbsp;Up&nbsp;#{sort_ui(:created_at)}"
12
- %th
13
- %tbody
14
- - if list.count > 0
15
- - list.all.each do |entity|
16
- %tr
17
- %td
18
- - if policy(entity).read?
19
- %a{ href: "#{base_path}/#{entity.display_id}" }= entity.email
20
- - else
21
- = entity.email
22
- %td= entity.name
23
- %td= entity.surname
24
- %td= entity.all_roles.map(&:name).map(&:titlecase).join(', ')
25
- %td= entity.created_at.strftime('%Y-%m-%d')
26
- %td
27
- - if policy(entity).update?
28
- %a{ href: "#{base_path}/#{entity.display_id}/edit", title: 'Edit' }
29
- %i.fa.fa-edit
30
- - else
4
+ .card.card-default.shadow.table-responsive
5
+ %table.table.table-striped.table-bordered.table-hover.mb-0
6
+ %thead.thead-dark
31
7
  %tr
32
- %td.text-center{ colspan: 6 } No records
8
+ %th= "Email&nbsp;#{sort_ui(:email)}"
9
+ %th= "Name&nbsp;#{sort_ui(:name)}"
10
+ %th= "Surname&nbsp;#{sort_ui(:surname)}"
11
+ %th Roles
12
+ %th= "Signed&nbsp;Up&nbsp;#{sort_ui(:created_at)}"
13
+ %th
14
+ %tbody
15
+ - if list.count > 0
16
+ - list.all.each do |entity|
17
+ %tr
18
+ %td
19
+ - if policy(entity).read?
20
+ %a{ href: "#{base_path}/#{entity.display_id}" }= entity.email
21
+ - else
22
+ = entity.email
23
+ %td= entity.name
24
+ %td= entity.surname
25
+ %td= entity.all_roles.map(&:name).map(&:titlecase).join(', ')
26
+ %td= entity.created_at.strftime('%Y-%m-%d')
27
+ %td
28
+ - if policy(entity).update?
29
+ %a{ href: "#{base_path}/#{entity.display_id}/edit", title: 'Edit' }
30
+ %i.fa.fa-edit
31
+ - else
32
+ %tr
33
+ %td.text-center{ colspan: 6 } No records
33
34
 
34
- - if list.count > 0
35
- = pagination(list, base_path)
35
+ - if list.count > 0
36
+ .card-body
37
+ = pagination(list, base_path)
@@ -37,7 +37,7 @@
37
37
  .card-body
38
38
  %h4.card-title Change Password
39
39
  = edit_form_tag("#{base_path}/#{entity.display_id}/identity") do
40
- - if current_user.super_admin? == false
40
+ - if current_user.super_admin? == false || current_user_id == entity.id
41
41
  = form_control(:old_password, identity, type: 'password', placeholder: 'Your current password', value: '')
42
42
  = form_control(:password, identity, type: 'password', placeholder: 'Your new password')
43
43
  = form_control(:password_confirmation, identity, type: 'password', label: 'Confirm Password', placeholder: 'Confirm your password')
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ditty
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.2
4
+ version: 0.11.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jurgens du Toit
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-09-08 00:00:00.000000000 Z
11
+ date: 2023-01-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -294,6 +294,9 @@ dependencies:
294
294
  name: haml
295
295
  requirement: !ruby/object:Gem::Requirement
296
296
  requirements:
297
+ - - "~>"
298
+ - !ruby/object:Gem::Version
299
+ version: '5.1'
297
300
  - - ">="
298
301
  - !ruby/object:Gem::Version
299
302
  version: 5.1.2
@@ -301,6 +304,9 @@ dependencies:
301
304
  prerelease: false
302
305
  version_requirements: !ruby/object:Gem::Requirement
303
306
  requirements:
307
+ - - "~>"
308
+ - !ruby/object:Gem::Version
309
+ version: '5.1'
304
310
  - - ">="
305
311
  - !ruby/object:Gem::Version
306
312
  version: 5.1.2
@@ -350,44 +356,44 @@ dependencies:
350
356
  name: omniauth
351
357
  requirement: !ruby/object:Gem::Requirement
352
358
  requirements:
353
- - - "~>"
359
+ - - ">="
354
360
  - !ruby/object:Gem::Version
355
361
  version: '1.0'
356
362
  type: :runtime
357
363
  prerelease: false
358
364
  version_requirements: !ruby/object:Gem::Requirement
359
365
  requirements:
360
- - - "~>"
366
+ - - ">="
361
367
  - !ruby/object:Gem::Version
362
368
  version: '1.0'
363
369
  - !ruby/object:Gem::Dependency
364
370
  name: omniauth-identity
365
371
  requirement: !ruby/object:Gem::Requirement
366
372
  requirements:
367
- - - "~>"
373
+ - - ">="
368
374
  - !ruby/object:Gem::Version
369
375
  version: '1.0'
370
376
  type: :runtime
371
377
  prerelease: false
372
378
  version_requirements: !ruby/object:Gem::Requirement
373
379
  requirements:
374
- - - "~>"
380
+ - - ">="
375
381
  - !ruby/object:Gem::Version
376
382
  version: '1.0'
377
383
  - !ruby/object:Gem::Dependency
378
384
  name: pundit
379
385
  requirement: !ruby/object:Gem::Requirement
380
386
  requirements:
381
- - - "~>"
387
+ - - ">="
382
388
  - !ruby/object:Gem::Version
383
- version: '1.0'
389
+ version: '2.0'
384
390
  type: :runtime
385
391
  prerelease: false
386
392
  version_requirements: !ruby/object:Gem::Requirement
387
393
  requirements:
388
- - - "~>"
394
+ - - ">="
389
395
  - !ruby/object:Gem::Version
390
- version: '1.0'
396
+ version: '2.0'
391
397
  - !ruby/object:Gem::Dependency
392
398
  name: rack-contrib
393
399
  requirement: !ruby/object:Gem::Requirement
@@ -546,14 +552,14 @@ dependencies:
546
552
  name: wisper
547
553
  requirement: !ruby/object:Gem::Requirement
548
554
  requirements:
549
- - - "~>"
555
+ - - ">="
550
556
  - !ruby/object:Gem::Version
551
557
  version: '2.0'
552
558
  type: :runtime
553
559
  prerelease: false
554
560
  version_requirements: !ruby/object:Gem::Requirement
555
561
  requirements:
556
- - - "~>"
562
+ - - ">="
557
563
  - !ruby/object:Gem::Version
558
564
  version: '2.0'
559
565
  description: Sinatra Based Application Framework