ixtlan 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|