padrino-admin 0.6.3 → 0.6.7
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +16 -0
- data/VERSION +1 -1
- data/lib/padrino-admin.rb +24 -5
- data/lib/padrino-admin/access_control.rb +75 -29
- data/lib/padrino-admin/{ext_js/column_store.rb → column_store.rb} +31 -15
- data/lib/padrino-admin/config.rb +36 -0
- data/lib/padrino-admin/generators/actions.rb +62 -7
- data/lib/padrino-admin/generators/admin_app.rb +27 -26
- data/lib/padrino-admin/generators/admin_page.rb +20 -18
- data/lib/padrino-admin/generators/admin_uploader.rb +83 -0
- data/lib/padrino-admin/generators/app/app.rb.tt +1 -3
- data/lib/padrino-admin/generators/app/controllers/accounts.rb +1 -1
- data/lib/padrino-admin/generators/app/public/flash/swfupload.swf +0 -0
- data/lib/padrino-admin/generators/app/public/images/{backend → admin}/back.gif +0 -0
- data/lib/padrino-admin/generators/app/public/images/{backend → admin}/background.png +0 -0
- data/lib/padrino-admin/generators/app/public/images/{backend → admin}/bg-content.png +0 -0
- data/lib/padrino-admin/generators/app/public/images/{backend → admin}/bg-hd-slate.png +0 -0
- data/lib/padrino-admin/generators/app/public/images/{backend → admin}/bg-hd.png +0 -0
- data/lib/padrino-admin/generators/app/public/images/{backend → admin}/bg-intro.png +0 -0
- data/lib/padrino-admin/generators/app/public/images/{backend → admin}/bg-login.png +0 -0
- data/lib/padrino-admin/generators/app/public/images/{backend → admin}/bg-menu-slate.png +0 -0
- data/lib/padrino-admin/generators/app/public/images/{backend → admin}/bg-menu.png +0 -0
- data/lib/padrino-admin/generators/app/public/images/{backend → admin}/bg.png +0 -0
- data/lib/padrino-admin/generators/app/public/images/{backend → admin}/btn-login.png +0 -0
- data/lib/padrino-admin/generators/app/public/images/admin/cancel.gif +0 -0
- data/lib/padrino-admin/generators/app/public/images/{backend → admin}/categories.gif +0 -0
- data/lib/padrino-admin/generators/app/public/images/admin/close.gif +0 -0
- data/lib/padrino-admin/generators/app/public/images/admin/close.png +0 -0
- data/lib/padrino-admin/generators/app/public/images/{backend → admin}/delete.gif +0 -0
- data/lib/padrino-admin/generators/app/public/images/{backend → admin}/download.gif +0 -0
- data/lib/padrino-admin/generators/app/public/images/{backend → admin}/duplicate.gif +0 -0
- data/lib/padrino-admin/generators/app/public/images/{backend → admin}/edit.gif +0 -0
- data/lib/padrino-admin/generators/app/public/images/{backend → admin}/export.gif +0 -0
- data/lib/padrino-admin/generators/app/public/images/{backend → admin}/hd-bg.gif +0 -0
- data/lib/padrino-admin/generators/app/public/images/{backend → admin}/image.gif +0 -0
- data/lib/padrino-admin/generators/app/public/images/{backend → admin}/loader.gif +0 -0
- data/lib/padrino-admin/generators/app/public/images/{backend → admin}/logo-loader.png +0 -0
- data/lib/padrino-admin/generators/app/public/images/{backend → admin}/logo-small.png +0 -0
- data/lib/padrino-admin/generators/app/public/images/{backend → admin}/new.gif +0 -0
- data/lib/padrino-admin/generators/app/public/images/{backend → admin}/no-image.png +0 -0
- data/lib/padrino-admin/generators/app/public/images/{backend → admin}/preview.gif +0 -0
- data/lib/padrino-admin/generators/app/public/images/{backend → admin}/print.gif +0 -0
- data/lib/padrino-admin/generators/app/public/images/{backend → admin}/save.gif +0 -0
- data/lib/padrino-admin/generators/app/public/images/{backend → admin}/support.gif +0 -0
- data/lib/padrino-admin/generators/app/public/images/admin/up.gif +0 -0
- data/lib/padrino-admin/generators/app/public/javascripts/ext.js +4 -1
- data/lib/padrino-admin/generators/app/public/javascripts/swfupload.js +4 -0
- data/lib/padrino-admin/generators/app/public/stylesheets/admin.css +25 -43
- data/lib/padrino-admin/generators/app/public/stylesheets/login.css +3 -3
- data/lib/padrino-admin/generators/app/public/stylesheets/standard.css +53 -52
- data/lib/padrino-admin/generators/app/views/accounts/_form.haml +1 -0
- data/lib/padrino-admin/generators/app/views/base/index.haml +5 -7
- data/lib/padrino-admin/generators/app/views/javascripts/admin.js.erb +217 -297
- data/lib/padrino-admin/generators/app/views/sessions/new.haml +1 -1
- data/lib/padrino-admin/generators/templates/{controller.rb.tt → page/controller.rb.tt} +1 -1
- data/lib/padrino-admin/generators/templates/{db → page/db}/seeds.rb.tt +0 -0
- data/lib/padrino-admin/generators/templates/{views → page/views}/_form.haml.tt +0 -0
- data/lib/padrino-admin/generators/templates/{views → page/views}/edit.haml.tt +0 -0
- data/lib/padrino-admin/generators/templates/{views → page/views}/grid.js.erb.tt +1 -1
- data/lib/padrino-admin/generators/templates/{views → page/views}/new.haml.tt +0 -0
- data/lib/padrino-admin/generators/templates/{views → page/views}/store.jml.tt +0 -0
- data/lib/padrino-admin/generators/templates/uploader/controller.rb +24 -0
- data/lib/padrino-admin/generators/templates/uploader/lib/uploader.rb +54 -0
- data/lib/padrino-admin/generators/templates/uploader/views/grid.js.erb +56 -0
- data/lib/padrino-admin/generators/templates/uploader/views/store.jml +10 -0
- data/lib/padrino-admin/helpers/authentication.rb +30 -19
- data/lib/padrino-admin/helpers/view.rb +172 -35
- data/lib/padrino-admin/locale/admin/en.yml +1 -0
- data/lib/padrino-admin/middleware/flash_middleware.rb +36 -0
- data/lib/padrino-admin/orm.rb +33 -0
- data/lib/padrino-admin/orm/abstract.rb +94 -0
- data/lib/padrino-admin/{adapters/ar.rb → orm/activerecord.rb} +69 -36
- data/lib/padrino-admin/orm/datamapper.rb +214 -0
- data/lib/padrino-admin/{adapters/mm.rb → orm/mongomapper.rb} +36 -20
- data/lib/padrino-admin/utils/literal.rb +1 -1
- data/padrino-admin.gemspec +62 -51
- data/test/fixtures/active_record.rb +14 -2
- data/test/fixtures/data_mapper.rb +2 -1
- data/test/fixtures/mongo_mapper.rb +1 -1
- data/test/fixtures/test_column_store.jml +1 -0
- data/test/helper.rb +3 -2
- data/test/test_access_control.rb +1 -1
- data/test/test_active_record.rb +56 -1
- data/test/test_column_store.rb +56 -10
- data/test/test_data_mapper.rb +67 -1
- metadata +58 -47
- data/lib/padrino-admin/adapters.rb +0 -108
- data/lib/padrino-admin/adapters/dm.rb +0 -147
- data/lib/padrino-admin/ext_js/config.rb +0 -186
- data/test/fixtures/test_generic.jml +0 -7
- data/test/fixtures/test_javascript.jml +0 -81
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'rack/utils'
|
2
|
+
|
3
|
+
module Padrino
|
4
|
+
module Admin
|
5
|
+
module Middleware
|
6
|
+
##
|
7
|
+
# FlashSessionCookieMiddleware
|
8
|
+
# passing your session in the URI, when it should be in the cookie
|
9
|
+
#
|
10
|
+
# This code only works in following cases:
|
11
|
+
# - passing the session as the variable for 'session_key' (it is best to set this to your app's session cookie name
|
12
|
+
# - the value is URI escaped once, (don't do it explicetally if using rails helpers, they do it for you)
|
13
|
+
# - Loading this middleware before session_store middleware
|
14
|
+
#
|
15
|
+
# Note, this could work also after session_store middleware (or others).
|
16
|
+
# However, these could already have modified the cooky values, and this module
|
17
|
+
# could become unstable because of that, and it's functioning can not be
|
18
|
+
# guaranteed.
|
19
|
+
#
|
20
|
+
class FlashMiddleware
|
21
|
+
def initialize(app, session_key = 'session_id')
|
22
|
+
@app = app
|
23
|
+
@session_key = session_key.to_s
|
24
|
+
end
|
25
|
+
|
26
|
+
def call(env)
|
27
|
+
if env['HTTP_USER_AGENT'] =~ /^(Adobe|Shockwave) Flash/
|
28
|
+
params = ::Rack::Request.new(env).params
|
29
|
+
env['rack.session'][@session_key.to_sym] = params[@session_key] unless params[@session_key].nil?
|
30
|
+
end
|
31
|
+
@app.call(env)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Padrino
|
2
|
+
module Admin
|
3
|
+
|
4
|
+
module Orm
|
5
|
+
|
6
|
+
class ExtSearch < Struct.new(:count, :records); end
|
7
|
+
|
8
|
+
##
|
9
|
+
# Method used for register the orm extensions.
|
10
|
+
#
|
11
|
+
def self.register!
|
12
|
+
::DataMapper::Model.append_inclusions(Padrino::Admin::Orm::DataMapper::Base) if defined?(::DataMapper)
|
13
|
+
::ActiveRecord::Base.send(:include, Padrino::Admin::Orm::ActiveRecord::Base) if defined?(::ActiveRecord)
|
14
|
+
::MongoMapper::Document.append_inclusions(Padrino::Admin::Orm::MongoMapper::Base) if defined?(::MongoMapper)
|
15
|
+
# Extend also account model
|
16
|
+
self.extend_account!
|
17
|
+
end
|
18
|
+
|
19
|
+
##
|
20
|
+
# This method it's used for extend Account Model (if present)
|
21
|
+
#
|
22
|
+
def self.extend_account!
|
23
|
+
if defined?(Account) && Account.respond_to?(:orm)
|
24
|
+
case Account.orm
|
25
|
+
when :activerecord then Account.send(:include, Padrino::Admin::Orm::ActiveRecord::Account)
|
26
|
+
when :datamapper then Account.send(:include, Padrino::Admin::Orm::DataMapper::Account)
|
27
|
+
when :mongomapper then Account.send(:include, Padrino::Admin::Orm::MongoMapper::Account)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end # Orm
|
32
|
+
end # Admin
|
33
|
+
end # Padrino
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Padrino
|
2
|
+
module Admin
|
3
|
+
class OrmError < StandardError; end
|
4
|
+
|
5
|
+
module Orm
|
6
|
+
module Abstract
|
7
|
+
##
|
8
|
+
# In this module we have shared function that are shared
|
9
|
+
# with all orm.
|
10
|
+
#
|
11
|
+
module Base
|
12
|
+
def self.included(base) #:nodoc:
|
13
|
+
base.send :include, InstanceMethods
|
14
|
+
base.extend ClassMethods
|
15
|
+
end
|
16
|
+
|
17
|
+
module InstanceMethods
|
18
|
+
end
|
19
|
+
|
20
|
+
module ClassMethods
|
21
|
+
##
|
22
|
+
# This method generate store and column config.
|
23
|
+
# for lazinies hands instead supply:
|
24
|
+
#
|
25
|
+
# Model.column_store("./../views/model/store.jml")
|
26
|
+
#
|
27
|
+
# you can:
|
28
|
+
#
|
29
|
+
# Model.column_store(options.views, "models/store")
|
30
|
+
def column_store(*args)
|
31
|
+
path = File.join(*args)
|
32
|
+
path = Dir[path + ".{jml,jaml}"].first.to_s if path !~ /(\.jml|\.jaml)$/
|
33
|
+
config = YAML.load_file(path)
|
34
|
+
Padrino::Admin::ColumnStore.new(self, config)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end # Abstract
|
38
|
+
|
39
|
+
##
|
40
|
+
# Accoun Extensions
|
41
|
+
#
|
42
|
+
# An Account must have these column/fields:
|
43
|
+
#
|
44
|
+
# role:: String
|
45
|
+
# email:: String (used as login)
|
46
|
+
# crypted_password:: String
|
47
|
+
# salt:: String
|
48
|
+
#
|
49
|
+
# We add to Account model some methods:
|
50
|
+
#
|
51
|
+
# Account.authenticate(email, password):: check if exist an account for the given email and return it if password match
|
52
|
+
# account.password_clean:: return the decrypted password of an Account instance
|
53
|
+
#
|
54
|
+
module Account
|
55
|
+
|
56
|
+
def self.included(base) #:nodoc:
|
57
|
+
base.extend ClassMethods
|
58
|
+
base.send :include, InstanceMethods
|
59
|
+
end
|
60
|
+
|
61
|
+
module ClassMethods
|
62
|
+
##
|
63
|
+
# This method it's for authentication purpose
|
64
|
+
#
|
65
|
+
def authenticate(email, password)
|
66
|
+
account = first(:conditions => { :email => email }) if email.present?
|
67
|
+
account && account.password_clean == password ? account : nil
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
module InstanceMethods
|
72
|
+
##
|
73
|
+
# This method it's used for retrive the original password.
|
74
|
+
#
|
75
|
+
def password_clean
|
76
|
+
crypted_password.decrypt(salt)
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
def generate_password
|
81
|
+
return if password.blank?
|
82
|
+
self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{email}--") if new_record?
|
83
|
+
self.crypted_password = password.encrypt(self.salt)
|
84
|
+
end
|
85
|
+
|
86
|
+
def password_required
|
87
|
+
crypted_password.blank? || !password.blank?
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end # Account
|
91
|
+
end # Abstract
|
92
|
+
end # Orm
|
93
|
+
end # Admin
|
94
|
+
end # Padrino
|
@@ -1,54 +1,79 @@
|
|
1
1
|
module Padrino
|
2
2
|
module Admin
|
3
|
-
module
|
4
|
-
module
|
5
|
-
|
3
|
+
module Orm
|
4
|
+
module ActiveRecord
|
5
|
+
##
|
6
6
|
# Here basic functions for interact with ActiveRecord
|
7
|
+
#
|
7
8
|
module Base
|
8
|
-
|
9
|
-
|
9
|
+
|
10
|
+
def self.included(base) #:nodoc:
|
11
|
+
base.send :include, Padrino::Admin::Orm::Abstract::Base
|
10
12
|
base.send :include, InstanceMethods
|
11
13
|
base.extend ClassMethods
|
12
14
|
end
|
13
15
|
|
14
16
|
module InstanceMethods
|
15
|
-
|
17
|
+
##
|
16
18
|
# Method for get only fields with errors
|
19
|
+
#
|
17
20
|
def errors_keys
|
18
21
|
errors.map { |k,v| k.to_sym }.uniq
|
19
22
|
end
|
20
|
-
end
|
23
|
+
end # InstanceMethods
|
21
24
|
|
22
25
|
module ClassMethods
|
23
|
-
|
26
|
+
##
|
27
|
+
# Transforms attribute key names into a more humane format, such as "First name" instead of "first_name".
|
28
|
+
#
|
29
|
+
# Example:
|
24
30
|
# Person.human_attribute_name("first_name") # => "First name"
|
25
|
-
#
|
26
|
-
# module now.
|
31
|
+
#
|
27
32
|
# Specify +options+ with additional translating options.
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
defaults << options[:default] if options[:default]
|
33
|
-
defaults.flatten!
|
34
|
-
defaults << attribute_key_name.to_s.humanize
|
35
|
-
options[:count] ||= 1
|
36
|
-
I18n.translate(defaults.shift, options.merge(:default => defaults, :scope => [:model, :attributes]))
|
33
|
+
#
|
34
|
+
def human_attribute_name(field, options = {})
|
35
|
+
options.reverse_merge!(:count => 1, :default => field.to_s.humanize, :scope => [:model, :attributes])
|
36
|
+
I18n.translate("#{self.name.underscore}.#{field}", options)
|
37
37
|
end
|
38
38
|
|
39
|
-
|
39
|
+
##
|
40
|
+
# Alias method for get columns names
|
41
|
+
#
|
40
42
|
def properties
|
41
43
|
columns
|
42
44
|
end
|
43
45
|
|
46
|
+
##
|
47
|
+
# Return :activerecord
|
48
|
+
#
|
49
|
+
def orm
|
50
|
+
:activerecord
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# Method for perorm a full text search / sorting in ExtJS grids.
|
55
|
+
#
|
56
|
+
# For build a query you can provide for +params+:
|
57
|
+
#
|
58
|
+
# query:: word do search will be converted to "%word%"
|
59
|
+
# fields:: where you want search
|
60
|
+
# sort:: field to sort
|
61
|
+
# dir:: one of ASC/DESC
|
62
|
+
# limit:: limit your results
|
63
|
+
# start:: offset of your resluts
|
64
|
+
#
|
65
|
+
# For +query+ we mean standard adapter options such as +include+, +joins+ ...
|
66
|
+
#
|
67
|
+
# So a +ext_search+ can be:
|
68
|
+
#
|
69
|
+
# Account.ext_search({:query => "foo", fileds="name,surname,categories.name", :sort => "name",
|
70
|
+
# :dir => "asc", :limit => 50, :offset => 10 }, { :joins => :categories })
|
71
|
+
#
|
44
72
|
def ext_search(params, query={})
|
45
73
|
|
46
74
|
# We build a base struct for have some good results
|
47
75
|
result = ExtSearch.new(0, [])
|
48
76
|
|
49
|
-
# We need a basic query
|
50
|
-
query = {}
|
51
|
-
|
52
77
|
# Search conditions
|
53
78
|
if params[:query].present? && params[:fields].present?
|
54
79
|
filters = params[:fields].split(",").collect { |f| "#{f} LIKE ?" }.compact
|
@@ -62,23 +87,30 @@ module Padrino
|
|
62
87
|
|
63
88
|
# Now time to limit/offset it
|
64
89
|
query[:limit] = params[:limit].to_i if params[:limit].to_i > 0
|
65
|
-
query[:offset] = params[:
|
90
|
+
query[:offset] = params[:start].to_i if params[:start].to_i > 0
|
66
91
|
|
67
92
|
result.records = all(query)
|
68
93
|
result
|
69
94
|
end
|
70
95
|
|
71
|
-
end
|
72
|
-
end
|
96
|
+
end # ClassMethods
|
97
|
+
end # Base
|
73
98
|
|
74
|
-
|
99
|
+
##
|
100
|
+
# Here extension for Account for ActiveRecord
|
101
|
+
#
|
102
|
+
# Basically we need only to perform:
|
103
|
+
#
|
104
|
+
# * Validations (email, password, role)
|
105
|
+
# * Generate crypted_password on save
|
106
|
+
#
|
75
107
|
module Account
|
76
|
-
|
77
|
-
def self.included(base)
|
78
|
-
base.send :include, Padrino::Admin::
|
108
|
+
|
109
|
+
def self.included(base) #:nodoc:
|
110
|
+
base.send :include, Padrino::Admin::Orm::Abstract::Account
|
79
111
|
base.send :attr_accessor, :password
|
80
112
|
# Validations
|
81
|
-
base.validates_presence_of :email
|
113
|
+
base.validates_presence_of :email, :role
|
82
114
|
base.validates_presence_of :password, :if => :password_required
|
83
115
|
base.validates_presence_of :password_confirmation, :if => :password_required
|
84
116
|
base.validates_length_of :password, :within => 4..40, :if => :password_required
|
@@ -86,11 +118,12 @@ module Padrino
|
|
86
118
|
base.validates_length_of :email, :within => 3..100
|
87
119
|
base.validates_uniqueness_of :email, :case_sensitive => false
|
88
120
|
base.validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
|
121
|
+
base.validates_format_of :role, :with => /[A-Za-z]/
|
89
122
|
# Callbacks
|
90
123
|
base.before_save :generate_password
|
91
124
|
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
125
|
+
end # Account
|
126
|
+
end # ActiveRecord
|
127
|
+
end # Orm
|
128
|
+
end # Admin
|
129
|
+
end # Padrino
|
@@ -0,0 +1,214 @@
|
|
1
|
+
module Padrino
|
2
|
+
module Admin
|
3
|
+
module Orm
|
4
|
+
module DataMapper
|
5
|
+
##
|
6
|
+
# Here basic functions for interact with DataMapper
|
7
|
+
#
|
8
|
+
module Base
|
9
|
+
|
10
|
+
def self.included(base) #:nodoc:
|
11
|
+
base.send :include, Padrino::Admin::Orm::Abstract::Base
|
12
|
+
base.send :include, InstanceMethods
|
13
|
+
base.extend ClassMethods
|
14
|
+
end
|
15
|
+
|
16
|
+
module InstanceMethods
|
17
|
+
##
|
18
|
+
# This is an alias method to allow us to don't see deprecations
|
19
|
+
# and keep compatibility with new DataMapper versions
|
20
|
+
#
|
21
|
+
def new_record?; new?; end
|
22
|
+
|
23
|
+
##
|
24
|
+
# Returns a String, which Padrino uses for constructing an URL to this object.
|
25
|
+
# The default implementation returns this record‘s id as a String,
|
26
|
+
# or nil if this record‘s unsaved.
|
27
|
+
#
|
28
|
+
def to_param
|
29
|
+
# We can't use alias_method here, because method 'id' optimizes itself on the fly.
|
30
|
+
(id = self.id) ? id.to_s : nil # Be sure to stringify the id for routes
|
31
|
+
end
|
32
|
+
|
33
|
+
##
|
34
|
+
# Update attributes is deprecated but for compatibility with other ORM we support them.
|
35
|
+
#
|
36
|
+
def update_attributes(attributes = {})
|
37
|
+
update(attributes)
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# Method for get only fields with errors
|
42
|
+
#
|
43
|
+
def errors_keys
|
44
|
+
errors.keys
|
45
|
+
end
|
46
|
+
|
47
|
+
end # InstanceMethods
|
48
|
+
|
49
|
+
module ClassMethods
|
50
|
+
##
|
51
|
+
# Transforms attribute key names into a more humane format, such as "First name" instead of "first_name".
|
52
|
+
#
|
53
|
+
# Example:
|
54
|
+
# Person.human_attribute_name("first_name") # => "First name"
|
55
|
+
#
|
56
|
+
# Specify +options+ with additional translating options.
|
57
|
+
#
|
58
|
+
def human_attribute_name(field, options = {})
|
59
|
+
options.reverse_merge!(:count => 1, :default => field.to_s.humanize, :scope => [:model, :attributes])
|
60
|
+
I18n.translate("#{self.name.underscore}.#{field}", options)
|
61
|
+
end
|
62
|
+
|
63
|
+
##
|
64
|
+
# Return the name of the Table
|
65
|
+
#
|
66
|
+
# Because in some unkown circumstances +storage_names[:default]+ give us an wrong value
|
67
|
+
# we use the class name for get the value
|
68
|
+
#
|
69
|
+
def table_name
|
70
|
+
self.name.underscore.pluralize
|
71
|
+
end
|
72
|
+
|
73
|
+
##
|
74
|
+
# Return :activerecord
|
75
|
+
#
|
76
|
+
def orm
|
77
|
+
:datamapper
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Method for perorm a full text search / sorting in ExtJS grids.
|
82
|
+
#
|
83
|
+
# For build a query you can provide for +params+:
|
84
|
+
#
|
85
|
+
# query:: word do search will be converted to "%word%"
|
86
|
+
# fields:: where you want search
|
87
|
+
# sort:: field to sort
|
88
|
+
# dir:: one of ASC/DESC
|
89
|
+
# limit:: limit your results
|
90
|
+
# start:: offset of your resluts
|
91
|
+
#
|
92
|
+
# For +query+ we mean standard adapter options such as +links+ ...
|
93
|
+
#
|
94
|
+
# So a +ext_search+ can be:
|
95
|
+
#
|
96
|
+
# Account.ext_search({:query => "foo", fileds="name,surname,categories.name", :sort => "name",
|
97
|
+
# :dir => "asc", :limit => 50, :offset => 10 }, { :links => :categories })
|
98
|
+
#
|
99
|
+
def ext_search(params, query={})
|
100
|
+
|
101
|
+
# We build a base struct for have some good results
|
102
|
+
result = ExtSearch.new(0, [])
|
103
|
+
|
104
|
+
if params[:query].present? && params[:fields].present?
|
105
|
+
# Here we build some like: ["name LIKE ?", "surname LIKE ?"]
|
106
|
+
fields = params[:fields].split(",").collect { |f| "#{f.strip.downcase} LIKE ?" }
|
107
|
+
# Here we build some like: ["name LIKE ? OR surname LIKE ?", "%foo%", "%foo%"]
|
108
|
+
query[:conditions] = [fields.join(" OR ")].concat(1.upto(fields.length).collect { "%#{params[:query]}%" })
|
109
|
+
end
|
110
|
+
|
111
|
+
# Now we can perform a count
|
112
|
+
result.count = count(query)
|
113
|
+
|
114
|
+
# First we need to sort our record but we have some problems if we sort for associated tables.
|
115
|
+
#
|
116
|
+
# see: http://www.mail-archive.com/datamapper@googlegroups.com/msg01310.html
|
117
|
+
#
|
118
|
+
# I can get these values:
|
119
|
+
#
|
120
|
+
# * accounts.name
|
121
|
+
# * accounts.posts.name
|
122
|
+
# * accounts.posts.categories.name
|
123
|
+
#
|
124
|
+
# We need to transform in some like [DataMapper::Query::Direction.new(Account.properties[:name], :asc)]
|
125
|
+
#
|
126
|
+
if params[:sort].present? && params[:dir].to_s =~ /^(asc|desc)$/i
|
127
|
+
values = params[:sort].to_s.sub(/^#{table_name}\./, "").split(".").collect(&:to_sym)
|
128
|
+
property = values.delete_at(-1) # the last value is always a property
|
129
|
+
relation = values.inject(self) do |relation, value|
|
130
|
+
raise "Unknow relation #{value} for #{relation}" unless relation.relationships[value]
|
131
|
+
relation.send(value)
|
132
|
+
end
|
133
|
+
target = relation.respond_to?(:model) ? relation.model.properties[property] : relation.properties[property]
|
134
|
+
query[:order] = [::DataMapper::Query::Direction.new(target, params[:dir].to_s.downcase.to_sym)]
|
135
|
+
end
|
136
|
+
|
137
|
+
# Now time to limit/offset it
|
138
|
+
query[:limit] = params[:limit].to_i if params[:limit].to_i > 0
|
139
|
+
query[:offset] = params[:start].to_i if params[:start].to_i > 0
|
140
|
+
|
141
|
+
# Now we can perform ording/limiting
|
142
|
+
result.records = all(query)
|
143
|
+
result
|
144
|
+
end
|
145
|
+
|
146
|
+
##
|
147
|
+
# The aim of this method is act DM as AR, so when you define a many-to-many relation like:
|
148
|
+
#
|
149
|
+
# has n, :images
|
150
|
+
# has n, :images, :through => Resource
|
151
|
+
#
|
152
|
+
# you have two new methods:
|
153
|
+
#
|
154
|
+
# images_ids
|
155
|
+
# images_ids=(*ids)
|
156
|
+
#
|
157
|
+
def has(cardinality, name, *args)
|
158
|
+
relationship = super(cardinality, name, *args)
|
159
|
+
if relationship.is_a?(::DataMapper::Associations::ManyToMany::Relationship) ||
|
160
|
+
relationship.is_a?(::DataMapper::Associations::OneToMany::Relationship)
|
161
|
+
|
162
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
163
|
+
def #{name.to_s.singular}_ids
|
164
|
+
#{name}.collect(&:id)
|
165
|
+
end
|
166
|
+
|
167
|
+
def #{name.to_s.singular}_ids=(*ids)
|
168
|
+
self.#{name} = #{relationship.child_model.name}.all(:id => ids.flatten)
|
169
|
+
end
|
170
|
+
RUBY
|
171
|
+
end
|
172
|
+
relationship
|
173
|
+
end
|
174
|
+
end # ClassMethods
|
175
|
+
end # Base
|
176
|
+
|
177
|
+
##
|
178
|
+
# Here extension for Account for MongoMapper
|
179
|
+
#
|
180
|
+
# Basically we need only to perform:
|
181
|
+
#
|
182
|
+
# * Validations (email, password, role)
|
183
|
+
# * Generate crypted_password on save
|
184
|
+
#
|
185
|
+
module Account
|
186
|
+
|
187
|
+
def self.included(base) #:nodoc:
|
188
|
+
super
|
189
|
+
base.send :include, Padrino::Admin::Orm::Abstract::Account
|
190
|
+
base.send :include, ::DataMapper::Validate
|
191
|
+
base.send :attr_accessor, :password, :password_confirmation
|
192
|
+
# Properties
|
193
|
+
base.property :email, String
|
194
|
+
base.property :crypted_password, String
|
195
|
+
base.property :salt, String
|
196
|
+
base.property :role, String
|
197
|
+
# Validations
|
198
|
+
base.validates_present :email, :role
|
199
|
+
base.validates_present :password, :if => :password_required
|
200
|
+
base.validates_present :password_confirmation, :if => :password_required
|
201
|
+
base.validates_length :password, :min => 4, :max => 40, :if => :password_required
|
202
|
+
base.validates_is_confirmed :password, :if => :password_required
|
203
|
+
base.validates_length :email, :min => 3, :max => 100
|
204
|
+
base.validates_is_unique :email, :case_sensitive => false
|
205
|
+
base.validates_format :email, :with => :email_address
|
206
|
+
base.validates_format :role, :with => /[A-Za-z]/
|
207
|
+
# Callbacks
|
208
|
+
base.before :save, :generate_password
|
209
|
+
end
|
210
|
+
end # Account
|
211
|
+
end # DataMapper
|
212
|
+
end # Orm
|
213
|
+
end # Admin
|
214
|
+
end # Padrino
|