typus 0.9.17

Sign up to get free protection for your applications and to get access to all the features.
Files changed (154) hide show
  1. data/.gitignore +8 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +86 -0
  4. data/Rakefile +61 -0
  5. data/VERSION +1 -0
  6. data/app/controllers/admin/master_controller.rb +354 -0
  7. data/app/controllers/typus_controller.rb +128 -0
  8. data/app/helpers/admin/form_helper.rb +386 -0
  9. data/app/helpers/admin/master_helper.rb +104 -0
  10. data/app/helpers/admin/public_helper.rb +27 -0
  11. data/app/helpers/admin/sidebar_helper.rb +236 -0
  12. data/app/helpers/admin/table_helper.rb +227 -0
  13. data/app/helpers/typus_helper.rb +194 -0
  14. data/app/models/typus_mailer.rb +14 -0
  15. data/app/models/typus_user.rb +5 -0
  16. data/app/views/admin/dashboard/_sidebar.html.erb +9 -0
  17. data/app/views/admin/resources/edit.html.erb +24 -0
  18. data/app/views/admin/resources/index.html.erb +23 -0
  19. data/app/views/admin/resources/new.html.erb +22 -0
  20. data/app/views/admin/resources/show.html.erb +18 -0
  21. data/app/views/admin/shared/_footer.html.erb +1 -0
  22. data/app/views/admin/shared/_pagination.html.erb +28 -0
  23. data/app/views/layouts/admin.html.erb +73 -0
  24. data/app/views/layouts/typus.html.erb +29 -0
  25. data/app/views/typus/dashboard.html.erb +9 -0
  26. data/app/views/typus/recover_password.html.erb +7 -0
  27. data/app/views/typus/reset_password.html.erb +15 -0
  28. data/app/views/typus/sign_in.html.erb +9 -0
  29. data/app/views/typus/sign_up.html.erb +7 -0
  30. data/app/views/typus_mailer/reset_password_link.erb +11 -0
  31. data/config/locales/typus/de.yml +109 -0
  32. data/config/locales/typus/es.yml +109 -0
  33. data/config/locales/typus/language.yml.template +113 -0
  34. data/config/locales/typus/pt-BR.yml +111 -0
  35. data/config/locales/typus/ru.yml +111 -0
  36. data/generators/typus/templates/config/initializers/typus.rb +33 -0
  37. data/generators/typus/templates/config/typus/README +51 -0
  38. data/generators/typus/templates/config/typus/application.yml +6 -0
  39. data/generators/typus/templates/config/typus/application_roles.yml +23 -0
  40. data/generators/typus/templates/config/typus/typus.yml +14 -0
  41. data/generators/typus/templates/config/typus/typus_roles.yml +2 -0
  42. data/generators/typus/templates/db/create_typus_users.rb +21 -0
  43. data/generators/typus/templates/public/images/admin/arrow_down.gif +0 -0
  44. data/generators/typus/templates/public/images/admin/arrow_up.gif +0 -0
  45. data/generators/typus/templates/public/images/admin/spinner.gif +0 -0
  46. data/generators/typus/templates/public/images/admin/status_false.gif +0 -0
  47. data/generators/typus/templates/public/images/admin/status_true.gif +0 -0
  48. data/generators/typus/templates/public/images/admin/trash.gif +0 -0
  49. data/generators/typus/templates/public/javascripts/admin/application.js +14 -0
  50. data/generators/typus/templates/public/stylesheets/admin/reset.css +68 -0
  51. data/generators/typus/templates/public/stylesheets/admin/screen.css +729 -0
  52. data/generators/typus/typus_generator.rb +122 -0
  53. data/generators/typus_update_schema_to_01/templates/config/typus.yml +14 -0
  54. data/generators/typus_update_schema_to_01/templates/migration.rb +11 -0
  55. data/generators/typus_update_schema_to_01/typus_update_schema_to_01_generator.rb +19 -0
  56. data/lib/typus.rb +122 -0
  57. data/lib/typus/active_record.rb +307 -0
  58. data/lib/typus/authentication.rb +142 -0
  59. data/lib/typus/configuration.rb +85 -0
  60. data/lib/typus/extensions/routes.rb +15 -0
  61. data/lib/typus/format.rb +55 -0
  62. data/lib/typus/generator.rb +81 -0
  63. data/lib/typus/hash.rb +8 -0
  64. data/lib/typus/locale.rb +17 -0
  65. data/lib/typus/object.rb +21 -0
  66. data/lib/typus/quick_edit.rb +40 -0
  67. data/lib/typus/reloader.rb +15 -0
  68. data/lib/typus/string.rb +11 -0
  69. data/lib/typus/templates/index.html.erb +11 -0
  70. data/lib/typus/templates/resource_controller.rb.erb +15 -0
  71. data/lib/typus/templates/resource_controller_test.rb.erb +10 -0
  72. data/lib/typus/templates/resources_controller.rb.erb +37 -0
  73. data/lib/typus/user.rb +134 -0
  74. data/lib/vendor/active_record.rb +15 -0
  75. data/lib/vendor/paginator.rb +143 -0
  76. data/rails/init.rb +3 -0
  77. data/tasks/typus_tasks.rake +32 -0
  78. data/test/config/broken/application.yml +68 -0
  79. data/test/config/broken/application_roles.yml +20 -0
  80. data/test/config/broken/empty.yml +0 -0
  81. data/test/config/broken/empty_roles.yml +0 -0
  82. data/test/config/broken/undefined.yml +3 -0
  83. data/test/config/broken/undefined_roles.yml +6 -0
  84. data/test/config/default/typus.yml +14 -0
  85. data/test/config/default/typus_roles.yml +2 -0
  86. data/test/config/empty/empty_01.yml +0 -0
  87. data/test/config/empty/empty_01_roles.yml +0 -0
  88. data/test/config/empty/empty_02.yml +0 -0
  89. data/test/config/empty/empty_02_roles.yml +0 -0
  90. data/test/config/locales/es.yml +10 -0
  91. data/test/config/ordered/001_roles.yml +2 -0
  92. data/test/config/ordered/002_roles.yml +2 -0
  93. data/test/config/unordered/app_one_roles.yml +2 -0
  94. data/test/config/unordered/app_two_roles.yml +2 -0
  95. data/test/config/working/application.yml +68 -0
  96. data/test/config/working/application_roles.yml +22 -0
  97. data/test/config/working/typus.yml +14 -0
  98. data/test/config/working/typus_roles.yml +2 -0
  99. data/test/fixtures/app/controllers/admin/assets_controller.rb +2 -0
  100. data/test/fixtures/app/controllers/admin/categories_controller.rb +2 -0
  101. data/test/fixtures/app/controllers/admin/comments_controller.rb +2 -0
  102. data/test/fixtures/app/controllers/admin/pages_controller.rb +2 -0
  103. data/test/fixtures/app/controllers/admin/posts_controller.rb +2 -0
  104. data/test/fixtures/app/controllers/admin/status_controller.rb +6 -0
  105. data/test/fixtures/app/controllers/admin/typus_users_controller.rb +2 -0
  106. data/test/fixtures/app/controllers/admin/watch_dog_controller.rb +6 -0
  107. data/test/fixtures/app/views/admin/comments/_edit.html.erb +1 -0
  108. data/test/fixtures/app/views/admin/comments/_index.html.erb +1 -0
  109. data/test/fixtures/app/views/admin/comments/_new.html.erb +1 -0
  110. data/test/fixtures/app/views/admin/comments/_show.html.erb +1 -0
  111. data/test/fixtures/app/views/admin/comments/_sidebar.html.erb +1 -0
  112. data/test/fixtures/app/views/admin/dashboard/_content.html.erb +1 -0
  113. data/test/fixtures/app/views/admin/dashboard/_sidebar.html.erb +1 -0
  114. data/test/fixtures/app/views/admin/resources/_sidebar.html.erb +1 -0
  115. data/test/fixtures/app/views/admin/shared/_footer.html.erb +1 -0
  116. data/test/fixtures/app/views/admin/status/index.html.erb +1 -0
  117. data/test/fixtures/app/views/admin/templates/_datepicker.html.erb +1 -0
  118. data/test/fixtures/assets.yml +11 -0
  119. data/test/fixtures/categories.yml +14 -0
  120. data/test/fixtures/comments.yml +27 -0
  121. data/test/fixtures/pages.yml +41 -0
  122. data/test/fixtures/posts.yml +37 -0
  123. data/test/fixtures/typus_users.yml +54 -0
  124. data/test/functional/admin/assets_controller_test.rb +57 -0
  125. data/test/functional/admin/categories_controller_test.rb +106 -0
  126. data/test/functional/admin/comments_controller_test.rb +120 -0
  127. data/test/functional/admin/master_controller_test.rb +5 -0
  128. data/test/functional/admin/posts_controller_test.rb +261 -0
  129. data/test/functional/admin/status_controller_test.rb +43 -0
  130. data/test/functional/admin/typus_users_controller_test.rb +239 -0
  131. data/test/functional/typus_controller_test.rb +321 -0
  132. data/test/helper.rb +51 -0
  133. data/test/helpers/admin/form_helper_test.rb +337 -0
  134. data/test/helpers/admin/master_helper_test.rb +69 -0
  135. data/test/helpers/admin/public_helper_test.rb +26 -0
  136. data/test/helpers/admin/sidebar_helper_test.rb +335 -0
  137. data/test/helpers/admin/table_helper_test.rb +239 -0
  138. data/test/helpers/typus_helper_test.rb +117 -0
  139. data/test/lib/active_record_test.rb +382 -0
  140. data/test/lib/configuration_test.rb +94 -0
  141. data/test/lib/hash_test.rb +11 -0
  142. data/test/lib/routes_test.rb +71 -0
  143. data/test/lib/string_test.rb +25 -0
  144. data/test/lib/typus_test.rb +85 -0
  145. data/test/models.rb +51 -0
  146. data/test/schema.rb +64 -0
  147. data/test/unit/typus_mailer_test.rb +33 -0
  148. data/test/unit/typus_test.rb +17 -0
  149. data/test/unit/typus_user_roles_test.rb +90 -0
  150. data/test/unit/typus_user_test.rb +177 -0
  151. data/test/vendor/active_record_test.rb +18 -0
  152. data/test/vendor/paginator_test.rb +136 -0
  153. data/typus.gemspec +228 -0
  154. metadata +241 -0
