ixtlan 0.2.0
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.
- data/Manifest.txt +64 -0
- data/README.txt +86 -0
- data/Rakefile +38 -0
- data/generators/ixtlan_datamapper_model/ixtlan_datamapper_model_generator.rb +20 -0
- data/generators/ixtlan_datamapper_model/templates/model.rb +17 -0
- data/generators/ixtlan_datamapper_rspec_model/ixtlan_datamapper_rspec_model_generator.rb +20 -0
- data/generators/ixtlan_datamapper_rspec_model/templates/model_spec.rb +58 -0
- data/generators/ixtlan_datamapper_rspec_scaffold/ixtlan_datamapper_rspec_scaffold_generator.rb +37 -0
- data/generators/ixtlan_datamapper_rspec_scaffold/templates/controller.rb +99 -0
- data/generators/ixtlan_datamapper_rspec_scaffold/templates/controller_spec.rb +206 -0
- data/generators/ixtlan_datamapper_rspec_scaffold/templates/guard.rb +8 -0
- data/generators/ixtlan_datamapper_rspec_scaffold/templates/i18n.rb +11 -0
- data/lib/dm-serializer/common.rb +28 -0
- data/lib/dm-serializer/to_xml.rb +99 -0
- data/lib/dm-serializer/xml_serializers/libxml.rb +42 -0
- data/lib/dm-serializer/xml_serializers/nokogiri.rb +36 -0
- data/lib/dm-serializer/xml_serializers/rexml.rb +30 -0
- data/lib/dm-serializer/xml_serializers.rb +17 -0
- data/lib/dm-serializer.rb +1 -0
- data/lib/ixtlan/audit_config.rb +35 -0
- data/lib/ixtlan/cms_script.rb +29 -0
- data/lib/ixtlan/controllers/texts_controller.rb +65 -0
- data/lib/ixtlan/digest.rb +15 -0
- data/lib/ixtlan/error_notifier/error_notification.rhtml +1 -0
- data/lib/ixtlan/guard.rb +125 -0
- data/lib/ixtlan/logger_config.rb +65 -0
- data/lib/ixtlan/models/authentication.rb +30 -0
- data/lib/ixtlan/models/configuration.rb +74 -0
- data/lib/ixtlan/models/configuration_locale.rb +19 -0
- data/lib/ixtlan/models/group.rb +69 -0
- data/lib/ixtlan/models/group_locale_user.rb +22 -0
- data/lib/ixtlan/models/group_user.rb +39 -0
- data/lib/ixtlan/models/locale.rb +37 -0
- data/lib/ixtlan/models/permission.rb +29 -0
- data/lib/ixtlan/models/phrase.rb +71 -0
- data/lib/ixtlan/models/role.rb +35 -0
- data/lib/ixtlan/models/text.rb +140 -0
- data/lib/ixtlan/models/translation.rb +40 -0
- data/lib/ixtlan/models/user.rb +112 -0
- data/lib/ixtlan/models/word.rb +12 -0
- data/lib/ixtlan/models.rb +13 -0
- data/lib/ixtlan/modified_by.rb +80 -0
- data/lib/ixtlan/monkey_patches.rb +38 -0
- data/lib/ixtlan/optimistic_persistence.rb +20 -0
- data/lib/ixtlan/optimistic_persistence_module.rb +22 -0
- data/lib/ixtlan/optimistic_persistence_validation.rb +21 -0
- data/lib/ixtlan/passwords.rb +19 -0
- data/lib/ixtlan/rails/audit.rb +22 -0
- data/lib/ixtlan/rails/error_handling.rb +130 -0
- data/lib/ixtlan/rails/guard.rb +11 -0
- data/lib/ixtlan/rails/session_timeout.rb +93 -0
- data/lib/ixtlan/rails/timestamps_modified_by_filter.rb +33 -0
- data/lib/ixtlan/rails/unrestful_authentication.rb +137 -0
- data/lib/ixtlan/rolling_file.rb +61 -0
- data/lib/ixtlan/session.rb +18 -0
- data/lib/ixtlan/user_logger.rb +49 -0
- data/lib/ixtlan/version.rb +3 -0
- data/lib/ixtlan.rb +2 -0
- data/lib/models.rb +14 -0
- data/spec/authentication_spec.rb +30 -0
- data/spec/guard_spec.rb +125 -0
- data/spec/guards/samples.rb +12 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +170 -0
- metadata +210 -0
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'dm-core'
|
2
|
+
require 'ixtlan/modified_by'
|
3
|
+
module Ixtlan
|
4
|
+
module Models
|
5
|
+
class Configuration
|
6
|
+
include DataMapper::Resource
|
7
|
+
|
8
|
+
def self.default_storage_name
|
9
|
+
"Configuration"
|
10
|
+
end
|
11
|
+
|
12
|
+
property :id, Serial
|
13
|
+
|
14
|
+
property :session_idle_timeout, Integer, :nullable => false
|
15
|
+
|
16
|
+
property :keep_audit_logs, Integer, :nullable => false
|
17
|
+
|
18
|
+
property :notification_sender_email, String, :format => :email, :nullable => true, :length => 64
|
19
|
+
|
20
|
+
property :notification_recipient_emails, String, :format => Proc.new { |email| emails = email.split(','); emails.find_all { |e| e =~ DataMapper::Validate::Format::Email::EmailAddress }.size == emails.size}, :nullable => true, :length => 254 #honour mysql max varchar length
|
21
|
+
|
22
|
+
timestamps :updated_at
|
23
|
+
|
24
|
+
modified_by ::Ixtlan::Models::USER, :updated_by
|
25
|
+
|
26
|
+
def locales
|
27
|
+
if @locales.nil?
|
28
|
+
# TODO spec the empty array to make sure new relations are stored
|
29
|
+
# in the database or the locales collection is empty before filling it
|
30
|
+
@locales = ::DataMapper::Collection.new(::DataMapper::Query.new(self.repository, Object.full_const_get(::Ixtlan::Models::LOCALE)), [])
|
31
|
+
ConfigurationLocale.all(:configuration_id => id).each{ |cl| @locales << cl.locale }
|
32
|
+
def @locales.configuration=(configuration)
|
33
|
+
@configuration = configuration
|
34
|
+
end
|
35
|
+
@locales.configuration = self
|
36
|
+
def @locales.<<(locale)
|
37
|
+
unless member? locale
|
38
|
+
ConfigurationLocale.create(:configuration_id => @configuration.id, :locale_code => locale.code)
|
39
|
+
super
|
40
|
+
end
|
41
|
+
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
def @locales.delete(locale)
|
46
|
+
cl = ConfigurationLocale.first(:configuration_id => @configuration.id, :user_id => @user.id, :locale_code => locale.code)
|
47
|
+
if cl
|
48
|
+
cl.destroy
|
49
|
+
end
|
50
|
+
super
|
51
|
+
end
|
52
|
+
end
|
53
|
+
@locales
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.instance
|
57
|
+
# HACK: return a new object in case there is none in the database
|
58
|
+
# to allow rails rake tasks to work with an empty database
|
59
|
+
begin
|
60
|
+
get(1) || new(:id => 1, :session_idle_timeout => 5, :keep_audit_logs => 7)
|
61
|
+
rescue
|
62
|
+
auto_migrate!
|
63
|
+
new(:id => 1, :session_idle_timeout => 5, :keep_audit_logs => 7)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
alias :to_x :to_xml_document
|
68
|
+
def to_xml_document(opts, doc = nil)
|
69
|
+
opts.merge!({:methods => [:updated_by, :locales], :exclude => [:updated_by_id, :id]})
|
70
|
+
to_x(opts, doc)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Ixtlan
|
2
|
+
module Models
|
3
|
+
class ConfigurationLocale
|
4
|
+
|
5
|
+
include DataMapper::Resource
|
6
|
+
|
7
|
+
def self.default_storage_name
|
8
|
+
"ConfigurationLocale"
|
9
|
+
end
|
10
|
+
|
11
|
+
property :configuration_id, Integer, :key => true
|
12
|
+
|
13
|
+
property :locale_code, String, :key => true
|
14
|
+
|
15
|
+
belongs_to :configuration, :model => Models::CONFIGURATION
|
16
|
+
belongs_to :locale, :model => Models::LOCALE
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Ixtlan
|
2
|
+
module Models
|
3
|
+
class Group
|
4
|
+
|
5
|
+
include DataMapper::Resource
|
6
|
+
|
7
|
+
def self.default_storage_name
|
8
|
+
"Group"
|
9
|
+
end
|
10
|
+
|
11
|
+
property :id, Serial, :field => "gidnumber"
|
12
|
+
|
13
|
+
property :name, String, :nullable => false , :format => /^[^<'&">]*$/, :length => 32, :field => "cn", :unique_index => true
|
14
|
+
|
15
|
+
timestamps :created_at
|
16
|
+
|
17
|
+
modified_by Models::USER
|
18
|
+
|
19
|
+
def locales(user = nil)
|
20
|
+
# return ::DataMapper::Collection.new(::DataMapper::Query.new(self.repository, Locale), [])
|
21
|
+
if @locales.nil? or not user.nil?
|
22
|
+
|
23
|
+
# TODO spec the empty array to make sure new relations are stored
|
24
|
+
# in the database or the locales collection is empty before filling it
|
25
|
+
@locales = ::DataMapper::Collection.new(::DataMapper::Query.new(self.repository, Models::Locale), [])
|
26
|
+
|
27
|
+
return @locales if user.nil?
|
28
|
+
|
29
|
+
GroupLocaleUser.all(:group_id => id, :user_id => user.id).each{ |glu| @locales << glu.locale }
|
30
|
+
def @locales.group=(group)
|
31
|
+
@group = group
|
32
|
+
end
|
33
|
+
@locales.group = self
|
34
|
+
def @locales.user=(user)
|
35
|
+
@user = user
|
36
|
+
end
|
37
|
+
@locales.user = user
|
38
|
+
def @locales.<<(locale)
|
39
|
+
unless member? locale
|
40
|
+
GroupLocaleUser.create(:group_id => @group.id, :user_id => @user.id, :locale_code => locale.code)
|
41
|
+
super
|
42
|
+
end
|
43
|
+
|
44
|
+
self
|
45
|
+
end
|
46
|
+
def @locales.delete(locale)
|
47
|
+
glu = GroupLocaleUser.first(:group_id => @group.id, :user_id => @user.id, :locale_code => locale.code)
|
48
|
+
if glu
|
49
|
+
glu.destroy
|
50
|
+
end
|
51
|
+
super
|
52
|
+
end
|
53
|
+
end
|
54
|
+
@locales
|
55
|
+
end
|
56
|
+
|
57
|
+
if protected_instance_methods.find {|m| m == 'to_x'}.nil?
|
58
|
+
|
59
|
+
protected
|
60
|
+
|
61
|
+
alias :to_x :to_xml_document
|
62
|
+
def to_xml_document(opts, doc = nil)
|
63
|
+
opts.merge!({:methods => [:locales]})
|
64
|
+
to_x(opts, doc)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Ixtlan
|
2
|
+
module Models
|
3
|
+
class GroupLocaleUser
|
4
|
+
|
5
|
+
include DataMapper::Resource
|
6
|
+
|
7
|
+
def self.default_storage_name
|
8
|
+
"GroupLocaleUser"
|
9
|
+
end
|
10
|
+
|
11
|
+
property :group_id, Integer, :key => true
|
12
|
+
|
13
|
+
property :user_id, Integer, :key => true
|
14
|
+
|
15
|
+
property :locale_code, String, :key => true
|
16
|
+
|
17
|
+
belongs_to :group, :model => Models::GROUP
|
18
|
+
belongs_to :user, :model => Models::USER
|
19
|
+
belongs_to :locale, :model => Models::LOCALE
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Ixtlan
|
2
|
+
module Models
|
3
|
+
class GroupUser
|
4
|
+
include DataMapper::Resource
|
5
|
+
|
6
|
+
def self.default_storage_name
|
7
|
+
"GroupUser"
|
8
|
+
end
|
9
|
+
|
10
|
+
# dn_prefix { |group_user| "cn=#{group_user.group.name}" }
|
11
|
+
|
12
|
+
#treebase "ou=groups"
|
13
|
+
|
14
|
+
# multivalue_field :memberuid
|
15
|
+
|
16
|
+
#ldap_properties do |group_user|
|
17
|
+
# {:cn=>"#{group_user.group.name}", :objectclass => "posixGroup"}
|
18
|
+
# end
|
19
|
+
property :memberuid, String, :key => true
|
20
|
+
property :gidnumber, Integer, :key => true
|
21
|
+
|
22
|
+
def group
|
23
|
+
Object.full_const_get(Models::GROUP).get!(gidnumber)
|
24
|
+
end
|
25
|
+
|
26
|
+
def group=(group)
|
27
|
+
gidnumber = group.id
|
28
|
+
end
|
29
|
+
|
30
|
+
def user
|
31
|
+
Object.full_const_get(Models::USER).first(:login => memberuid)
|
32
|
+
end
|
33
|
+
|
34
|
+
def user=(user)
|
35
|
+
memberuid = user.login
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Ixtlan
|
2
|
+
module Models
|
3
|
+
class Locale
|
4
|
+
include DataMapper::Resource
|
5
|
+
|
6
|
+
def self.default_storage_name
|
7
|
+
"Locale"
|
8
|
+
end
|
9
|
+
|
10
|
+
unless const_defined? "DEFAULT"
|
11
|
+
DEFAULT = "DEFAULT"
|
12
|
+
ALL = "ALL"
|
13
|
+
end
|
14
|
+
|
15
|
+
property :code, String, :nullable => false , :format => /^[a-z][a-z](_[A-Z][A-Z])?$|^#{DEFAULT}$|^#{ALL}$/, :length => 7, :key => true
|
16
|
+
|
17
|
+
timestamps :created_at
|
18
|
+
|
19
|
+
def self.default
|
20
|
+
get(DEFAULT) || create(:code => DEFAULT)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.every
|
24
|
+
get(ALL) || create(:code => ALL)
|
25
|
+
end
|
26
|
+
|
27
|
+
def hash
|
28
|
+
attribute_get(:code).hash
|
29
|
+
end
|
30
|
+
|
31
|
+
alias :eql? :==
|
32
|
+
def ==(other)
|
33
|
+
attribute_get(:code).eql?(other.attribute_get(:code))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Ixtlan
|
2
|
+
module Models
|
3
|
+
class Permission
|
4
|
+
|
5
|
+
include DataMapper::Resource
|
6
|
+
|
7
|
+
def self.default_storage_name
|
8
|
+
"Permission"
|
9
|
+
end
|
10
|
+
|
11
|
+
property :resource, String,:format => /^[a-zA-Z0-9_.]*$/, :key => true
|
12
|
+
|
13
|
+
property :action, String, :format => /^[a-zA-Z0-9_.]*$/, :key => true
|
14
|
+
|
15
|
+
has n, :roles, :model => Models::ROLE
|
16
|
+
|
17
|
+
if protected_instance_methods.find {|m| m == 'to_x'}.nil?
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
alias :to_x :to_xml_document
|
22
|
+
def to_xml_document(opts, doc = nil)
|
23
|
+
opts.merge!({:methods => [:roles]})
|
24
|
+
to_x(opts, doc)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Ixtlan
|
2
|
+
module Models
|
3
|
+
class Phrase
|
4
|
+
include DataMapper::Resource
|
5
|
+
|
6
|
+
def self.default_storage_name
|
7
|
+
"Phrase"
|
8
|
+
end
|
9
|
+
|
10
|
+
property :id, Serial
|
11
|
+
|
12
|
+
property :code, String, :nullable => false, :length => 64
|
13
|
+
|
14
|
+
property :text, String, :nullable => false, :length => 256
|
15
|
+
|
16
|
+
property :current_text, String, :nullable => true, :length => 256
|
17
|
+
|
18
|
+
property :updated_at, DateTime, :nullable => false, :auto_validation => false
|
19
|
+
|
20
|
+
belongs_to :updated_by, :model => Models::USER
|
21
|
+
|
22
|
+
belongs_to :locale, :model => Models::LOCALE
|
23
|
+
|
24
|
+
belongs_to :default_translation, :model => Models::TRANSLATION, :nullable => true
|
25
|
+
|
26
|
+
belongs_to :parent_translation, :model => Models::TRANSLATION, :nullable => true
|
27
|
+
|
28
|
+
alias :to_x :to_xml_document
|
29
|
+
def to_xml_document(opts = {}, doc = nil)
|
30
|
+
opts.merge!({:exclude => [:id, :parent_translation_id, :default_translation_id, :locale_code], :element_name => 'phrase', :methods => [:default_translation, :parent_translation, :locale]})
|
31
|
+
to_x(opts, doc)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.all(args = {})
|
35
|
+
phrases = ::DataMapper::Collection.new(::DataMapper::Query.new(self.repository, Ixtlan::Models::Phrase), [])
|
36
|
+
map = {}
|
37
|
+
locale = (args[:locale] ||= Locale.default)
|
38
|
+
Text.not_approved(args.dup).each do |text|
|
39
|
+
phrase = Phrase.new(:code => text.code, :text => text.text, :current_text => text.text, :locale => locale, :updated_at => text.updated_at, :updated_by => text.updated_by)
|
40
|
+
map[phrase.code] = phrase
|
41
|
+
end
|
42
|
+
Text.latest_approved(args.dup).each do |text|
|
43
|
+
if (phrase = map[text.code])
|
44
|
+
phrase.current_text = text.text
|
45
|
+
else
|
46
|
+
phrase = Phrase.new(:code => text.code, :text => text.text, :current_text => text.text, :locale => locale, :updated_at => text.updated_at, :updated_by => text.updated_by)
|
47
|
+
map[phrase.code] = phrase
|
48
|
+
end
|
49
|
+
end
|
50
|
+
case locale.code.size
|
51
|
+
when 2
|
52
|
+
params = args.dup
|
53
|
+
params[:locale] = Locale.default
|
54
|
+
Translation.map_for(params).each do |code, trans|
|
55
|
+
ph = map[code]
|
56
|
+
if(ph.nil?)
|
57
|
+
map[code] = Phrase.new(:code => code, :text => trans.text, :current_text => trans.text, :locale => locale, :updated_at => trans.approved_at, :updated_by => trans.approved_by, :default_translation => trans)
|
58
|
+
else
|
59
|
+
ph.default_translation = trans
|
60
|
+
end
|
61
|
+
end
|
62
|
+
when 5
|
63
|
+
raise "not implemented yet"
|
64
|
+
end
|
65
|
+
map.values.each { |ph| phrases << ph}
|
66
|
+
phrases
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Ixtlan
|
2
|
+
module Models
|
3
|
+
class Role
|
4
|
+
|
5
|
+
include DataMapper::Resource
|
6
|
+
|
7
|
+
def self.default_storage_name
|
8
|
+
"Role"
|
9
|
+
end
|
10
|
+
|
11
|
+
property :name, String, :nullable => false , :format => /^[a-zA-Z0-9\-_.]*$/, :length => 32, :key => true
|
12
|
+
|
13
|
+
def hash
|
14
|
+
attribute_get(:name).hash
|
15
|
+
end
|
16
|
+
|
17
|
+
alias :eql? :==
|
18
|
+
def ==(other)
|
19
|
+
attribute_get(:name).eql?(other.attribute_get(:name))
|
20
|
+
end
|
21
|
+
|
22
|
+
if protected_instance_methods.find {|m| m == 'to_x'}.nil?
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
alias :to_x :to_xml_document
|
27
|
+
def to_xml_document(opts, doc = nil)
|
28
|
+
opts.merge!({:exclude => [:permission_resource,:permission_action]})
|
29
|
+
to_x(opts, doc)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
@@ -0,0 +1,140 @@
|
|
1
|
+
module Ixtlan
|
2
|
+
module Models
|
3
|
+
class Text
|
4
|
+
include DataMapper::Resource
|
5
|
+
|
6
|
+
def self.default_storage_name
|
7
|
+
"Text"
|
8
|
+
end
|
9
|
+
|
10
|
+
property :id, Serial
|
11
|
+
|
12
|
+
property :code, String, :nullable => false, :length => 64
|
13
|
+
|
14
|
+
property :text, String, :nullable => false, :length => 256
|
15
|
+
|
16
|
+
property :version, Integer, :nullable => true
|
17
|
+
|
18
|
+
property :current, Boolean, :nullable => false, :auto_validation => false
|
19
|
+
|
20
|
+
property :previous, Boolean, :nullable => false, :auto_validation => false
|
21
|
+
|
22
|
+
property :updated_at, DateTime, :nullable => false, :auto_validation => false
|
23
|
+
|
24
|
+
belongs_to :updated_by, :model => Models::USER
|
25
|
+
|
26
|
+
property :approved_at, DateTime, :nullable => true
|
27
|
+
|
28
|
+
belongs_to :approved_by, :model => Models::USER, :nullable => true
|
29
|
+
|
30
|
+
belongs_to :locale, :model => Models::LOCALE
|
31
|
+
|
32
|
+
validates_with_method :invariant
|
33
|
+
|
34
|
+
def invariant
|
35
|
+
no_version = original_attributes[:version].nil? && attribute_get(:version).nil?
|
36
|
+
if no_version
|
37
|
+
if (!attribute_get(:version).nil? && attribute_dirty?(:version) && attribute_dirty?(:text))
|
38
|
+
[false, 'can not approve and change text at the same time']
|
39
|
+
elsif new?
|
40
|
+
[true]
|
41
|
+
elsif attribute_dirty?(:code)
|
42
|
+
[false, 'code is invariant']
|
43
|
+
else
|
44
|
+
[true]
|
45
|
+
end
|
46
|
+
else
|
47
|
+
if (!attribute_get(:version).nil? && attribute_dirty?(:version))
|
48
|
+
if (attribute_dirty?(:text))
|
49
|
+
[false, 'can not approve and change text at the same time']
|
50
|
+
elsif !original_attributes[properties[:version]].nil?
|
51
|
+
[false, 'can not change version']
|
52
|
+
else
|
53
|
+
[true]
|
54
|
+
end
|
55
|
+
elsif attribute_dirty?(:code)
|
56
|
+
[false, 'can not change code']
|
57
|
+
elsif attribute_dirty?(:text)
|
58
|
+
[false, 'can not change text']
|
59
|
+
else
|
60
|
+
[true]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
validates_with_method :cascade
|
66
|
+
|
67
|
+
def cascade
|
68
|
+
size =
|
69
|
+
case locale.code.size
|
70
|
+
when 2
|
71
|
+
self.model.latest_approved(:code => code, :locale => Locale.default).size
|
72
|
+
when 5
|
73
|
+
self.model.latest_approved(:code => code, :locale => Locale.get(locale.code[0,1])).size
|
74
|
+
else
|
75
|
+
1
|
76
|
+
end
|
77
|
+
if(size == 0)
|
78
|
+
[false, "parent text for '#{code}' does not exists"]
|
79
|
+
else
|
80
|
+
[true]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
before :save do
|
85
|
+
if(new? or attribute_get(:version).nil?)
|
86
|
+
attribute_set(:version, nil)
|
87
|
+
attribute_set(:current, false)
|
88
|
+
attribute_set(:previous, false)
|
89
|
+
attribute_set(:approved_at, nil)
|
90
|
+
approved_by = nil
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def approve(params = {})
|
95
|
+
latest = self.class.latest_approved(:code => attribute_get(:code),
|
96
|
+
:locale => locale).first
|
97
|
+
previous = self.class.second_latest_approved(:code => attribute_get(:code),
|
98
|
+
:locale => locale).first
|
99
|
+
params[:version] = latest.nil? ? 1 : latest.version + 1
|
100
|
+
params[:current] = true
|
101
|
+
params[:approved_at] = attribute_get(:updated_at)
|
102
|
+
params[:approved_by] = params[:current_user]
|
103
|
+
|
104
|
+
p = (previous.nil? ? true : previous.update(:previous => false,
|
105
|
+
:current_user => params[:current_user]))
|
106
|
+
l = (latest.nil? ? true : latest.update(:current => false,
|
107
|
+
:previous => true,
|
108
|
+
:current_user => params[:current_user]))
|
109
|
+
u = update(params)
|
110
|
+
|
111
|
+
u && l && p
|
112
|
+
end
|
113
|
+
|
114
|
+
def approved?
|
115
|
+
!attribute_get(:version).nil?
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.latest_approved(args = {})
|
119
|
+
args[:current] = true
|
120
|
+
all(args)
|
121
|
+
end
|
122
|
+
|
123
|
+
def self.second_latest_approved(args = {})
|
124
|
+
args[:previous] = true
|
125
|
+
all(args)
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.approved(args = {})
|
129
|
+
args[:version.not] = nil
|
130
|
+
all(args)
|
131
|
+
end
|
132
|
+
|
133
|
+
def self.not_approved(args = {})
|
134
|
+
args[:version] = nil
|
135
|
+
all(args)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Ixtlan
|
2
|
+
module Models
|
3
|
+
class Translation
|
4
|
+
include DataMapper::Resource
|
5
|
+
|
6
|
+
def self.default_storage_name
|
7
|
+
"Translation"
|
8
|
+
end
|
9
|
+
|
10
|
+
property :id, Serial
|
11
|
+
|
12
|
+
property :text, String, :nullable => false, :length => 256
|
13
|
+
|
14
|
+
property :previous_text, String, :nullable => true, :length => 256
|
15
|
+
|
16
|
+
property :approved_at, DateTime, :nullable => false, :auto_validation => false
|
17
|
+
|
18
|
+
belongs_to :approved_by, :model => Models::USER
|
19
|
+
|
20
|
+
def self.map_for(args = {})
|
21
|
+
map = {}
|
22
|
+
Text.latest_approved(args.dup).each do |text|
|
23
|
+
map[text.code] = Translation.create(:text => text.text, :approved_at => text.approved_at, :approved_by => text.approved_by)
|
24
|
+
end
|
25
|
+
Text.second_latest_approved(args.dup).each do |text|
|
26
|
+
translation = map[text.code]
|
27
|
+
translation.previous_text = text.text
|
28
|
+
end
|
29
|
+
map
|
30
|
+
end
|
31
|
+
|
32
|
+
alias :to_x :to_xml_document
|
33
|
+
def to_xml_document(opts = {}, doc = nil)
|
34
|
+
opts.merge!({:exclude => [:id]})
|
35
|
+
to_x(opts, doc)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'digest'
|
2
|
+
require 'base64'
|
3
|
+
require 'ixtlan/modified_by'
|
4
|
+
require 'dm-serializer'
|
5
|
+
module Ixtlan
|
6
|
+
module Models
|
7
|
+
class User
|
8
|
+
include DataMapper::Resource
|
9
|
+
|
10
|
+
def self.default_storage_name
|
11
|
+
"User"
|
12
|
+
end
|
13
|
+
|
14
|
+
property :id, Serial, :field => "uidnumber"
|
15
|
+
|
16
|
+
property :login, String, :nullable => false , :length => 4..32, :index => :unique_index, :format => /^[a-zA-z0-9]*$/, :writer => :private, :field => "uid"
|
17
|
+
|
18
|
+
property :name, String, :nullable => false, :format => /^[^<">]*$/, :length => 2..64, :field => "cn"
|
19
|
+
|
20
|
+
property :email, String, :nullable => false, :format => :email_address, :nullable => false, :length => 8..64, :index => :unique_index, :field => "mail"
|
21
|
+
|
22
|
+
property :language, String, :nullable => false, :format => /[a-z][a-z]/, :length => 2, :field => "preferredlanguage"
|
23
|
+
|
24
|
+
property :hashed_password, String, :nullable => true, :length => 128, :accessor => :private, :field => "userpassword"
|
25
|
+
|
26
|
+
timestamps :at
|
27
|
+
|
28
|
+
modified_by ::Ixtlan::Models::USER
|
29
|
+
|
30
|
+
# Virtual attribute for the plaintext password
|
31
|
+
attr_reader :password
|
32
|
+
|
33
|
+
validates_is_unique :login
|
34
|
+
validates_is_unique :email
|
35
|
+
|
36
|
+
def reset_password(len = 12)
|
37
|
+
@password = Ixtlan::Passwords.generate(len)
|
38
|
+
attribute_set(:hashed_password, Ixtlan::Digest.ssha(@password, "--#{Time.now}--#{login}--"))
|
39
|
+
@password
|
40
|
+
end
|
41
|
+
|
42
|
+
def digest
|
43
|
+
::Base64.decode64(@hashed_password[6,200])
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.authenticate(login, password)
|
47
|
+
user = first(:login => login) # need to get the salt
|
48
|
+
if user
|
49
|
+
digest = user.digest
|
50
|
+
salt = digest[20,147]
|
51
|
+
if ::Digest::SHA1.digest("#{password}" + salt) == digest[0,20]
|
52
|
+
user
|
53
|
+
else
|
54
|
+
"wrong password"
|
55
|
+
end
|
56
|
+
else
|
57
|
+
"unknown login"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def groups
|
62
|
+
if @groups.nil?
|
63
|
+
# TODO spec the empty array to make sure new relations are stored
|
64
|
+
# in the database or the groups collection is empty before filling it
|
65
|
+
@groups = ::DataMapper::Collection.new(::DataMapper::Query.new(self.repository, Models::Group), [])
|
66
|
+
GroupUser.all(:memberuid => login).each do |gu|
|
67
|
+
@groups << gu.group
|
68
|
+
end
|
69
|
+
def @groups.user=(user)
|
70
|
+
@user = user
|
71
|
+
end
|
72
|
+
@groups.user = self
|
73
|
+
def @groups.<<(group)
|
74
|
+
group.locales(@user)
|
75
|
+
unless member? group
|
76
|
+
gu = GroupUser.create(:memberuid => @user.login, :gidnumber => group.id)
|
77
|
+
super
|
78
|
+
end
|
79
|
+
|
80
|
+
self
|
81
|
+
end
|
82
|
+
|
83
|
+
def @groups.delete(group)
|
84
|
+
gu = GroupUser.first(:memberuid => @user.login, :gidnumber => group.id)
|
85
|
+
if gu
|
86
|
+
gu.destroy
|
87
|
+
end
|
88
|
+
super
|
89
|
+
end
|
90
|
+
@groups.each {|g| g.locales(self) }
|
91
|
+
end
|
92
|
+
@groups
|
93
|
+
end
|
94
|
+
|
95
|
+
# make sure login is immutable
|
96
|
+
def login=(new_login)
|
97
|
+
attribute_set(:login, new_login) if (login.nil? or login == "'NULL'" or login == "NULL")
|
98
|
+
end
|
99
|
+
|
100
|
+
if protected_instance_methods.find {|m| m == 'to_x'}.nil?
|
101
|
+
alias :to_x :to_xml_document
|
102
|
+
|
103
|
+
protected
|
104
|
+
|
105
|
+
def to_xml_document(opts={}, doc = nil)
|
106
|
+
opts.merge!({:exclude => [:hashed_password], :methods => [:groups]})
|
107
|
+
to_x(opts, doc)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|