skylinecms 3.2.0 → 3.3.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/.yardopts +1 -1
- data/CHANGELOG +50 -0
- data/README.md +11 -3
- data/app/controllers/skyline/application_controller.rb +6 -5
- data/app/controllers/skyline/articles_controller.rb +4 -4
- data/app/controllers/skyline/content_controller.rb +2 -4
- data/app/controllers/skyline/media/data_controller.rb +1 -0
- data/app/controllers/skyline/site/media_files_data_controller.rb +1 -0
- data/app/controllers/skyline/user_preferences_controller.rb +1 -1
- data/app/controllers/skyline/users_controller.rb +5 -1
- data/app/controllers/skyline/variant_current_editor_controller.rb +6 -1
- data/app/controllers/skyline/variants_controller.rb +2 -2
- data/app/helpers/skyline/content_helper.rb +1 -1
- data/app/helpers/skyline/editors/editor.rb +6 -6
- data/app/helpers/skyline/editors/joinable_list.rb +1 -1
- data/app/helpers/skyline/form_helper.rb +1 -0
- data/app/helpers/skyline/menu_helper.rb +0 -8
- data/app/helpers/skyline/presenters/presenter.rb +2 -2
- data/app/middleware/skyline/sprockets_middleware.rb +3 -8
- data/app/models/skyline/article.rb +29 -25
- data/app/models/skyline/article_version.rb +20 -20
- data/app/models/skyline/associated_tag.rb +1 -1
- data/app/models/skyline/grant.rb +1 -1
- data/app/models/skyline/link_section_link.rb +1 -1
- data/app/models/skyline/media_cache.rb +1 -1
- data/app/models/skyline/media_dir.rb +0 -2
- data/app/models/skyline/media_node.rb +1 -1
- data/app/models/skyline/object_ref.rb +3 -3
- data/app/models/skyline/page.rb +1 -3
- data/app/models/skyline/publication.rb +3 -1
- data/app/models/skyline/ref_object.rb +1 -1
- data/app/models/skyline/referable_uri.rb +1 -1
- data/app/models/skyline/right.rb +1 -1
- data/app/models/skyline/role.rb +1 -1
- data/app/models/skyline/section.rb +9 -9
- data/app/models/skyline/sections/content_collection_section.rb +3 -3
- data/app/models/skyline/sections/link_section.rb +3 -3
- data/app/models/skyline/sections/media_section.rb +2 -0
- data/app/models/skyline/sections/rss_section.rb +11 -12
- data/app/models/skyline/tag.rb +1 -1
- data/app/models/skyline/user.rb +213 -202
- data/app/models/skyline/user_preference.rb +2 -2
- data/app/models/skyline/variant.rb +18 -14
- data/app/observers/skyline/version_stamper.rb +1 -1
- data/app/templates/skyline/page/default/index.html.erb +1 -1
- data/app/views/skyline/articles/_security.html.erb +1 -1
- data/app/views/skyline/articles/edit.html.erb +1 -1
- data/app/views/skyline/content/{_add.rhtml → _add.html.erb} +0 -0
- data/app/views/skyline/content/{_filter.rhtml → _filter.html.erb} +0 -0
- data/app/views/skyline/content/{create.rhtml → create.html.erb} +0 -0
- data/app/views/skyline/content/{edit.rhtml → edit.html.erb} +0 -0
- data/app/views/skyline/content/{error.rhtml → error.html.erb} +0 -0
- data/app/views/skyline/content/{list.rhtml → list.html.erb} +3 -3
- data/app/views/skyline/content/{show.rhtml → show.html.erb} +0 -0
- data/app/views/skyline/content_sections/_tags.html.erb +1 -1
- data/app/views/skyline/shared/_header_area.html.erb +1 -1
- data/app/views/skyline/users/_new.html.erb +1 -1
- data/config/initializers/middlewares.rb +5 -1
- data/config/initializers/mime_types.rb +0 -6
- data/config/initializers/observers.rb +1 -1
- data/config/locales/en-US.yml +1 -1
- data/db/fixtures/roles_and_rights.rb +5 -5
- data/db/fixtures/roots.rb +1 -1
- data/doc/concepts.md +37 -0
- data/lib/skyline/belongs_to_referable.rb +5 -5
- data/lib/skyline/content/class_meta_data.rb +4 -3
- data/lib/skyline/content/content.rb +28 -18
- data/lib/skyline/content/exportable.rb +3 -3
- data/lib/skyline/content/field_meta_data.rb +12 -11
- data/lib/skyline/content/implementation.rb +7 -6
- data/lib/skyline/content/meta_data/field.rb +1 -1
- data/lib/skyline/content/stack.rb +1 -1
- data/lib/skyline/content/versioning/version.rb +1 -1
- data/lib/skyline/engine.rb +1 -1
- data/lib/skyline/has_many_referables_in.rb +4 -4
- data/lib/skyline/plugins/manager.rb +4 -3
- data/lib/skyline/plugins/plugin.rb +9 -1
- data/lib/skyline/rendering/renderer.rb +12 -2
- data/lib/skyline/sections/interface.rb +1 -1
- data/lib/skyline/taggable.rb +4 -4
- data/lib/skyline/version.rb +2 -2
- data/public/skyline/javascripts/src/application.js +1 -1
- data/public/skyline/javascripts/src/application_preinit.js +1 -0
- data/public/skyline/javascripts/src/library_uploader.js +1 -1
- data/public/skyline/javascripts/src/skyline.editor/src/skyline.editor.js +18 -18
- data/public/skyline/javascripts/src/skyline.editor/src/tinymce_preinit.js +4 -0
- data/public/skyline/javascripts/src/skyline.js +20 -18
- data/public/skyline/javascripts/src/skyline/src/skyline.js +2 -2
- data/public/skyline/javascripts/src/skyline/src/uploader.js +1 -1
- data/skylinecms.gemspec +7 -5
- data/test/factories.rb +2 -0
- data/test/integration/media_browser_super_access_test.rb +3 -3
- data/test/integration/media_browser_user_access_test.rb +2 -2
- data/test/integration/user_preferences_test.rb +5 -5
- data/test/test_helper.rb +1 -0
- data/test/unit/article_test.rb +1 -1
- data/test/unit/locales_test.rb +13 -3
- data/test/unit/test_content_object_test.rb +1 -1
- data/test/unit/test_section_test.rb +3 -2
- data/test/unit/user_preference_test.rb +2 -2
- data/vendor/digitpaint/nested_attributes_positioning.rb +5 -5
- metadata +66 -40
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @private
|
|
2
2
|
class Skyline::ArticleVersion < ActiveRecord::Base
|
|
3
|
-
|
|
3
|
+
self.table_name = "skyline_article_versions"
|
|
4
4
|
|
|
5
5
|
belongs_to :article, :class_name => "Skyline::Article", :foreign_key => "article_id"
|
|
6
6
|
belongs_to :data, :polymorphic => true, :dependent => :destroy
|
|
@@ -15,41 +15,41 @@ class Skyline::ArticleVersion < ActiveRecord::Base
|
|
|
15
15
|
|
|
16
16
|
accepts_nested_attributes_for :data
|
|
17
17
|
|
|
18
|
-
def build_data(
|
|
19
|
-
|
|
20
|
-
raise ArgumentError, "Missing class parameter when building data" unless
|
|
21
|
-
klass =
|
|
22
|
-
self.data = klass.constantize.new(
|
|
18
|
+
def build_data(*params, &block)
|
|
19
|
+
attrs = params.first.dup
|
|
20
|
+
raise ArgumentError, "Missing class parameter when building data" unless attrs["class"]
|
|
21
|
+
klass = attrs.delete("class")
|
|
22
|
+
self.data = klass.constantize.new(attrs)
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def to_text
|
|
26
26
|
self.sections.collect{|s| s.to_text}.join(" ").squeeze(" ")
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
def
|
|
30
|
-
super.tap do |
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
def dup
|
|
30
|
+
super.tap do |dup|
|
|
31
|
+
dup.created_at = nil
|
|
32
|
+
dup.updated_at = nil
|
|
33
|
+
dup.sections = self.sections.collect{|section| section.dup}
|
|
34
|
+
dup.data = self.data.dup
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
# Clones this object and makes it have another class
|
|
39
|
-
def
|
|
40
|
-
|
|
39
|
+
def dup_to_class(klass_or_proxy)
|
|
40
|
+
dup = klass_or_proxy.build
|
|
41
41
|
|
|
42
42
|
attrs = clone_attributes(:read_attribute_before_type_cast)
|
|
43
43
|
attrs.delete(self.class.primary_key)
|
|
44
44
|
attrs.delete("created_at")
|
|
45
45
|
attrs.delete("updated_at")
|
|
46
|
-
attrs["type"] =
|
|
47
|
-
|
|
46
|
+
attrs["type"] = dup.type
|
|
47
|
+
dup.send :instance_variable_set, '@attributes', attrs
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
dup.variant = self if dup.respond_to?(:variant) # only call clone.variant= for publications
|
|
50
|
+
dup.sections = self.sections.collect{|section| section.dup}
|
|
51
|
+
dup.data = self.data.dup
|
|
52
|
+
dup
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
# Custom data method that ensures that you always have
|
data/app/models/skyline/grant.rb
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# @private
|
|
2
2
|
class Skyline::MediaDir < Skyline::MediaNode
|
|
3
|
-
extend ActiveSupport::Memoizable
|
|
4
3
|
include UniqueIdentifiers
|
|
5
4
|
|
|
6
5
|
has_many :files, :foreign_key => "parent_id", :class_name => "Skyline::MediaFile"
|
|
@@ -14,7 +13,6 @@ class Skyline::MediaDir < Skyline::MediaNode
|
|
|
14
13
|
validate :only_one_root
|
|
15
14
|
|
|
16
15
|
class << self
|
|
17
|
-
extend ActiveSupport::Memoizable
|
|
18
16
|
# returns an Array of hashes
|
|
19
17
|
#
|
|
20
18
|
# ==== Returns
|
|
@@ -35,9 +35,9 @@ class Skyline::ObjectRef < Skyline::RefObject
|
|
|
35
35
|
self.referable_type == "Skyline::MediaFile"
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
-
def
|
|
39
|
-
super.tap do |
|
|
40
|
-
|
|
38
|
+
def dup
|
|
39
|
+
super.tap do |dup|
|
|
40
|
+
dup.referable = self.referable.dup if self.referable.kind_of?(Skyline::ReferableUri)
|
|
41
41
|
end
|
|
42
42
|
end
|
|
43
43
|
|
data/app/models/skyline/page.rb
CHANGED
|
@@ -54,7 +54,6 @@ class Skyline::Page < Skyline::Article
|
|
|
54
54
|
default_scope :order => "position"
|
|
55
55
|
|
|
56
56
|
class << self
|
|
57
|
-
extend ActiveSupport::Memoizable
|
|
58
57
|
|
|
59
58
|
def right_prefix
|
|
60
59
|
"page"
|
|
@@ -158,9 +157,8 @@ class Skyline::Page < Skyline::Article
|
|
|
158
157
|
end
|
|
159
158
|
|
|
160
159
|
def nesting
|
|
161
|
-
self.root? ? [self] : self.parent.nesting + [self]
|
|
160
|
+
@nesting ||= self.root? ? [self] : self.parent.nesting + [self]
|
|
162
161
|
end
|
|
163
|
-
memoize :nesting
|
|
164
162
|
|
|
165
163
|
def parents
|
|
166
164
|
self.nesting.dup[0..-2]
|
|
@@ -16,9 +16,11 @@ class Skyline::Publication < Skyline::ArticleVersion
|
|
|
16
16
|
raise ArgumentError, "variant_attributes must be an Hash" unless variant_attributes.kind_of?(Hash)
|
|
17
17
|
raise ArgumentError, "variant_attributes['name'] expected" unless variant_attributes.include?('name')
|
|
18
18
|
|
|
19
|
-
variant = self.
|
|
19
|
+
variant = self.dup_to_class(self.article.variants)
|
|
20
20
|
variant.attributes = variant_attributes
|
|
21
21
|
variant.variant_id = nil
|
|
22
|
+
variant.created_at = Time.now
|
|
23
|
+
variant.updated_at = Time.now
|
|
22
24
|
variant.save
|
|
23
25
|
variant
|
|
24
26
|
end
|
data/app/models/skyline/right.rb
CHANGED
data/app/models/skyline/role.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @private
|
|
2
2
|
class Skyline::Section < ActiveRecord::Base
|
|
3
|
-
|
|
3
|
+
self.table_name = "skyline_sections"
|
|
4
4
|
|
|
5
5
|
belongs_to :variant, :class_name => "Skyline::Variant"
|
|
6
6
|
belongs_to :article_version, :class_name => "Skyline::ArticleVersion"
|
|
@@ -12,16 +12,16 @@ class Skyline::Section < ActiveRecord::Base
|
|
|
12
12
|
|
|
13
13
|
default_scope :order => "position ASC"
|
|
14
14
|
|
|
15
|
-
def build_sectionable(
|
|
16
|
-
|
|
17
|
-
raise ArgumentError, "Missing class parameter when building sectionable" unless
|
|
18
|
-
klass =
|
|
19
|
-
self.sectionable = klass.constantize.new(
|
|
15
|
+
def build_sectionable(*params, &block)
|
|
16
|
+
attrs = params.first.dup
|
|
17
|
+
raise ArgumentError, "Missing class parameter when building sectionable" unless attrs["class"]
|
|
18
|
+
klass = attrs.delete("class")
|
|
19
|
+
self.sectionable = klass.constantize.new(attrs)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
def
|
|
23
|
-
super.tap do |
|
|
24
|
-
|
|
22
|
+
def dup
|
|
23
|
+
super.tap do |dup|
|
|
24
|
+
dup.sectionable = dup.sectionable.dup
|
|
25
25
|
end
|
|
26
26
|
end
|
|
27
27
|
|
|
@@ -31,9 +31,9 @@ class Skyline::Sections::ContentCollectionSection < ActiveRecord::Base
|
|
|
31
31
|
self.content_class.published.with_tags(self.tags).scoped(:limit => self.number)
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
def
|
|
35
|
-
super.tap do |
|
|
36
|
-
|
|
34
|
+
def dup
|
|
35
|
+
super.tap do |dup|
|
|
36
|
+
dup.associated_tags = self.associated_tags.collect{|associated_tag| associated_tag.dup}
|
|
37
37
|
end
|
|
38
38
|
end
|
|
39
39
|
end
|
|
@@ -9,9 +9,9 @@ class Skyline::Sections::LinkSection < ActiveRecord::Base
|
|
|
9
9
|
|
|
10
10
|
accepts_nested_attributes_for :links, :allow_destroy => true
|
|
11
11
|
|
|
12
|
-
def
|
|
13
|
-
super.tap do |
|
|
14
|
-
|
|
12
|
+
def dup
|
|
13
|
+
super.tap do |dup|
|
|
14
|
+
dup.links = self.links.collect{|link| link.dup}
|
|
15
15
|
end
|
|
16
16
|
end
|
|
17
17
|
|
|
@@ -27,6 +27,8 @@ class Skyline::Sections::MediaSection < ActiveRecord::Base
|
|
|
27
27
|
width = self[:width].to_i
|
|
28
28
|
height = self[:height].to_i
|
|
29
29
|
if self.media.present?
|
|
30
|
+
width = media.width if self[:width].blank?
|
|
31
|
+
height = media.height if self[:height].blank?
|
|
30
32
|
proportional = self.media.proportional_dimension(width,height)
|
|
31
33
|
if proportional
|
|
32
34
|
width,height = proportional
|
|
@@ -2,7 +2,6 @@ require 'fileutils'
|
|
|
2
2
|
|
|
3
3
|
# @private
|
|
4
4
|
class Skyline::Sections::RssSection < ActiveRecord::Base
|
|
5
|
-
extend ActiveSupport::Memoizable
|
|
6
5
|
extend UrlValidation
|
|
7
6
|
include Skyline::Sections::Interface
|
|
8
7
|
|
|
@@ -31,15 +30,10 @@ class Skyline::Sections::RssSection < ActiveRecord::Base
|
|
|
31
30
|
self.rss_feed ? self.rss_feed[:items] : []
|
|
32
31
|
end
|
|
33
32
|
|
|
34
|
-
protected
|
|
35
|
-
def cache_file
|
|
36
|
-
File.join(self.class.cache_path, "#{self.id}.yml")
|
|
37
|
-
end
|
|
38
|
-
memoize :cache_file
|
|
39
|
-
|
|
40
33
|
def rss_feed
|
|
34
|
+
return @rss_feed unless @rss_feed === nil
|
|
41
35
|
if File.exists?(self.cache_file) && File.mtime(self.cache_file) > Time.now - self.cache_timeout
|
|
42
|
-
YAML.load(File.read(self.cache_file))
|
|
36
|
+
@rss_feed = YAML.load(File.read(self.cache_file))
|
|
43
37
|
else
|
|
44
38
|
if feed = fetch_feed
|
|
45
39
|
results = RSS::Parser.parse(feed, false)
|
|
@@ -66,20 +60,25 @@ class Skyline::Sections::RssSection < ActiveRecord::Base
|
|
|
66
60
|
} if i < self.show_count
|
|
67
61
|
end
|
|
68
62
|
cache(feed)
|
|
69
|
-
feed
|
|
63
|
+
@rss_feed = feed
|
|
70
64
|
elsif File.exists?(self.cache_file)
|
|
71
65
|
self.reset_mtime
|
|
72
|
-
YAML.load(File.read(self.cache_file))
|
|
66
|
+
@rss_feed = YAML.load(File.read(self.cache_file))
|
|
73
67
|
else
|
|
74
|
-
false
|
|
68
|
+
@rss_feed = false
|
|
75
69
|
end
|
|
76
70
|
end
|
|
71
|
+
@rss_feed
|
|
77
72
|
# rescue
|
|
78
73
|
# logger.error "[RssSection] Failed to parse feed!"
|
|
79
74
|
# false
|
|
80
75
|
end
|
|
81
|
-
memoize :rss_feed
|
|
82
76
|
|
|
77
|
+
protected
|
|
78
|
+
def cache_file
|
|
79
|
+
@cache_file ||= File.join(self.class.cache_path, "#{self.id}.yml")
|
|
80
|
+
end
|
|
81
|
+
|
|
83
82
|
def fetch_feed
|
|
84
83
|
logger.debug "[RssSection] Fetching feed #{self.url}"
|
|
85
84
|
feed = open(self.url).read
|
data/app/models/skyline/tag.rb
CHANGED
data/app/models/skyline/user.rb
CHANGED
|
@@ -1,202 +1,213 @@
|
|
|
1
|
-
class Skyline::User < ActiveRecord::Base
|
|
2
|
-
include Skyline::Authentication::User
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
has_many :grants, :class_name => "Skyline::Grant", :dependent => :delete_all
|
|
6
|
-
has_many :roles, :class_name => "Skyline::Role", :through => :grants
|
|
7
|
-
has_many :rights, :class_name => "Skyline::Right",
|
|
8
|
-
:finder_sql => proc{ "SELECT DISTINCT r.* FROM skyline_rights AS r, skyline_grants AS g JOIN skyline_rights_skyline_roles AS rr ON rr.role_id = g.role_id WHERE g.user_id = #{id} AND r.id = rr.right_id" }
|
|
9
|
-
|
|
10
|
-
# This must be set to change the password (by the user himself)
|
|
11
|
-
attr_accessor :current_password
|
|
12
|
-
|
|
13
|
-
# This can be set by the admin to change the PW
|
|
14
|
-
attr_reader :force_password
|
|
15
|
-
attr_protected :force_password
|
|
16
|
-
|
|
17
|
-
# This can be set by the admin to force checking of the current password etc
|
|
18
|
-
attr_accessor :editing_myself
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
validates_presence_of :
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
#
|
|
50
|
-
#
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
user
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
#
|
|
91
|
-
#
|
|
92
|
-
#
|
|
93
|
-
#
|
|
94
|
-
#
|
|
95
|
-
#
|
|
96
|
-
#
|
|
97
|
-
#
|
|
98
|
-
#
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
@rights.
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
#
|
|
128
|
-
#
|
|
129
|
-
#
|
|
130
|
-
#
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
#
|
|
138
|
-
#
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
pw
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
#
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
#
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
Skyline::Role.all
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
def
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
1
|
+
class Skyline::User < ActiveRecord::Base
|
|
2
|
+
include Skyline::Authentication::User
|
|
3
|
+
|
|
4
|
+
self.table_name = "skyline_users"
|
|
5
|
+
has_many :grants, :class_name => "Skyline::Grant", :dependent => :delete_all
|
|
6
|
+
has_many :roles, :class_name => "Skyline::Role", :through => :grants
|
|
7
|
+
has_many :rights, :class_name => "Skyline::Right",
|
|
8
|
+
:finder_sql => proc{ "SELECT DISTINCT r.* FROM skyline_rights AS r, skyline_grants AS g JOIN skyline_rights_skyline_roles AS rr ON rr.role_id = g.role_id WHERE g.user_id = #{id} AND r.id = rr.right_id" }
|
|
9
|
+
|
|
10
|
+
# This must be set to change the password (by the user himself)
|
|
11
|
+
attr_accessor :current_password
|
|
12
|
+
|
|
13
|
+
# This can be set by the admin to change the PW
|
|
14
|
+
attr_reader :force_password
|
|
15
|
+
attr_protected :force_password
|
|
16
|
+
|
|
17
|
+
# This can be set by the admin to force checking of the current password etc
|
|
18
|
+
attr_accessor :editing_myself
|
|
19
|
+
|
|
20
|
+
attr_accessor :skip_email_validation
|
|
21
|
+
|
|
22
|
+
# Validations
|
|
23
|
+
validate :valid_current_password, :if => :editing_myself, :on => :update
|
|
24
|
+
|
|
25
|
+
validates_presence_of :password, :if => :password_changed?
|
|
26
|
+
validates_presence_of :password_confirmation, :message => :confirmation, :if => :password_changed?, :on => :update
|
|
27
|
+
validates_confirmation_of :password, :message => :confirmation, :if => :password_changed?
|
|
28
|
+
|
|
29
|
+
validates_confirmation_of :force_password, :message => :confirmation, :if => lambda{|user| user.force_password.present?}
|
|
30
|
+
|
|
31
|
+
validates_presence_of :email
|
|
32
|
+
validate :valid_email_address
|
|
33
|
+
validates_uniqueness_of :email, :unless => :skip_email_validation
|
|
34
|
+
|
|
35
|
+
validate :grants_didnt_change, :if => :editing_myself
|
|
36
|
+
|
|
37
|
+
before_validation :reset_password_on_empty_current_password, :on => :update
|
|
38
|
+
before_save :set_forced_password,:encrypt_password
|
|
39
|
+
|
|
40
|
+
default_scope :order => "email ASC"
|
|
41
|
+
|
|
42
|
+
accepts_nested_attributes_for :grants
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class << self
|
|
46
|
+
|
|
47
|
+
# Authenticates a user with email and password
|
|
48
|
+
#
|
|
49
|
+
# ==== Returns
|
|
50
|
+
# User:: The user if authentication passed
|
|
51
|
+
# --
|
|
52
|
+
def authenticate(email,password)
|
|
53
|
+
user = self.first(:conditions => {:email => email.to_s.downcase, :password => encrypt(password.to_s), :is_destroyed => false})
|
|
54
|
+
user && user || false
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def find_by_identification(identification)
|
|
58
|
+
self.find_by_id(identification)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def encrypt(pw)
|
|
62
|
+
Digest::SHA1.hexdigest(pw)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def extract_valid_email_address(email)
|
|
66
|
+
if email.kind_of? Mail::Address
|
|
67
|
+
return email.address
|
|
68
|
+
else
|
|
69
|
+
begin
|
|
70
|
+
address = Mail::Address.new(email.to_s)
|
|
71
|
+
rescue
|
|
72
|
+
return false
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
if address && address.respond_to?(:domain) && address.domain
|
|
76
|
+
return address.address
|
|
77
|
+
else
|
|
78
|
+
return false
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def per_page; 30; end
|
|
83
|
+
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def identification
|
|
87
|
+
self.id
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Check if a user has a specific right
|
|
91
|
+
#
|
|
92
|
+
# ==== Parameters
|
|
93
|
+
# right_or_class<String,Symbol,~right_prefix>::
|
|
94
|
+
# Can be a string or a symbol to check a specific right, or you can pass
|
|
95
|
+
# an object that responds to #right_prefix, right_prefix must return a string
|
|
96
|
+
# suffix<String,Symbol>,::
|
|
97
|
+
# A suffix to append to the right, mostly usefull in combination with an object
|
|
98
|
+
#
|
|
99
|
+
# ==== Returns
|
|
100
|
+
#<Boolean>:: true if the right exists in one of the roles fo the user
|
|
101
|
+
# --
|
|
102
|
+
def allow?(right_or_class,suffix=nil)
|
|
103
|
+
if right_or_class.respond_to?(:right_prefix)
|
|
104
|
+
right_or_class = right_or_class.right_prefix
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
right = [right_or_class,suffix].compact.map(&:to_s).join("_")
|
|
108
|
+
|
|
109
|
+
@rights ||= self.rights.map{|r| r.name }.uniq
|
|
110
|
+
@rights.include?(right)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Check if this users password matches.
|
|
114
|
+
def correct_password?(password)
|
|
115
|
+
if self.password_changed?
|
|
116
|
+
self.password == password.to_s
|
|
117
|
+
else
|
|
118
|
+
self.password == self.class.encrypt(password.to_s)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def current_password=(v)
|
|
123
|
+
changed_attributes.delete("password") if v.blank?
|
|
124
|
+
@current_password = v
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Generate a new password, set it and return it.
|
|
128
|
+
#
|
|
129
|
+
# ==== Returns
|
|
130
|
+
# String:: The newly set password
|
|
131
|
+
# --
|
|
132
|
+
def generate_new_password!
|
|
133
|
+
pw = SimplePassword.new(8).to_s
|
|
134
|
+
self.force_password!(pw)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Forcefully set a password without any validations
|
|
138
|
+
# Directly saves the object.
|
|
139
|
+
# --
|
|
140
|
+
def force_password!(pw)
|
|
141
|
+
self.update_attribute(:password, pw)
|
|
142
|
+
pw
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def force_password=(pw)
|
|
146
|
+
@force_password = pw if pw.present?
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Display the name or e-mailaddress of the user for
|
|
150
|
+
# display purposes.
|
|
151
|
+
def display_name
|
|
152
|
+
self.name.present? ? self.name : self.email
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Don't really destroy the object, just
|
|
156
|
+
# set the is_destroyed? flag.
|
|
157
|
+
def destroy
|
|
158
|
+
unless new_record?
|
|
159
|
+
self.update_attributes(:is_destroyed => true)
|
|
160
|
+
self.grants.collect {|g| g.destroy }
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
freeze
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def viewable_roles
|
|
167
|
+
if self.system?
|
|
168
|
+
Skyline::Role.all
|
|
169
|
+
else
|
|
170
|
+
Skyline::Role.all(:conditions => {:system => false})
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Reactivate user with these attributes, if they are valid
|
|
175
|
+
def reactivate(attributes)
|
|
176
|
+
temp_user = Skyline::User.new(attributes)
|
|
177
|
+
temp_user.skip_email_validation = true
|
|
178
|
+
if temp_user.valid?
|
|
179
|
+
self.attributes = attributes
|
|
180
|
+
self.force_password! attributes[:password]
|
|
181
|
+
self.is_destroyed = false
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
protected
|
|
186
|
+
|
|
187
|
+
def set_forced_password
|
|
188
|
+
self.password = @force_password if @force_password.present?
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def encrypt_password
|
|
192
|
+
return unless self.password_changed?
|
|
193
|
+
self.password = self.class.encrypt(self.password.to_s)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def valid_current_password
|
|
197
|
+
unless self.current_password && self.password_was == self.class.encrypt(self.current_password)
|
|
198
|
+
self.errors.add(:current_password, :mismatch)
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def reset_password_on_empty_current_password
|
|
203
|
+
changed_attributes.delete("password") if self.current_password.blank?
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def valid_email_address
|
|
207
|
+
return self.errors.add(:email,:invalid) if !Skyline::User.extract_valid_email_address(self.email)
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def grants_didnt_change
|
|
211
|
+
self.errors.add :grants, :changed if self.grants.detect{|g| g.changed?}
|
|
212
|
+
end
|
|
213
|
+
end
|