@@ -0,0 +1,15 @@
1
+ module Typus
2
+
3
+ module Reloader
4
+
5
+ # Reload config and roles when app is running in development.
6
+ def reload_config_et_roles
7
+ return if Rails.env.production?
8
+ logger.info "=> [typus] Configuration files have been reloaded."
9
+ Typus::Configuration.roles!
10
+ Typus::Configuration.config!
11
+ end
12
+
13
+ end
14
+
15
+ end
@@ -0,0 +1,11 @@
1
+ class String
2
+
3
+ def typus_actions_for(filter)
4
+ Typus::Configuration.config[self]['actions'][filter.to_s].split(', ') rescue []
5
+ end
6
+
7
+ def typus_defaults_for(filter)
8
+ Typus::Configuration.config[self][filter.to_s].split(', ') rescue []
9
+ end
10
+
11
+ end
@@ -0,0 +1,11 @@
1
+ <!-- Sidebar -->
2
+
3
+ <% content_for :sidebar do %>
4
+ <%= typus_block :location => 'dashboard', :partial => 'sidebar' %>
5
+ <% end %>
6
+
7
+ <!-- Content -->
8
+
9
+ <h2>Resource#index</h2>
10
+
11
+ <p>Find me in app/views/<%= params[:controller] %>/index.html.erb</p>
@@ -0,0 +1,15 @@
1
+ # Controller generated by Typus, use it to extend admin functionality.
2
+ class Admin::<%= resource %>Controller < TypusController
3
+
4
+ ##
5
+ # This controller was generated because you have defined a resource
6
+ # on <tt>config/typus/XXXXXX_roles.yml</tt> which is a tableless model.
7
+ #
8
+ # admin:
9
+ # <%= resource %>: index
10
+ #
11
+
12
+ def index
13
+ end
14
+
15
+ end
@@ -0,0 +1,10 @@
1
+ require 'test_helper'
2
+
3
+ class Admin::<%= model.pluralize %>ControllerTest < ActionController::TestCase
4
+
5
+ # Replace this with your real tests.
6
+ test "the truth" do
7
+ assert true
8
+ end
9
+
10
+ end
@@ -0,0 +1,37 @@
1
+ # Controller generated by Typus, use it to extend admin functionality.
2
+ class Admin::<%= model.pluralize %>Controller < Admin::MasterController
3
+
4
+ =begin
5
+
6
+ ##
7
+ # You can overwrite and extend Admin::MasterController with your methods.
8
+ #
9
+ # Actions have to be defined in <tt>config/typus/application.yml</tt>:
10
+ #
11
+ # <%= model %>:
12
+ # actions:
13
+ # index: custom_action
14
+ # edit: custom_action_for_an_item
15
+ #
16
+ # And you have to add permissions on <tt>config/typus/application_roles.yml</tt>
17
+ # to have access to them.
18
+ #
19
+ # admin:
20
+ # <%= model %>: create, read, update, destroy, custom_action
21
+ #
22
+ # editor:
23
+ # <%= model %>: create, read, update, custom_action_for_an_item
24
+ #
25
+
26
+ def index
27
+ end
28
+
29
+ def custom_action
30
+ end
31
+
32
+ def custom_action_for_an_item
33
+ end
34
+
35
+ =end
36
+
37
+ end
data/lib/typus/user.rb ADDED
@@ -0,0 +1,134 @@
1
+ module Typus
2
+
3
+ module EnableAsTypusUser
4
+
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ end
8
+
9
+ module ClassMethods
10
+
11
+ def enable_as_typus_user
12
+
13
+ extend ClassMethodsMixin
14
+
15
+ attr_accessor :password
16
+
17
+ validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/
18
+ validates_presence_of :email
19
+ validates_uniqueness_of :email
20
+
21
+ validates_confirmation_of :password, :if => :password_required?
22
+ validates_length_of :password, :within => 8..40, :if => :password_required?
23
+ validates_presence_of :password, :if => :password_required?
24
+
25
+ validates_presence_of :role
26
+
27
+ before_save :initialize_salt, :encrypt_password, :initialize_token
28
+
29
+ include InstanceMethods
30
+
31
+ end
32
+
33
+ end
34
+
35
+ module ClassMethodsMixin
36
+
37
+ def role
38
+ Typus::Configuration.roles.keys.sort
39
+ end
40
+
41
+ def authenticate(email, password)
42
+ user = find_by_email_and_status(email, true)
43
+ user && user.authenticated?(password) ? user : nil
44
+ end
45
+
46
+ def generate(email, password, role = Typus::Configuration.options[:root], status = true)
47
+ new :email => email,
48
+ :password => password,
49
+ :password_confirmation => password,
50
+ :role => role,
51
+ :status => status
52
+ end
53
+
54
+ end
55
+
56
+ module InstanceMethods
57
+
58
+ def name
59
+ (!first_name.empty? && !last_name.empty?) ? "#{first_name} #{last_name}" : email
60
+ end
61
+
62
+ def authenticated?(password)
63
+ crypted_password == encrypt(password)
64
+ end
65
+
66
+ def resources
67
+ Typus::Configuration.roles[role].compact
68
+ end
69
+
70
+ def can_perform?(resource, action, options = {})
71
+
72
+ if options[:special]
73
+ _action = action
74
+ else
75
+ _action = case action
76
+ when 'new', 'create' then 'create'
77
+ when 'index', 'show' then 'read'
78
+ when 'edit', 'update' then 'update'
79
+ when 'position' then 'update'
80
+ when 'toggle' then 'update'
81
+ when 'relate', 'unrelate' then 'update'
82
+ when 'destroy' then 'delete'
83
+ else
84
+ action
85
+ end
86
+ end
87
+
88
+ # OPTIMIZE: We should not use a rescue.
89
+ resources[resource.to_s].split(', ').include?(_action) rescue false
90
+
91
+ end
92
+
93
+ def is_root?
94
+ role == Typus::Configuration.options[:root]
95
+ end
96
+
97
+ protected
98
+
99
+ def generate_hash(string)
100
+ Digest::SHA1.hexdigest(string)
101
+ end
102
+
103
+ def encrypt_password
104
+ return if password.blank?
105
+ self.crypted_password = encrypt(password)
106
+ end
107
+
108
+ def encrypt(string)
109
+ generate_hash("--#{salt}--#{string}")
110
+ end
111
+
112
+ def initialize_salt
113
+ self.salt = generate_hash("--#{Time.now.utc.to_s}--#{email}--") if new_record?
114
+ end
115
+
116
+ def initialize_token
117
+ generate_token if new_record?
118
+ end
119
+
120
+ def generate_token
121
+ self.token = encrypt("--#{Time.now.utc.to_s}--#{password}--").first(12)
122
+ end
123
+
124
+ def password_required?
125
+ crypted_password.blank? || !password.blank?
126
+ end
127
+
128
+ end
129
+
130
+ end
131
+
132
+ end
133
+
134
+ ActiveRecord::Base.send :include, Typus::EnableAsTypusUser
@@ -0,0 +1,15 @@
1
+ class ActiveRecord::Base
2
+
3
+ def to_dom(*args)
4
+
5
+ options = args.extract_options!
6
+ display_id = new_record? ? 'new' : id
7
+
8
+ [ options[:prefix],
9
+ self.class.name.underscore,
10
+ display_id,
11
+ options[:suffix] ].compact.join('_')
12
+
13
+ end
14
+
15
+ end
@@ -0,0 +1,143 @@
1
+ require 'forwardable'
2
+
3
+ class Paginator
4
+
5
+ VERSION = '1.1.1'
6
+
7
+ include Enumerable
8
+
9
+ class ArgumentError < ::ArgumentError; end
10
+ class MissingCountError < ArgumentError; end
11
+ class MissingSelectError < ArgumentError; end
12
+
13
+ attr_reader :per_page, :count
14
+
15
+ # Instantiate a new Paginator object
16
+ #
17
+ # Provide:
18
+ # * A total count of the number of objects to paginate
19
+ # * The number of objects in each page
20
+ # * A block that returns the array of items
21
+ # * The block is passed the item offset
22
+ # (and the number of items to show per page, for
23
+ # convenience, if the arity is 2)
24
+ def initialize(count, per_page, &select)
25
+ @count, @per_page = count, per_page
26
+ unless select
27
+ raise MissingSelectError, "Must provide block to select data for each page"
28
+ end
29
+ @select = select
30
+ end
31
+
32
+ # Total number of pages
33
+ def number_of_pages
34
+ (@count / @per_page).to_i + (@count % @per_page > 0 ? 1 : 0)
35
+ end
36
+
37
+ # First page object
38
+ def first
39
+ page 1
40
+ end
41
+
42
+ # Last page object
43
+ def last
44
+ page number_of_pages
45
+ end
46
+
47
+ def each
48
+ 1.upto(number_of_pages) do |number|
49
+ yield page(number)
50
+ end
51
+ end
52
+
53
+ # Retrieve page object by number
54
+ def page(number)
55
+ number = (n = number.to_i) > 0 ? n : 1
56
+ Page.new(self, number, lambda {
57
+ offset = (number - 1) * @per_page
58
+ args = [offset]
59
+ args << @per_page if @select.arity == 2
60
+ @select.call(*args)
61
+ })
62
+ end
63
+
64
+ # Page object
65
+ #
66
+ # Retrieves items for a page and provides metadata about the position
67
+ # of the page in the paginator
68
+ class Page
69
+
70
+ include Enumerable
71
+
72
+ attr_reader :number, :pager
73
+
74
+ def initialize(pager, number, select) #:nodoc:
75
+ @pager, @number = pager, number
76
+ @offset = (number - 1) * pager.per_page
77
+ @select = select
78
+ end
79
+
80
+ # Retrieve the items for this page
81
+ # * Caches
82
+ def items
83
+ @items ||= @select.call
84
+ end
85
+
86
+ # Does this page have any items?
87
+ def empty?
88
+ items.empty?
89
+ end
90
+
91
+ # Checks to see if there's a page before this one
92
+ def prev?
93
+ @number > 1
94
+ end
95
+
96
+ # Get previous page (if possible)
97
+ def prev
98
+ @pager.page(@number - 1) if prev?
99
+ end
100
+
101
+ # Checks to see if there's a page after this one
102
+ def next?
103
+ @number < @pager.number_of_pages
104
+ end
105
+
106
+ # Get next page (if possible)
107
+ def next
108
+ @pager.page(@number + 1) if next?
109
+ end
110
+
111
+ # The "item number" of the first item on this page
112
+ def first_item_number
113
+ 1 + @offset
114
+ end
115
+
116
+ # The "item number" of the last item on this page
117
+ def last_item_number
118
+ if next?
119
+ @offset + @pager.per_page
120
+ else
121
+ @pager.count
122
+ end
123
+ end
124
+
125
+ def ==(other) #:nodoc:
126
+ @pager == other.pager && self.number == other.number
127
+ end
128
+
129
+ def each(&block)
130
+ items.each(&block)
131
+ end
132
+
133
+ def method_missing(meth, *args, &block) #:nodoc:
134
+ if @pager.respond_to?(meth)
135
+ @pager.__send__(meth, *args, &block)
136
+ else
137
+ super
138
+ end
139
+ end
140
+
141
+ end
142
+
143
+ end
data/rails/init.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'typus'
2
+
3
+ Typus.boot!
@@ -0,0 +1,32 @@
1
+ namespace :typus do
2
+
3
+ desc 'Install acts_as_list, acts_as_tree and paperclip.'
4
+ task :misc do
5
+ plugins = [ 'git://github.com/thoughtbot/paperclip.git',
6
+ 'git://github.com/rails/acts_as_list.git',
7
+ 'git://github.com/rails/acts_as_tree.git' ]
8
+ system "script/plugin install #{plugins.join(' ')} --force"
9
+ end
10
+
11
+ desc 'Install ssl_requirement.'
12
+ task :ssl do
13
+ system "script/plugin install git://github.com/rails/ssl_requirement.git --force"
14
+ end
15
+
16
+ desc 'Install tiny_mce.'
17
+ task :tiny_mce do
18
+ system "script/plugin install git://github.com/kete/tiny_mce.git --force"
19
+ load File.join Rails.root, 'vendor', 'plugins', 'tiny_mce', 'tasks', 'tiny_mce.rake'
20
+ Rake::Task["tiny_mce:install"].invoke
21
+ end
22
+
23
+ desc 'List current roles.'
24
+ task :roles => :environment do
25
+ Typus::Configuration.roles.each do |role|
26
+ puts "\n#{role.first.capitalize} role has access to:"
27
+ role.last.each { |key, value| puts "- #{key}: #{value}" }
28
+ end
29
+ puts "\n"
30
+ end
31
+
32
+ end
@@ -0,0 +1,68 @@
1
+ Page:
2
+ fields:
3
+ # Body field is overwrited from a Page class method.
4
+ list: title, body, status
5
+ form: title, body, status
6
+ relationships:
7
+ has_many: assets
8
+ application: Site
9
+
10
+ Asset:
11
+ fields:
12
+ list: caption
13
+ form: caption
14
+ application: Site
15
+
16
+ Category:
17
+ fields:
18
+ list: name, position
19
+ form: name, permalink, position
20
+ order_by: position
21
+ module: Post
22
+
23
+ Post:
24
+ fields:
25
+ list: title, created_at, status
26
+ form: title, body, created_at, status
27
+ relationship: title, created_at
28
+ options:
29
+ selectors: status
30
+ read_only: permalink
31
+ auto_generated: created_at
32
+ date_formats:
33
+ created_at: post_short
34
+ relationships: assets, categories
35
+ actions:
36
+ index: cleanup
37
+ edit: send_as_newsletter, preview
38
+ filters: status, created_at, user, user_id
39
+ search: title
40
+ order_by: title, -created_at
41
+ application: Blog
42
+
43
+ User:
44
+ fields:
45
+ list: first_name, last_name, email
46
+
47
+ Comment:
48
+ fields:
49
+ list: email, post, post_id
50
+ form: email, post
51
+ filters: post
52
+ search: email, body
53
+ application: Blog
54
+
55
+ TypusUser:
56
+ fields:
57
+ list: first_name, last_name, email, role, status
58
+ form: first_name, last_name, email, role, password, password_confirmation
59
+ relationship: first_name, last_name, role, email, status
60
+ options:
61
+ selectors: role
62
+ booleans:
63
+ status: active, inactive
64
+ filters: status, role, unexisting
65
+ search: first_name, last_name, email, role
66
+ module: Typus
67
+ description: System Users Administration
68
+ application: Typus