ditty 0.10.1 → 0.11.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +3 -2
- data/Readme.md +1 -1
- data/ditty.gemspec +5 -5
- data/lib/ditty/cli.rb +8 -1
- data/lib/ditty/components/ditty.rb +3 -2
- data/lib/ditty/controllers/application_controller.rb +0 -1
- data/lib/ditty/controllers/component_controller.rb +5 -5
- data/lib/ditty/controllers/users_controller.rb +10 -10
- data/lib/ditty/db.rb +2 -2
- data/lib/ditty/emails/base.rb +1 -1
- data/lib/ditty/generators/project_generator.rb +3 -3
- data/lib/ditty/helpers/authentication.rb +4 -0
- data/lib/ditty/helpers/pundit.rb +2 -2
- data/lib/ditty/helpers/response.rb +6 -6
- data/lib/ditty/helpers/views.rb +9 -0
- data/lib/ditty/listener.rb +4 -1
- data/lib/ditty/models/identity.rb +8 -4
- data/lib/ditty/models/user.rb +1 -15
- data/lib/ditty/services/pagination_wrapper.rb +1 -1
- data/lib/ditty/services/settings.rb +5 -1
- data/lib/ditty/tasks/ditty.rake +26 -4
- data/lib/ditty/templates/settings.yml.erb +10 -0
- data/lib/ditty/templates/views/index.haml.tt +26 -24
- data/lib/ditty/version.rb +1 -1
- data/views/audit_logs/index.haml +33 -31
- data/views/auth/identity.haml +2 -2
- data/views/layout.haml +7 -4
- data/views/partials/actions.haml +7 -9
- data/views/partials/footer.haml +1 -8
- data/views/partials/user_associations.haml +2 -2
- data/views/roles/index.haml +24 -22
- data/views/user_login_traits/index.haml +26 -23
- data/views/users/index.haml +32 -30
- data/views/users/profile.haml +1 -1
- data/views/users/user.haml +1 -1
- metadata +18 -13
- data/views/partials/timespan_selector.haml +0 -64
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0ecdd202f458e55d205e3e77b295a3c8908f7d3ecaf3d894fa9ae09c62d8b4fd
|
4
|
+
data.tar.gz: bcc5967235a668d896cee25228130b1ab2d98761dc2fb2125d5f7f2d2912881b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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', '
|
49
|
-
spec.add_dependency 'omniauth-identity', '
|
50
|
-
spec.add_dependency 'pundit', '
|
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', '
|
62
|
+
spec.add_dependency 'wisper', '>= 2.0'
|
63
63
|
end
|
data/lib/ditty/cli.rb
CHANGED
@@ -31,6 +31,7 @@ module Ditty
|
|
31
31
|
require './application' if File.exist?('application.rb')
|
32
32
|
require 'ditty/db' unless defined?(DB)
|
33
33
|
::Ditty::Components.tasks
|
34
|
+
|
34
35
|
def server
|
35
36
|
# Ensure the token files are present
|
36
37
|
Rake::Task['ditty:generate_tokens'].invoke
|
@@ -47,8 +48,14 @@ module Ditty
|
|
47
48
|
Rake::Task['ditty:seed'].invoke
|
48
49
|
|
49
50
|
# RackUP!
|
51
|
+
rack_opts = {
|
52
|
+
environment: ENV['APP_ENV'] || 'development',
|
53
|
+
Port: ENV['APP_PORT'] || 9292,
|
54
|
+
Host: ENV['APP_HOST'] || '0.0.0.0',
|
55
|
+
config: 'config.ru'
|
56
|
+
}
|
50
57
|
puts 'Starting the Ditty Server'
|
51
|
-
Rack::Server.start(
|
58
|
+
Rack::Server.start(rack_opts)
|
52
59
|
end
|
53
60
|
|
54
61
|
desc 'migrate', 'Run the Ditty migrations'
|
@@ -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') { |
|
70
|
-
::Ditty::Role.find_or_create(name: 'user') { |
|
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
|
|
@@ -7,6 +7,8 @@ require 'sinatra/json'
|
|
7
7
|
|
8
8
|
module Ditty
|
9
9
|
class ComponentController < ApplicationController
|
10
|
+
use Rack::Csrf, raise: ENV['APP_ENV'] == 'development' unless ENV['APP_ENV'] == 'test'
|
11
|
+
|
10
12
|
helpers Helpers::Component, Helpers::Response
|
11
13
|
|
12
14
|
set base_path: nil
|
@@ -37,9 +39,7 @@ module Ditty
|
|
37
39
|
after do
|
38
40
|
return if settings.environment == 'production'
|
39
41
|
|
40
|
-
if (response.successful? || response.redirection?) && @skip_verify == false
|
41
|
-
verify_authorized
|
42
|
-
end
|
42
|
+
verify_authorized if (response.successful? || response.redirection?) && @skip_verify == false
|
43
43
|
end
|
44
44
|
|
45
45
|
after '/' do
|
@@ -65,7 +65,7 @@ module Ditty
|
|
65
65
|
|
66
66
|
entity = settings.model_class.new(permitted_parameters(settings.model_class, :create))
|
67
67
|
haml :"#{view_location}/new",
|
68
|
-
locals: { entity: entity, title: heading(:new) },
|
68
|
+
locals: { entity: entity, title: heading(:new), actions: actions(action: :new) },
|
69
69
|
layout: layout
|
70
70
|
end
|
71
71
|
|
@@ -98,7 +98,7 @@ module Ditty
|
|
98
98
|
|
99
99
|
flash[:redirect_to] = "#{base_path}/#{entity.display_id}" unless flash.keep(:redirect_to)
|
100
100
|
haml :"#{view_location}/edit",
|
101
|
-
locals: { entity: entity, title: heading(:edit) },
|
101
|
+
locals: { entity: entity, title: heading(:edit), actions: actions(entity: entity, action: :edit) },
|
102
102
|
layout: layout
|
103
103
|
end
|
104
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
|
-
|
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, :
|
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 '
|
32
|
+
::Ditty::Services::Logger.error '*** NO DATABASE CONNECTION SET UP ***'
|
33
33
|
end
|
data/lib/ditty/emails/base.rb
CHANGED
@@ -71,7 +71,7 @@ module Ditty
|
|
71
71
|
template = File.expand_path("./views/#{file}.haml")
|
72
72
|
return template if File.file? template
|
73
73
|
|
74
|
-
template = File.expand_path("./#{file}.haml",
|
74
|
+
template = File.expand_path("./#{file}.haml", ::Ditty::Ditty.view_folder)
|
75
75
|
return template if File.file? template
|
76
76
|
|
77
77
|
file
|
@@ -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', './
|
45
|
-
copy_file '../../../spec/support/api_shared_examples.rb', './
|
46
|
-
copy_file '../../../spec/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)
|
data/lib/ditty/helpers/pundit.rb
CHANGED
@@ -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
|
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[
|
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}" : '')
|
data/lib/ditty/helpers/views.rb
CHANGED
@@ -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
|
data/lib/ditty/listener.rb
CHANGED
@@ -70,11 +70,14 @@ module Ditty
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def log_action(values)
|
73
|
-
|
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
|
26
|
+
self
|
27
27
|
end
|
28
28
|
|
29
29
|
def persisted?
|
@@ -37,6 +37,10 @@ module Ditty
|
|
37
37
|
}
|
38
38
|
end
|
39
39
|
|
40
|
+
def uid
|
41
|
+
user&.id
|
42
|
+
end
|
43
|
+
|
40
44
|
# Validation
|
41
45
|
def validate
|
42
46
|
super
|
@@ -65,7 +69,7 @@ module Ditty
|
|
65
69
|
end
|
66
70
|
|
67
71
|
# Callbacks
|
68
|
-
def
|
72
|
+
def before_validation
|
69
73
|
super
|
70
74
|
encrypt_password unless password == '' || password.nil?
|
71
75
|
end
|
@@ -77,7 +81,7 @@ module Ditty
|
|
77
81
|
end
|
78
82
|
|
79
83
|
def password_required
|
80
|
-
crypted_password.blank? ||
|
84
|
+
crypted_password.blank? || password.present?
|
81
85
|
end
|
82
86
|
end
|
83
87
|
end
|
data/lib/ditty/models/user.rb
CHANGED
@@ -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 =
|
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
|
data/lib/ditty/tasks/ditty.rake
CHANGED
@@ -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
|
-
|
41
|
-
|
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
|
-
|
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
|
-
|
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')
|
@@ -7,6 +7,16 @@ logger:
|
|
7
7
|
class: Logger
|
8
8
|
level: DEBUG
|
9
9
|
options: logs/<%%= ENV['APP_ENV'] %>.log
|
10
|
+
# email:
|
11
|
+
# from_address: 'no-reply@ditty.io'
|
12
|
+
# delivery_method: smtp
|
13
|
+
# options:
|
14
|
+
# address: smtp.server
|
15
|
+
# port: 25
|
16
|
+
# user_name: 'SMTPUSERNAME'
|
17
|
+
# password: 'SMTPPASSWORD'
|
18
|
+
# authentication: login
|
19
|
+
# enable_starttls_auto: true
|
10
20
|
# authentication:
|
11
21
|
# google_oauth2:
|
12
22
|
# login_prompt: Log in with Google
|
@@ -1,29 +1,31 @@
|
|
1
1
|
.row
|
2
2
|
.col-md-12
|
3
3
|
= haml :'partials/search'
|
4
|
-
|
5
|
-
%
|
6
|
-
%
|
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
|
-
|
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
|
-
|
29
|
+
- if list.count > 0
|
30
|
+
.card-body
|
31
|
+
= pagination(list, base_path)
|
data/lib/ditty/version.rb
CHANGED
data/views/audit_logs/index.haml
CHANGED
@@ -1,36 +1,38 @@
|
|
1
1
|
.row
|
2
2
|
.col-md-12
|
3
3
|
= haml :'partials/search'
|
4
|
-
|
5
|
-
%
|
6
|
-
%
|
7
|
-
%th= "User #{sort_ui(:user)}"
|
8
|
-
%th= "Action #{sort_ui(:action)}"
|
9
|
-
%th= "Details #{sort_ui(:details)}"
|
10
|
-
%th= "IP Address #{sort_ui(:ip_address)}"
|
11
|
-
%th= "Browser #{sort_ui(:browser)}"
|
12
|
-
%th= "Device #{sort_ui(:device)}"
|
13
|
-
%th= "Platform #{sort_ui(:platform)}"
|
14
|
-
%th= "Created At #{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
|
-
%
|
8
|
+
%th= "User #{sort_ui(:user)}"
|
9
|
+
%th= "Action #{sort_ui(:action)}"
|
10
|
+
%th= "Details #{sort_ui(:details)}"
|
11
|
+
%th= "IP Address #{sort_ui(:ip_address)}"
|
12
|
+
%th= "Browser #{sort_ui(:browser)}"
|
13
|
+
%th= "Device #{sort_ui(:device)}"
|
14
|
+
%th= "Platform #{sort_ui(:platform)}"
|
15
|
+
%th= "Created At #{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
|
-
|
36
|
+
- if list.count > 0
|
37
|
+
.card-body
|
38
|
+
= pagination(list, base_path)
|
data/views/auth/identity.haml
CHANGED
@@ -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
|
-
|
51
|
-
|
52
|
-
|
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
|
data/views/partials/actions.haml
CHANGED
@@ -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
|
-
|
10
|
-
-actions.each do |k, v|
|
11
|
-
%
|
12
|
-
|
13
|
-
|
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
|
data/views/partials/footer.haml
CHANGED
@@ -3,11 +3,4 @@
|
|
3
3
|
.container.my-auto
|
4
4
|
-if ENV['SKIP_BRANDING'].blank?
|
5
5
|
.copyright.text-center.my-auto.text-dark
|
6
|
-
%
|
7
|
-
Copyright ©
|
8
|
-
%a{ href: 'https://hackerpla.net/requestd', target: '_blank' }
|
9
|
-
HackerPlanet
|
10
|
-
2020
|
11
|
-
%p.mb-0
|
12
|
-
%a{ href: 'https://hackerpla.net/requestd', target: '_blank' }
|
13
|
-
%img.rounded{ src: 'http://hackerpla.net/wp-content/uploads/2014/07/HP_1C_128x128.png', height: 60 }
|
6
|
+
%span Copyright © Ditty.io 2019
|
@@ -21,8 +21,8 @@
|
|
21
21
|
- users = user_options.select { |k, v| entity.users.map(&:id).include?(k) == false }
|
22
22
|
- if users && users.count.positive?
|
23
23
|
= new_form_tag "#{base_path}/#{entity.display_id}/users" do
|
24
|
-
.input-group
|
25
|
-
%select.form-control{ name: "#{group}[user_id]", id: 'user_id' }
|
24
|
+
.input-group.tall-select2
|
25
|
+
%select.form-control.select2{ name: "#{group}[user_id]", id: 'user_id' }
|
26
26
|
%option{ value: '' } Select User
|
27
27
|
- users.each do |key, value|
|
28
28
|
%option{ value: key }= value
|
data/views/roles/index.haml
CHANGED
@@ -1,27 +1,29 @@
|
|
1
1
|
.row
|
2
2
|
.col-md-12
|
3
3
|
= haml :'partials/search'
|
4
|
-
|
5
|
-
%
|
6
|
-
%
|
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
|
-
%
|
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
|
-
|
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
|
-
|
5
|
-
%
|
6
|
-
%
|
7
|
-
%th= "User #{sort_ui(:user_id)}"
|
8
|
-
%th= "IP Address #{sort_ui(:ip_address)}"
|
9
|
-
%th= "Device #{sort_ui(:device)}"
|
10
|
-
%th= "Platform #{sort_ui(:platform)}"
|
11
|
-
%th= "Browser #{sort_ui(:browser)}"
|
12
|
-
%th= "Last Seen #{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
|
-
%
|
8
|
+
%th= "User #{sort_ui(:user_id)}"
|
9
|
+
%th= "IP Address #{sort_ui(:ip_address)}"
|
10
|
+
%th= "Device #{sort_ui(:device)}"
|
11
|
+
%th= "Platform #{sort_ui(:platform)}"
|
12
|
+
%th= "Browser #{sort_ui(:browser)}"
|
13
|
+
%th= "Last Seen #{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
|
-
|
29
|
+
- if list.count > 0
|
30
|
+
.card-body
|
31
|
+
= pagination(list, base_path)
|
data/views/users/index.haml
CHANGED
@@ -1,35 +1,37 @@
|
|
1
1
|
.row
|
2
2
|
.col-md-12
|
3
3
|
= haml :'partials/search'
|
4
|
-
|
5
|
-
%
|
6
|
-
%
|
7
|
-
%th= "Email #{sort_ui(:email)}"
|
8
|
-
%th= "Name #{sort_ui(:name)}"
|
9
|
-
%th= "Surname #{sort_ui(:surname)}"
|
10
|
-
%th Roles
|
11
|
-
%th= "Signed Up #{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
|
-
%
|
8
|
+
%th= "Email #{sort_ui(:email)}"
|
9
|
+
%th= "Name #{sort_ui(:name)}"
|
10
|
+
%th= "Surname #{sort_ui(:surname)}"
|
11
|
+
%th Roles
|
12
|
+
%th= "Signed Up #{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
|
-
|
35
|
+
- if list.count > 0
|
36
|
+
.card-body
|
37
|
+
= pagination(list, base_path)
|
data/views/users/profile.haml
CHANGED
@@ -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')
|
data/views/users/user.haml
CHANGED
@@ -1,4 +1,4 @@
|
|
1
1
|
= form_control(:name, user)
|
2
2
|
= form_control(:surname, user)
|
3
3
|
- if policy(user).permitted_attributes.include? :role_id
|
4
|
-
= form_control(:role_id, user, type: 'select', options: Ditty::Role.order(:name).as_hash(:id, :name), name: 'user[role_id][]', value: user.new? ? [] : user.roles_dataset.map(:id), multiple: true)
|
4
|
+
= form_control(:role_id, user, type: 'select', options: Ditty::Role.select(:id, :name).order(:name).as_hash(:id, :name), name: 'user[role_id][]', value: user.new? ? [] : user.roles_dataset.map(:id), multiple: true)
|
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.
|
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:
|
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: '
|
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: '
|
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
|
@@ -733,7 +739,6 @@ files:
|
|
733
739
|
- views/partials/search.haml
|
734
740
|
- views/partials/sidebar.haml
|
735
741
|
- views/partials/sort_ui.haml
|
736
|
-
- views/partials/timespan_selector.haml
|
737
742
|
- views/partials/topbar.haml
|
738
743
|
- views/partials/user_associations.haml
|
739
744
|
- views/quick_start.haml
|
@@ -1,64 +0,0 @@
|
|
1
|
-
- if defined?(context)
|
2
|
-
- if context.interval
|
3
|
-
%li.nav-item.dropdown.no-arrow
|
4
|
-
%a#intervalDropdown.nav-link.dropdown-toggle{ href: '#', role: 'button', 'aria-haspopup': 'true', 'area-expanded': 'false', 'data-toggle': 'dropdown' }
|
5
|
-
%span.mr-2.d-none.d-lg-inline.text-gray-600
|
6
|
-
Interval /
|
7
|
-
%strong
|
8
|
-
= context.interval
|
9
|
-
.dropdown-menu.dropdown-menu-right.shadow.animated--grow-in{ 'aria-labelledby': 'intervalDropdown' }
|
10
|
-
%a.dropdown-item{ href: "?#{URI.encode_www_form(and_params.merge(interval: '1m'))}", class: context.interval == '1m' ? 'active' : '' }
|
11
|
-
1 Minute
|
12
|
-
%a.dropdown-item{ href: "?#{URI.encode_www_form(and_params.merge(interval: '5m'))}", class: context.interval == '5m' ? 'active' : '' }
|
13
|
-
5 Minutes
|
14
|
-
%a.dropdown-item{ href: "?#{URI.encode_www_form(and_params.merge(interval: '15m'))}", class: context.interval == '15m' ? 'active' : '' }
|
15
|
-
15 Minutes
|
16
|
-
.dropdown-divider
|
17
|
-
%a.dropdown-item{ href: "?#{URI.encode_www_form(and_params.merge(interval: '1h'))}", class: context.interval == '1h' ? 'active' : '' }
|
18
|
-
1 Hour
|
19
|
-
%a.dropdown-item{ href: "?#{URI.encode_www_form(and_params.merge(interval: '3h'))}", class: context.interval == '3h' ? 'active' : '' }
|
20
|
-
3 Hours
|
21
|
-
%a.dropdown-item{ href: "?#{URI.encode_www_form(and_params.merge(interval: '6h'))}", class: context.interval == '6h' ? 'active' : '' }
|
22
|
-
6 Hours
|
23
|
-
.dropdown-divider
|
24
|
-
%a.dropdown-item{ href: "?#{URI.encode_www_form(and_params.merge(interval: '1d'))}", class: context.interval == '1d' ? 'active' : '' }
|
25
|
-
1 Day
|
26
|
-
%a.dropdown-item{ href: "?#{URI.encode_www_form(and_params.merge(interval: '2d'))}", class: context.interval == '2d' ? 'active' : '' }
|
27
|
-
2 Days
|
28
|
-
%a.dropdown-item{ href: "?#{URI.encode_www_form(and_params.merge(interval: '1w'))}", class: context.interval == '1w' ? 'active' : '' }
|
29
|
-
1 Week
|
30
|
-
|
31
|
-
- if context.timespan
|
32
|
-
%li.nav-item.dropdown.no-arrow
|
33
|
-
%a#timespanDropdown.nav-link.dropdown-toggle{ href: '#', role: 'button', 'aria-haspopup': 'true', 'area-expanded': 'false', 'data-toggle': 'dropdown' }
|
34
|
-
%span.mr-2.d-none.d-lg-inline.text-gray-600
|
35
|
-
Timespan /
|
36
|
-
%strong
|
37
|
-
= context.timespan.split('/').first
|
38
|
-
.dropdown-menu.dropdown-menu-right.shadow.animated--grow-in{ 'aria-labelledby': 'timespanDropdown' }
|
39
|
-
%a.dropdown-item{ href: "?#{URI.encode_www_form(and_params.merge(timespan: '1h', interval: ''))}", class: context.timespan == '1h' ? 'active' : '' }
|
40
|
-
%i.fas.fa-chevron-right.fa-sm.fa-fw.mr-2.text-gray-400
|
41
|
-
1 Hour
|
42
|
-
%a.dropdown-item{ href: "?#{URI.encode_www_form(and_params.merge(timespan: '1d', interval: ''))}", class: context.timespan == '1d' ? 'active' : '' }
|
43
|
-
%i.fas.fa-chevron-right.fa-sm.fa-fw.mr-2.text-gray-400
|
44
|
-
1 Day
|
45
|
-
.dropdown-divider
|
46
|
-
%a.dropdown-item{ href: "?#{URI.encode_www_form(and_params.merge(timespan: '1w/d', interval: ''))}", class: context.timespan == '1w/d' ? 'active' : '' }
|
47
|
-
%i.fas.fa-chevron-right.fa-sm.fa-fw.mr-2.text-gray-400
|
48
|
-
1 Week
|
49
|
-
%a.dropdown-item{ href: "?#{URI.encode_www_form(and_params.merge(timespan: '2w/d', interval: ''))}", class: context.timespan == '2w/d' ? 'active' : '' }
|
50
|
-
%i.fas.fa-chevron-right.fa-sm.fa-fw.mr-2.text-gray-400
|
51
|
-
2 Weeks
|
52
|
-
%a.dropdown-item{ href: "?#{URI.encode_www_form(and_params.merge(timespan: '1M/d', interval: ''))}", class: context.timespan == '1M/d' ? 'active' : '' }
|
53
|
-
%i.fas.fa-chevron-right.fa-sm.fa-fw.mr-2.text-gray-400
|
54
|
-
1 Month
|
55
|
-
.dropdown-divider
|
56
|
-
%a.dropdown-item{ href: "?#{URI.encode_www_form(and_params.merge(timespan: '3M/d', interval: ''))}", class: context.timespan == '3M/d' ? 'active' : '' }
|
57
|
-
%i.fas.fa-chevron-right.fa-sm.fa-fw.mr-2.text-gray-400
|
58
|
-
3 Months
|
59
|
-
%a.dropdown-item{ href: "?#{URI.encode_www_form(and_params.merge(timespan: '6M/d', interval: ''))}", class: context.timespan == '6M/d' ? 'active' : '' }
|
60
|
-
%i.fas.fa-chevron-right.fa-sm.fa-fw.mr-2.text-gray-400
|
61
|
-
6 Months
|
62
|
-
%a.dropdown-item{ href: "?#{URI.encode_www_form(and_params.merge(timespan: '1y/d', interval: ''))}", class: context.timespan == '1y/d' ? 'active' : '' }
|
63
|
-
%i.fas.fa-chevron-right.fa-sm.fa-fw.mr-2.text-gray-400
|
64
|
-
1 Year
|