Class: Node
- Inherits:
-
ActiveRecord::Base
- Object
- ActiveRecord::Base
- Node
- Extended by:
- Zena::Acts::Secure, Zena::Acts::SecureNode, Zena::Use::Search::NodeClassMethods, Zena::Use::Upload::UploadedFile
- Includes:
- Property, Property::Serialization::JSON, RubyLess, Versions::Multi, Zena::Acts::Enrollable::ModelMethods, Zena::Acts::Serializable::ModelMethods, Zena::Use::Ancestry::ModelMethods, Zena::Use::Dates::ModelMethods, Zena::Use::FieldIndex::ModelMethods, Zena::Use::Fulltext::ModelMethods, Zena::Use::Kpath::InstanceMethods, Zena::Use::MLIndex::ModelMethods, Zena::Use::NestedAttributesAlias::ModelMethods, Zena::Use::PropEval::ModelMethods, Zena::Use::QueryNode::ModelMethods, Zena::Use::Relations::ModelMethods, Zena::Use::ScopeIndex::ModelMethods, Zena::Use::VersionHash::ModelMethods, Zena::Use::Workflow
- Defined in:
- app/models/node.rb
Overview
A Node is the root class of all elements in the zena application. Class inheritance diagram:
FIXME: some parts are not correct (Partial, Task, Request, Milestone). Either correct this tree or add these classes. Node (manages access and publication cycle)
|
+-- Page (web pages)
| |
| +--- Project (has it's own project_id. Can contain notes, collaborators, etc)
| |
| +--- Section (has it's own section_id = group of pages)
| |
| +--- Skin (theme: contains css, templates, etc)
|
+--- Document
| |
| +--- Image
| |
| +--- TextDocument (for css, scripts)
| |
| +--- Partial (uses the zafu templating language)
| |
| +--- Template (entry for rendering)
|
+-- Note (date related information, event)
|
+--- Post (blog entry)
Properties
The Version class stores the node’s properties (attributes). You need to declare the attributes either in the virtual class or as a Role attached to an existing class in order to use them.
Attributes
Each node uses the following basic attributes:
Base attributes:
- zip
-
unique id (incremented in each site’s scope).
- _id
-
cached title (used to identify nodes in DB: not used in Zena)
- site_id
-
site to which this node belongs to.
- parent_id
-
parent node (every node except root is inserted in a unique place through this attribute).
- user_id
-
creator of the node.
- ref_lang
-
original node language.
- created_at
-
creation date.
- updated_at
-
modification date.
- log_at
-
announcement date.
- event_at
-
event date.
- custom_base
-
boolean value. When set to true, the node’s url becomes it’s fullpath. All it descendants will use this node’s fullpath as their base url. See below for an example.
- inherit
-
inheritance mode (0=custom, 1=inherit, -1=private).
Attributes inherited from the parent:
- section_id
-
reference project (cannot be overwritten even if inheritance mode is custom).
- rgroup_id
-
id of the readers group.
- wgroup_id
-
id of the writers group.
- dgroup_id
-
id of the publishers group.
- skin_id
-
Skin to use when rendering the page (‘theme’).
Attributes used internally:
- publish_from
-
earliest publication date from all published versions.
- kpath
-
inheritance hierarchy. For example an Image has ‘NPDI’ (Node, Page, Document, Image), a Letter would have ‘NNTL’ (Node, Note, Task. Letter). This is used to optimize sql queries.
- fullpath
-
cached full path made of ancestors’ zip (<gdparent zip>/<parent zip>/<self zip>).
- basepath
-
cached base path (the base path is used to build the url depending on the ‘custom_base’ flag).
Node url
A node’s url is made of it’s class and zip
. For the examples below, this is our site tree:
root
|
+--- projects (Page)
|
+--- worldTour (Project)
| |
| +--- photos (Page)
|
+--- music (Project)
The worldTour project’s url would look like:
/en/project21.html
The ‘photos’ url would be:
/en/page23.html
When custom base is set (only for descendants of Page), worldTour url becomes its fullpath:
/en/projects/worldTour
and the ‘photos’ url is now in the worldTour project’s basepath:
/en/projects/worldTour/page23.html
Setting ‘custom_base’ on a node should be done with caution as the node’s zip is on longer in the url and when you move the node around, there is no way to find the new location from the old url. Custom_base should therefore only be used for nodes that are not going to move.
Constant Summary collapse
- VERSION_ATTRIBUTES =
List of version attributes that should be accessed as proxies ‘v_lang’, ‘v_status’, etc
%w{status lang publish_from backup}
- Caster =
::ActiveRecord::ConnectionAdapters::Column
- @@native_node_classes =
{'N' => self}
- @@native_node_classes_by_name =
{'Node' => self}
- @@unhandled_children =
[]
Constants included from Zena::Use::Workflow
Zena::Use::Workflow::WORKFLOW_ATTRIBUTES
Class Method Summary collapse
-
.allowed_change_to_classes ⇒ Object
List of classes that a node can change to.
-
.author_proc ⇒ Object
Dynamic resolution of the author class from the user prototype.
- .auto_create_discussion ⇒ Object
-
.cast_to_class(type) ⇒ Object
Return class of cast value.
-
.change_to_classes_for_form ⇒ Object
Class list to which this class can change to.
-
.class_for_relation(rel) ⇒ Object
FIXME: Where is this used ?.
-
.classes_for_form(opts = {}) ⇒ Object
TODO: remove and use VirtualClass.classes_for_form directly.
-
.create_node(new_attributes, transform = true) ⇒ Object
TODO: cleanup and rename with something indicating the attrs cleanup that this method does.
-
.create_nodes_from_folder(opts) ⇒ Object
Create new nodes from the data in a folder or archive.
-
.create_or_update_node(new_attributes) ⇒ Object
def attr_public?(attribute) if attribute.to_s =~ /(.*)_zips?$/ return true if self.ancestors.include?(Node) && RelationProxy.find_by_role($1.singularize) end super end.
- .extract_archive(archive) ⇒ Object
- .find_by_parent_title_and_kpath(parent_id, title, kpath = nil, opts = {}) ⇒ Object
-
.find_by_title(title, opts = {}) ⇒ Object
Find node by the indexed title.
- .find_by_zip(zip) ⇒ Object
-
.find_node_by_pseudo(id, base_node = nil) ⇒ Object
Find a node based on a query shortcut.
- .get_attributes_from_yaml(filepath, base_node = nil) ⇒ Object
-
.get_class(rel, opts = {}) ⇒ Object
Return class or virtual class from name.
-
.get_role(rel) ⇒ Object
Find a role by name.
- .inherited(child) ⇒ Object
-
.inspect ⇒ Object
Seeing all the columns of the Node class on every inspect does not help at all.
-
.kpath_match?(kpath) ⇒ Boolean
check inheritance chain through kpath.
-
.kpaths_for_form(opts = {}) ⇒ Object
FIXME: how to make sure all sub-classes of Node are loaded before this is called ? TODO: move into helper.
- .load_unhandled_children ⇒ Object
-
.make_schema ⇒ Object
We want to use a Role as schema for properties defined in the real_class instead of Property::Schema.
-
.native_classes ⇒ Object
Return the list of (kpath,subclasses) for the current class.
-
.native_classes_by_name ⇒ Object
Return the list of (name,class) for the current class.
- .new(hash = {}, vclass = nil) ⇒ Object (also: new_instance)
-
.new_node(new_attributes, transform = true) ⇒ Object
TODO: cleanup and rename with something indicating the attrs cleanup that this method does.
- .plural_relation?(rel) ⇒ Boolean
-
.transform_attributes(new_attributes, base_node = nil, change_timezone = true, is_link = false) ⇒ Object
Translate attributes from the visitor’s reference to the application.
-
.translate_pseudo_id(id, sym = :id, base_node = nil) ⇒ Object
Find a node’s attribute based on a pseudo (id or path).
-
.zafu_attribute(node, attribute) ⇒ Object
Return a safe string to access node attributes in compiled templates and compiled sql.
Instance Method Summary collapse
-
#all_relations ⇒ Object
Used by zafu to find the search score def score self end.
-
#archive ⇒ Object
create a ‘tgz’ archive with node content and children, returning temporary file path.
-
#asset_path(asset_filename) ⇒ Object
Return save path for an asset (element produced by text like a png file from LateX).
- #auth ⇒ Object
- #auth=(params) ⇒ Object
-
#auth_user ⇒ Object
Find first user using this node as contact node.
-
#auth_users ⇒ Object
Find all users using this node as contact node.
-
#author ⇒ Object
ACCESSORS.
-
#can_auto_create_discussion? ⇒ Boolean
Automatically create a discussion if any of the following conditions are met: - there already exists an
outside
,open
discussion for another language - the node is not published (creates an internal discussion) - the user has drive access to the node. -
#can_comment? ⇒ Boolean
Return true if it is allowed to add comments to the node in the current context.
-
#comments ⇒ Object
Comments for the current context.
-
#comments_count ⇒ Object
TODO: remove, replace by relation proxy: proxy.count…
-
#content_lang ⇒ Object
Return the code language used for syntax highlighting.
-
#data ⇒ Object
Find all data entries linked to the current node.
-
#discussion ⇒ Object
Find the discussion for the current context (v_status and v_lang).
- #dyn_attribute_keys ⇒ Object
-
#empty? ⇒ Boolean
Include data entry verification in multiversion’s empty? method.
-
#export_keys ⇒ Object
List of attribute keys to export in a zml file.
-
#export_to_folder(path) ⇒ Object
export node content and children into a folder.
-
#find_node_by_pseudo(string, base_node = nil) ⇒ Object
This is needed during ‘unparse_assets’ when the node is it’s own helper.
-
#get_discussion_id ⇒ Object
Return current discussion id (used by query_builder).
-
#get_project_id ⇒ Object
Return self if the node is a kind of Project.
-
#get_section_id ⇒ Object
Return self if the node is a kind of Section.
- #klass ⇒ Object
- #klass=(str) ⇒ Object
- #klass_changed? ⇒ Boolean
-
#kpath_match?(kpath) ⇒ Boolean
check inheritance chain through kpath.
- #login_info ⇒ Object
- #m_author ⇒ Object
- #m_author=(str) ⇒ Object
-
#m_text ⇒ Object
FIXME: use nested_attributes_alias and try to use native Rails to create the comment comment_attributes=, …
- #m_text=(str) ⇒ Object
- #m_title ⇒ Object
- #m_title=(str) ⇒ Object
-
#merge_multi_errors(key, object) ⇒ Object
This is an adaptation of Versions::Multi code to use our special v_ shortcut to access version attributes.
-
#new_child(opts = {}, transform = true) ⇒ Object
Create a child and let him inherit from rwp groups and section_id.
- #o_skin ⇒ Object
- #o_user ⇒ Object
-
#parent(is_secure = true) ⇒ Object
Find parent.
-
#parent_zip ⇒ Object
Id to zip mapping for parent_id.
-
#parent_zip=(zip) ⇒ Object
When setting parent trough controllers, we receive parent_zip=.
-
#parse_assets(text, helper, key) ⇒ Object
Parse text content and replace all relative urls (‘../projects/art’) by ids (‘34’).
-
#parse_keys ⇒ Object
List of attribute keys to transform (change references, etc).
-
#project ⇒ Object
Return self if the current node is a project else find project.
-
#project_zip ⇒ Object
Id to zip mapping for project_id.
-
#rcast(key, type) ⇒ Object
Read with cast to an appropriate instance.
-
#real_project(is_secure = true) ⇒ Object
Find real project (Project’s project if node is a Project).
-
#real_section(is_secure = true) ⇒ Object
Find real section.
-
#reload ⇒ Object
Remove loaded version and properties on reload.
-
#replace_attributes_in_values(hash) ⇒ Object
Replace [id], [title], etc in attributes values.
-
#safe_method_type(signature, receiver = nil) ⇒ Object
We use the virtual_class as proxy for method type resolution.
-
#safe_send(method) ⇒ Object
Safe dynamic method dispatching when the method is not known during compile time.
-
#section ⇒ Object
Return self if the current node is a section else find section.
-
#section_zip ⇒ Object
Id to zip mapping for section_id.
- #skin ⇒ Object
- #skin_zip ⇒ Object
-
#skin_zip=(zip) ⇒ Object
When setting skin trough controllers, we receive skin_zip=.
- #sweep_cache(filter = nil) ⇒ Object
-
#to_yaml ⇒ Object
export node as a hash.
-
#unparse_assets(text, helper, key) ⇒ Object
Parse text and replace ids ‘!30!’ by their pseudo path ‘!(img/bird)!’ key is used in TextDocument overloaded method.
-
#update_attributes_with_transformation(new_attributes, change_timezone = true) ⇒ Object
Update a node’s attributes, transforming the attributes first from the visitor’s context to Node context.
-
#user ⇒ Object
TODO: why do we need secure here ?.
-
#user_zip ⇒ Object
Id to zip mapping for user_id.
- #v_number ⇒ Object
-
#vclass ⇒ Object
virtual class FIXME: alias vclass to virtual_class alias vclass virtual_class.
-
#versions_with_secure(*args) ⇒ Object
TODO: remove when :inverse_of works.
- #virtual_class ⇒ Object (also: #schema)
- #virtual_class=(vclass) ⇒ Object
-
#vkind_of?(klass) ⇒ Boolean
include virtual classes to check inheritance chain.
-
#zafu_eval(str, opts = {}) ⇒ Object
Enable dynamic property evaluation.
- #zafu_versions ⇒ Object
Methods included from Zena::Use::Search::NodeClassMethods
match_query, search_index, search_records, search_text
Methods included from Zena::Acts::Secure
secure_scope, secure_write_scope, visitor=
Methods included from Zena::Acts::Secure::SecureResult
#construct_id_map, #secure_result
Methods included from Zena::Acts::SecureNode
Methods included from Zena::Use::Dates::ModelMethods
Methods included from Zena::Use::Dates::Common
Methods included from Zena::Use::QueryNode::ModelMethods
#db_attr, #find, included, #safe_first, #start_node_zip
Methods included from Zena::Use::ScopeIndex::ModelMethods
included, #rebuild_index_with_scope_index!, #rebuild_scope_index!, #scope_index, scope_index_proc
Methods included from Zena::Use::Relations::ModelMethods
#add_link, included, #l_comment, #l_comment=, #l_date, #l_date=, #l_status, #l_status=, #link_id, #link_id=, #linked_node, #linked_node=, #rel, #rel=, #rel_attributes=, #relation_alias, #relation_links, #relation_proxy, #relation_proxy_from_link, #relations_for_form, #remove_link
Methods included from Zena::Use::Fulltext::ModelMethods
included, #rebuild_index_for_version_with_fulltext
Methods included from Zena::Use::PropEval::ModelMethods
included, #merge_prop_eval, #need_set__id, #rebuild_index_for_version_with_prop_eval, #set__id
Methods included from Zena::Use::VersionHash::ModelMethods
#rebuild_vhash, #v_public?, #version, #version_id, #vhash, #visible_versions
Methods included from Zena::Acts::Serializable::ModelMethods
#all_link_ids, #default_serialization_options, #export_ids, #export_properties, included, #to_xml
Methods included from Zena::Use::Ancestry::ModelMethods
#ancestors, #basepath, #fullpath_as_title, included, #is_ancestor?, #pseudo_id, #short_path, #z_ancestors
Methods included from Zena::Use::Workflow
#apply, #apply_with_callbacks, #auto_publish?, #can_apply?, #can_destroy_version?, #can_edit?, #can_propose?, #can_publish?, #can_refuse?, #can_remove?, #can_unpublish?, #can_update?, #destroy_version, #edit_content!, #get_publish_from, included, #propose, #publish, #redit, #refuse, #remove, #save_without_clone, #set_current_transition, #traductions, #transition_allowed?, #transition_for, #unpublish, #update_attributes, #update_attributes_without_clone, #would_change_original?
Methods included from Zena::Use::NestedAttributesAlias::ModelMethods
#attributes_with_nested_alias=, included, #nested_model_names_for_alias, #resolve_attributes_alias
Methods included from Zena::Acts::Enrollable::ModelMethods
#assigned_roles, #has_role?, included, #zafu_possible_roles
Methods included from Zena::Use::FieldIndex::ModelMethods
included, #property_field_index
Methods included from Zena::Use::MLIndex::ModelMethods
included, #index_reader, #rebuild_index_for_version, #rebuild_index_with_multi_lingual!
Methods included from Zena::Use::Kpath::InstanceMethods
Dynamic Method Handling
This class handles dynamic methods through the method_missing method in the class Zena::Use::Relations::ModelMethods
Class Method Details
.allowed_change_to_classes ⇒ Object
List of classes that a node can change to.
471 472 473 |
# File 'app/models/node.rb', line 471 def allowed_change_to_classes change_to_classes_for_form.map {|k,v| v} end |
.author_proc ⇒ Object
Dynamic resolution of the author class from the user prototype
201 202 203 204 205 206 207 208 209 210 211 |
# File 'app/models/node.rb', line 201 def self. Proc.new do |h, r, s| res = {:method => 'author', :nil => true} if prototype = visitor.prototype res[:class] = prototype.vclass else res[:class] = VirtualClass['Node'] end res end end |
.auto_create_discussion ⇒ Object
1000 1001 1002 |
# File 'app/models/node.rb', line 1000 def auto_create_discussion false end |
.cast_to_class(type) ⇒ Object
Return class of cast value.
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 |
# File 'app/models/node.rb', line 310 def self.cast_to_class(type) case type when :string then String when :text then String when :integer then Number when :float then Number when :decimal then Number when :datetime then Time when :timestamp then Time when :time then Time when :date then Time when :binary then String when :boolean then Boolean else nil end end |
.change_to_classes_for_form ⇒ Object
Class list to which this class can change to
466 467 468 |
# File 'app/models/node.rb', line 466 def change_to_classes_for_form classes_for_form(:class => 'Node', :without => 'Document') end |
.class_for_relation(rel) ⇒ Object
FIXME: Where is this used ?
882 883 884 885 886 887 888 889 890 891 892 893 |
# File 'app/models/node.rb', line 882 def class_for_relation(rel) case rel when 'author' User when 'traductions' Version when 'versions' Version else Node end end |
.classes_for_form(opts = {}) ⇒ Object
TODO: remove and use VirtualClass.classes_for_form directly
476 477 478 |
# File 'app/models/node.rb', line 476 def classes_for_form(opts={}) VirtualClass[self.name].classes_for_form(opts) end |
.create_node(new_attributes, transform = true) ⇒ Object
TODO: cleanup and rename with something indicating the attrs cleanup that this method does.
661 662 663 664 665 666 667 |
# File 'app/models/node.rb', line 661 def create_node(new_attributes, transform = true) node = new_node(new_attributes, transform) if node.errors.empty? node.save end node end |
.create_nodes_from_folder(opts) ⇒ Object
Create new nodes from the data in a folder or archive.
670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 |
# File 'app/models/node.rb', line 670 def create_nodes_from_folder(opts) # TODO: all this needs refactoring (and moved into a module). # It's probably the messiest part of Zena. return [] unless (opts[:folder] || opts[:archive]) && (opts[:parent] || opts[:parent_id]) scope = self.scoped_methods[0] || {} parent_id = opts[:parent_id] || opts[:parent][:id] folder = opts[:folder] defaults = (opts[:defaults] || {}).stringify_keys # Initial object class klass = opts[:class] || opts[:klass] || "Page" res = {} unless folder # Create from archive res = nil extract_archive(opts[:archive]) do |folder| res = create_nodes_from_folder(:folder => folder, :parent_id => parent_id, :defaults => defaults, :klass => klass) end return res end entries = Dir.entries(folder).reject { |f| f =~ /^[\.~]|^__/ }.map do |filename| [filename, String.from_filename(filename)] end.sort index = 0 while entries[index] catch (:next_entry) do type = current_obj = sub_folder = document_path = nil versions = [] filename, title = *entries[index] path = File.join(folder, filename) if File.stat(path).directory? type = :folder sub_folder = path attrs = defaults.dup attrs['v_lang'] ||= visitor.lang elsif title =~ /^(.+?)(\.\w\w|)(\.\d+|)\.zml$/ # bird.jpg.en.zml # node content in yaml type = :node title = "#{$1}#{$4}" lang = $2.blank? ? nil : $2[1..-1] # no need for base_node (this is done after all with parse_assets in the controller) attrs = defaults.merge(get_attributes_from_yaml(path)) attrs['title'] = title attrs['v_lang'] = lang || attrs['v_lang'] || visitor.lang versions << attrs elsif title =~ /^((.+?)\.(.+?))(\.\w\w|)(\.\d+|)$/ # bird.jpg.en type = :document title = $1 attrs = defaults.dup lang = $4.blank? ? nil : $4[1..-1] attrs['v_lang'] = lang || attrs['v_lang'] || visitor.lang attrs['ext'] = $3 document_path = path else # Document without extension type = :document attrs = defaults.dup lang = nil attrs['v_lang'] = lang || attrs['v_lang'] || visitor.lang attrs['ext'] = 'bin' document_path = path end index += 1 while entries[index] && entries[index][1] =~ /^#{title}(\.\w\w|)(\.\d+|)\.zml$/ # bird.jpg.en.zml lang = $1.blank? ? visitor.lang : $1[1..-1] path = File.join(folder,entries[index][0]) # we have a zml file. Create a version with this file # no need for base_node (this is done after all with parse_assets in the controller) attrs = defaults.merge(get_attributes_from_yaml(path)) attrs['title'] ||= title attrs['v_lang'] ||= lang versions << attrs index += 1 end if versions.empty? if type == :folder # minimal node for a folder attrs['title'] = title attrs['v_lang'] ||= lang attrs['class'] = klass versions << attrs elsif type == :document # minimal node for a document attrs['title'] = title attrs['v_lang'] ||= lang versions << attrs end end new_object = false versions.each do |attrs| # FIXME: same lang: remove before update current_obj.remove if current_obj.v_lang == attrs['v_lang'] && current_obj.v_status != Zena::Status::Red # FIXME: current_obj.publish if attrs['v_status'].to_i == Zena::Status::Pub if type == :document attrs['title' ] = attrs['title'].split('.')[0..-2].join('.') if document_path attrs['ext'] ||= document_path.split('.').last # file insert_zafu_headings = false if opts[:parent_class] == 'Skin' if ['html','xhtml'].include?(attrs['ext']) && attrs['title'] == 'index' attrs['ext'] = 'zafu' attrs['title'] = 'Node' attrs['content_type'] = 'text/zafu' insert_zafu_headings = true elsif attrs['ext'] == 'yml' && attrs['title'] == '_roles' # import roles # FIXME: security. We should show diff and ask for validation... ::Role.import(YAML.load(File.read(document_path))) # Continue in outer loop. throw :next_entry end end ctype = Zena::EXT_TO_TYPE[attrs['ext']] ctype = ctype ? ctype[0] : "application/octet-stream" attrs['content_type'] = ctype File.open(document_path) do |f| file = uploaded_file(f, filename, ctype) (class << file; self; end;).class_eval do alias o_read read define_method(:read) do if insert_zafu_headings o_read.sub(%r{</head>}," <r:stylesheets/>\n <r:javascripts/>\n <r:uses_datebox/>\n</head>") else o_read end end end current_obj = create_or_update_node(attrs.merge(:file => file, :klass => 'Document', :_parent_id => parent_id)) end document_path = nil else current_obj = create_or_update_node(attrs.merge(:_parent_id => parent_id, :klass => 'Document')) end else # :folder, :node current_obj = create_or_update_node(attrs.merge(:_parent_id => parent_id)) end new_object = new_object || current_obj.instance_variable_get(:@new_record_before_save) end current_obj.instance_variable_set(:@new_record_before_save, new_object) current_obj.instance_variable_set(:@versions_count, versions.size) res[current_obj[:id].to_i] = current_obj res.merge!(create_nodes_from_folder(:folder => sub_folder, :parent_id => current_obj[:id], :defaults => defaults, :parent_class => current_obj.klass)) if sub_folder && !current_obj.new_record? end # catch :next_entry end res end |
.create_or_update_node(new_attributes) ⇒ Object
def attr_public?(attribute)
if attribute.to_s =~ /(.*)_zips?$/
return true if self.ancestors.include?(Node) && RelationProxy.find_by_role($1.singularize)
end
super
end
576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 |
# File 'app/models/node.rb', line 576 def create_or_update_node(new_attributes) attributes = transform_attributes(new_attributes) v_lang = attributes['v_lang'] if !current_site.lang_list.include?(v_lang) attributes['v_lang'] = current_site.lang_list.first end if zip = attributes.delete('parent_zip') if id = secure(Node) { Node.translate_pseudo_id(zip, :id, self) } attributes['parent_id'] = id else node = Node.new node.errors.add('parent_id', 'could not be found') return node end end unless attributes['title'] && attributes['parent_id'] node = Node.new node.errors.add('title', "can't be blank") if attributes['title'].blank? node.errors.add('parent_id', "can't be blank") if attributes['parent_id'].blank? return node end # FIXME: remove 'with_exclusive_scope' once scopes are clarified and removed from 'secure' node = Node.send(:with_exclusive_scope) do find_by_parent_title_and_kpath(attributes['parent_id'], attributes['title'], nil) end if node visitor.visit(node) # secure # TODO: class ignored (could be used to transform from one class to another...) attributes.delete('class') attributes.delete('klass') updated_date = node.updated_at node.update_attributes(attributes) if updated_date != node.updated_at node[:create_or_update] = 'updated' else node[:create_or_update] = 'same' end else node = create_node(attributes, false) node[:create_or_update] = 'new' end node end |
.extract_archive(archive) ⇒ Object
835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 |
# File 'app/models/node.rb', line 835 def extract_archive(archive) begin n = 0 # TODO: we could move the tmp folder inside sites/{current_site}/tmp folder = File.join(RAILS_ROOT, 'tmp', sprintf('%s.%d.%d', 'import', $$, n)) end while File.exists?(folder) begin FileUtils::mkpath(folder) if archive.kind_of?(StringIO) filename = archive.original_filename tempf = Tempfile.new(archive.original_filename) File.open(tempf.path, 'wb') { |f| f.syswrite(archive.read) } archive = tempf else filename = archive.original_filename end # extract file in this temporary folder. # FIXME: SECURITY is there a security risk here ? # FIXME: not compatible with Windows. if filename =~ /\.tgz$/ `tar -C '#{folder}' -xz < '#{archive.path}'` elsif filename =~ /\.tar$/ `tar -C '#{folder}' -x < '#{archive.path}'` elsif filename =~ /\.zip$/ `unzip -d '#{folder}' '#{archive.path}'` elsif filename =~ /(.*)(\.gz|\.z)$/ `gzip -d '#{archive.path}' -c > '#{folder}/#{$1.gsub("'",'')}'` else # FIXME: send errors back puts "BAD #{archive.inspect}" end yield folder ensure FileUtils::rmtree(folder) end end |
.find_by_parent_title_and_kpath(parent_id, title, kpath = nil, opts = {}) ⇒ Object
398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 |
# File 'app/models/node.rb', line 398 def find_by_parent_title_and_kpath(parent_id, title, kpath = nil, opts = {}) if cond = opts[:conditions] cond[0] = Array(cond[0]) else cond = opts[:conditions] = [[]] end if kpath cond[0] << "kpath like ?" cond << "#{kpath}%" end cond[0] << "site_id = ? AND parent_id = ?" cond << current_site.id << parent_id find_by_title(title, opts) end |
.find_by_title(title, opts = {}) ⇒ Object
Find node by the indexed title.
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 |
# File 'app/models/node.rb', line 416 def find_by_title(title, opts = {}) if cond = opts[:conditions] cond[0] = Array(cond[0]) else cond = opts[:conditions] = [[]] end if opts.delete(:like) cond[0] << "id1.value LIKE ?" else cond[0] << "id1.value = ?" end cond << title cond[0] = cond[0].join(' AND ') opts[:joins] = Node.title_join opts[:select] = 'nodes.*' Node.find(:first, opts) end |
.find_by_zip(zip) ⇒ Object
875 876 877 878 879 |
# File 'app/models/node.rb', line 875 def find_by_zip(zip) node = find(:first, :conditions=>"zip = #{zip.to_i}") raise ActiveRecord::RecordNotFound unless node node end |
.find_node_by_pseudo(id, base_node = nil) ⇒ Object
Find a node based on a query shortcut. Used by zazen to create a link for “”::art for example.
526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 |
# File 'app/models/node.rb', line 526 def find_node_by_pseudo(id, base_node = nil) raise Zena::AccessViolation if self.scoped_methods == [] str = id.to_s if str =~ /\A\d+\Z/ # zip find_by_zip(str) elsif path = str[/\A\(([^\)]+)\)\Z/,1] if path[0..0] == '/' path = path[1..-1].split('/').map {|p| String.from_filename(p) } find_by_path(path) elsif base_node # transform ../../foo and 45/32/61/72 ==> 'foo' and 45/32 path = path.split('/') root = base_node.fullpath.split('/') while path[0] == '..' root.pop path.shift end path = path.map {|p| String.from_filename(p) } if base_node.zip == root.last.to_i find_by_path(path, base_node.id) elsif root.last if base = find_by_zip(root.last) find_by_path(path, base.id) else nil end else find_by_path(path) end else # do not use (path) pseudo when there is no base_node (during create_or_update_node for example). # FIXME: path pseudo is needed for links... and it should be done here (egg and hen problem) nil end elsif str =~ /\A:?([^\+]+)(\+*)\Z/ offset = $2.to_s.size Node.search_records($1.gsub('-',' '), :offset => offset, :limit => 1).first end end |
.get_attributes_from_yaml(filepath, base_node = nil) ⇒ Object
980 981 982 983 984 |
# File 'app/models/node.rb', line 980 def get_attributes_from_yaml(filepath, base_node = nil) attributes = YAML::load( File.read( filepath ) ) attributes.delete(:_parent_id) transform_attributes(attributes, base_node) end |
.get_class(rel, opts = {}) ⇒ Object
Return class or virtual class from name. FIXME: remove once everything can use VirtualClass
492 493 494 495 496 497 498 499 500 501 502 |
# File 'app/models/node.rb', line 492 def get_class(rel, opts={}) # mushroom_types ==> MushroomType class_name = rel =~ /\A[a-z]/ ? rel.singularize.camelize : rel vclass = VirtualClass[class_name] if vclass && opts[:create] && vclass.id # TODO: how do we deal with real class ? (Currently = pass). visitor.group_ids.include?(vclass.create_group_id) ? vclass : nil else vclass end end |
.get_role(rel) ⇒ Object
Find a role by name.
505 506 507 508 509 |
# File 'app/models/node.rb', line 505 def get_role(rel) # mushroom_types ==> MushroomType role_name = rel =~ /\A[a-z]/ ? rel.singularize.camelize : rel Role.first(:conditions => ['name = ? AND site_id = ?', role_name, current_site.id]) end |
.inherited(child) ⇒ Object
390 391 392 393 394 395 396 |
# File 'app/models/node.rb', line 390 def inherited(child) super unless child.name.blank? # Do not register anonymous classes created during Zafu compilation @@unhandled_children << child end end |
.inspect ⇒ Object
Seeing all the columns of the Node class on every inspect does not help at all.
996 997 998 |
# File 'app/models/node.rb', line 996 def inspect to_s end |
.kpath_match?(kpath) ⇒ Boolean
check inheritance chain through kpath
461 462 463 |
# File 'app/models/node.rb', line 461 def kpath_match?(kpath) self.kpath =~ /^#{kpath}/ end |
.kpaths_for_form(opts = {}) ⇒ Object
FIXME: how to make sure all sub-classes of Node are loaded before this is called ? TODO: move into helper
482 483 484 485 486 487 488 |
# File 'app/models/node.rb', line 482 def kpaths_for_form(opts={}) VirtualClass.all_classes(opts).map do |vclass| # white spaces are insecable spaces (not ' ') a, b = vclass.kpath, vclass.name [a[1..-1].gsub(/./,' ') + b, a] end end |
.load_unhandled_children ⇒ Object
450 451 452 453 454 455 456 457 458 |
# File 'app/models/node.rb', line 450 def load_unhandled_children # this is to make sure subclasses are loaded before the first call # TODO: find a better way to make sure they are all loaded [Note,Page,Project,Section,Document,Image,TextDocument,Skin,Template] while child = @@unhandled_children.pop @@native_node_classes[child.kpath] = child @@native_node_classes_by_name[child.name] = child end end |
.make_schema ⇒ Object
We want to use a Role as schema for properties defined in the real_class instead of Property::Schema.
127 128 129 130 131 132 133 134 135 |
# File 'app/models/node.rb', line 127 def self.make_schema ::Role.new(:name => name).tap do |role| role.kpath = self.kpath # Enable property method definitions. role.klass = self # Used for property inheritance. role.real_class = self end end |
.native_classes ⇒ Object
Return the list of (kpath,subclasses) for the current class.
439 440 441 442 |
# File 'app/models/node.rb', line 439 def native_classes load_unhandled_children @@native_node_classes end |
.native_classes_by_name ⇒ Object
Return the list of (name,class) for the current class.
445 446 447 448 |
# File 'app/models/node.rb', line 445 def native_classes_by_name load_unhandled_children @@native_node_classes_by_name end |
.new(hash = {}, vclass = nil) ⇒ Object Also known as: new_instance
376 377 378 379 380 381 382 |
# File 'app/models/node.rb', line 376 def new(hash={}, vclass = nil) node = super() # set virtual_class (acts as schema) before setting attributes node.virtual_class = vclass || VirtualClass[self.name] node.attributes = hash node end |
.new_node(new_attributes, transform = true) ⇒ Object
TODO: cleanup and rename with something indicating the attrs cleanup that this method does.
628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 |
# File 'app/models/node.rb', line 628 def new_node(new_attributes, transform = true) attributes = transform ? transform_attributes(new_attributes) : new_attributes klass_name = attributes.delete('class') || attributes.delete('klass') klass_name ||= attributes['file'] ? 'Document' : 'Page' if klass_name.kind_of?(VirtualClass) || klass_name.kind_of?(Class) klass = klass_name else unless klass = get_class(klass_name, :create => true) node = Node.new node.instance_eval { @attributes.merge!(attributes) } node.errors.add('klass', 'invalid') # This is to show the klass in the form seizure node.instance_variable_set(:@klass, klass_name.to_s) def node.klass; @klass; end return node end end if attributes['file'] && !(klass.kpath =~ %r{^ND}) klass = VirtualClass['Document'] end if klass.kind_of?(VirtualClass) node = secure(klass.real_class) { klass.new_instance(attributes) } else node = secure(klass) { klass.new_instance(attributes) } end node end |
.plural_relation?(rel) ⇒ Boolean
895 896 897 898 899 900 901 902 903 904 905 906 |
# File 'app/models/node.rb', line 895 def plural_relation?(rel) rel = rel.split(/\s/).first if ['root', 'parent', 'self', 'children', 'documents_only', 'all_pages'].include?(rel) || Node.get_class(rel) rel.pluralize == rel elsif rel =~ /\A\d+\Z/ false else relation = RelationProxy.find_by_role(rel.singularize) return rel =~ /s$/ unless relation relation.target_role == rel.singularize ? !relation.target_unique : !relation.source_unique end end |
.transform_attributes(new_attributes, base_node = nil, change_timezone = true, is_link = false) ⇒ Object
Translate attributes from the visitor’s reference to the application. This method translates dates, zazen shortcuts and zips and returns a stringified hash.
910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 |
# File 'app/models/node.rb', line 910 def transform_attributes(new_attributes, base_node = nil, change_timezone = true, is_link = false) res = {} res['parent_id'] = new_attributes[:_parent_id] if new_attributes[:_parent_id] # real id set inside zena. copy_node = new_attributes.delete(:_copy) attributes = new_attributes.stringify_keys if copy_node || attributes['copy_id'] if !copy_node && attributes['copy_id'].blank? attributes = Node.new.replace_attributes_in_values(attributes) else copy_node ||= Node.find_by_zip(attributes.delete('copy_id')) attributes = copy_node.replace_attributes_in_values(attributes) end end if !res['parent_id'] && p = attributes['parent_id'] res['parent_zip'] = p unless p.blank? end attributes.each do |key, value| next if ['parent_id', 'parent_zip', '_parent_id'].include?(key) if %w{rgroup_id wgroup_id dgroup_id}.include?(key) res[key] = Group.translate_pseudo_id(value, :id) || value elsif %w{rgroup wgroup dgroup}.include?(key) res["#{key}_id"] = Group.translate_pseudo_id(value, :id) || value elsif %w{user_id}.include?(key) res[key] = User.translate_pseudo_id(value, :id) || value elsif %w{link_id}.include?(key) # Link id, not translated res[key] = value elsif %w{id create_at updated_at}.include?(key) # ignore (can be present in xml) elsif %w{log_at event_at v_publish_from}.include?(key) || (is_link && %w{date}.include?(key)) # FIXME: !!! We need to fix timezone parsing in dates depending on the Schema used. This means # that we probably need to do this at the property level (during write). if value.kind_of?(Time) res[key] = value elsif value # parse date if key == 'date' # TODO: this is a temporary hack because date in links do not support timezones/formats properly res[key] = value.to_utc("%Y-%m-%d %H:%M:%S") else res[key] = value.to_utc(_(Zena::Use::Dates::DATETIME), change_timezone ? visitor.tz : nil) end end elsif key =~ /^(\w+)_id$/ res["#{$1}_zip"] = value elsif key =~ /^(\w+)_ids$/ res["#{$1}_zips"] = value.kind_of?(Array) ? value : value.split(',').map(&:strip) elsif key == 'v_status' || key == 'file' res[key] = value unless value.blank? elsif value.kind_of?(Hash) res[key] = transform_attributes(value, base_node, change_timezone, %w{link rel rel_attributes}.include?(key) || is_link) else # translate zazen if value.kind_of?(String) # FIXME: ignore if 'text' of a TextDocument... res[key] = ZazenParser.new(value,:helper=>self).render(:translate_ids=>:zip, :node=>base_node) else res[key] = value end end end res end |
.translate_pseudo_id(id, sym = :id, base_node = nil) ⇒ Object
Find a node’s attribute based on a pseudo (id or path). Used by zazen to create a link for “”::art or “”:(people/ant) for example.
512 513 514 515 516 517 518 519 520 521 522 523 |
# File 'app/models/node.rb', line 512 def translate_pseudo_id(id, sym = :id, base_node = nil) if id.to_s =~ /\A(-?)(\d+)\Z/ # zip # FIXME: this is not secure res = Zena::Db.fetch_attribute("SELECT #{sym} FROM nodes WHERE site_id = #{current_site[:id]} AND zip = '#{$2}'") res ? ($1.blank? ? res.to_i : -res.to_i) : nil elsif node = find_node_by_pseudo(id,base_node) node[sym] else nil end end |
.zafu_attribute(node, attribute) ⇒ Object
Return a safe string to access node attributes in compiled templates and compiled sql.
987 988 989 990 991 992 993 |
# File 'app/models/node.rb', line 987 def zafu_attribute(node, attribute) if node.kind_of?(String) raise Exception.new("You should use safe_method_type...") else node.safe_read(attribute) end end |
Instance Method Details
#all_relations ⇒ Object
Used by zafu to find the search score def score
self[:score]
end
1132 1133 1134 |
# File 'app/models/node.rb', line 1132 def all_relations @all_relations ||= self.vclass.all_relations(self) end |
#archive ⇒ Object
create a ‘tgz’ archive with node content and children, returning temporary file path
1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 |
# File 'app/models/node.rb', line 1390 def archive n = 0 while true folder_path = File.join(RAILS_ROOT, 'tmp', sprintf('%s.%d.%d', 'archive', $$, n)) n += 1 break unless File.exists?(folder_path) end begin FileUtils::mkpath(folder_path) export_to_folder(folder_path) tempf = Tempfile.new(title.to_filename) `cd #{folder_path.inspect}; tar czf #{tempf.path.inspect} *` ensure FileUtils::rmtree(folder_path) end tempf end |
#asset_path(asset_filename) ⇒ Object
Return save path for an asset (element produced by text like a png file from LateX)
1104 1105 1106 1107 1108 |
# File 'app/models/node.rb', line 1104 def asset_path(asset_filename) # It would be nice to move this outside 'self[:id]' so that the same asset can # be used by many pages... But then, how do we expire unused assets ? "#{SITES_ROOT}#{site.data_path}/asset/#{self[:id]}/#{asset_filename}" end |
#auth ⇒ Object
1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 |
# File 'app/models/node.rb', line 1504 def auth @auth ||= if l = auth_user h = StringHash.new [:login, :lang, :profile].each do |k| h[k] = auth_user.send(k) end h else {} end end |
#auth=(params) ⇒ Object
1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 |
# File 'app/models/node.rb', line 1485 def auth=(params) return unless visitor.is_manager? if !users = auth_users # Create user user = secure(User) { User.new } users = @auth_users = [user] @auth_user = user end users.each do |user| user.attributes = params end @auth = nil end |
#auth_user ⇒ Object
Find first user using this node as contact node
1517 1518 1519 1520 1521 1522 |
# File 'app/models/node.rb', line 1517 def auth_user @auth_user ||= begin l = auth_users l && l.first end end |
#auth_users ⇒ Object
Find all users using this node as contact node.
1500 1501 1502 |
# File 'app/models/node.rb', line 1500 def auth_users @auth_users ||= new_record? ? nil : secure(User) { User.all(:conditions => {:node_id => self.id}) } end |
#author ⇒ Object
ACCESSORS
1207 1208 1209 |
# File 'app/models/node.rb', line 1207 def user.node end |
#can_auto_create_discussion? ⇒ Boolean
Automatically create a discussion if any of the following conditions are met:
-
there already exists an
outside
,open
discussion for another language -
the node is not published (creates an internal discussion)
-
the user has drive access to the node
1309 1310 1311 1312 1313 1314 |
# File 'app/models/node.rb', line 1309 def can_auto_create_discussion? can_drive? || (v_status != Zena::Status::Pub) || Discussion.find(:first, :conditions=>[ "node_id = ? AND inside = ? AND open = ?", self[:id], false, true ]) end |
#can_comment? ⇒ Boolean
Return true if it is allowed to add comments to the node in the current context
1357 1358 1359 |
# File 'app/models/node.rb', line 1357 def can_comment? visitor.commentator? && discussion && discussion.open? end |
#comments ⇒ Object
Comments for the current context. Returns nil when there is no discussion.
1338 1339 1340 1341 1342 1343 1344 1345 |
# File 'app/models/node.rb', line 1338 def comments if discussion res = discussion.comments(:with_prop=>can_drive?) res == [] ? nil : res else nil end end |
#comments_count ⇒ Object
TODO: remove, replace by relation proxy: proxy.count…
1348 1349 1350 1351 1352 1353 1354 |
# File 'app/models/node.rb', line 1348 def comments_count if discussion discussion.comments_count(:with_prop=>can_drive?) else 0 end end |
#content_lang ⇒ Object
Return the code language used for syntax highlighting.
1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 |
# File 'app/models/node.rb', line 1111 def content_lang ctype = prop['content_type'] if ctype =~ /^text\/(.*)/ case $1 when 'x-ruby-script' 'ruby' when 'html', 'zafu' 'zafu' else $1 end else nil end end |
#data ⇒ Object
Find all data entries linked to the current node
1223 1224 1225 1226 |
# File 'app/models/node.rb', line 1223 def data list = DataEntry.find(:all, :conditions => "node_a_id = #{id} OR node_b_id = #{id} OR node_c_id = #{id} OR node_d_id = #{id}", :order => 'date ASC,created_at ASC') list == [] ? nil : list end |
#discussion ⇒ Object
Find the discussion for the current context (v_status and v_lang). This automatically creates a new #Discussion if there is no closed or open discussion for the current lang and Node#can_auto_create_discussion? is true
1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 |
# File 'app/models/node.rb', line 1293 def discussion return @discussion if defined?(@discussion) @discussion = Discussion.find(:first, :conditions=>[ "node_id = ? AND inside = ? AND lang = ?", self[:id], v_status != Zena::Status::Pub, v_lang ], :order=>'id DESC') || if can_auto_create_discussion? Discussion.new(:node_id=>self[:id], :lang=>v_lang, :inside=>(v_status != Zena::Status::Pub)) else nil end end |
#dyn_attribute_keys ⇒ Object
1062 1063 1064 |
# File 'app/models/node.rb', line 1062 def dyn_attribute_keys (version.dyn.keys + (virtual_class ? virtual_class.dyn_keys.to_s.split(',').map(&:strip) : [])).uniq.sort end |
#empty? ⇒ Boolean
Include data entry verification in multiversion’s empty? method.
1384 1385 1386 1387 |
# File 'app/models/node.rb', line 1384 def empty? return true if new_record? super && 0 == self.class.count_by_sql("SELECT COUNT(*) FROM #{DataEntry.table_name} WHERE node_a_id = #{id} OR node_b_id = #{id} OR node_c_id = #{id} OR node_d_id = #{id}") end |
#export_keys ⇒ Object
List of attribute keys to export in a zml file.
1458 1459 1460 1461 1462 1463 |
# File 'app/models/node.rb', line 1458 def export_keys { :zazen => Hash[*prop.select { |k, v| v.kind_of?(String) }.flatten], :dates => Hash[*prop.select { |k, v| v.kind_of?(Time) }.flatten], } end |
#export_to_folder(path) ⇒ Object
export node content and children into a folder
1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 |
# File 'app/models/node.rb', line 1410 def export_to_folder(path) children = secure(Node) { Node.find(:all, :conditions=>['parent_id = ?', self[:id] ]) } if kind_of?(Document) && (kind_of?(TextDocument) || text.blank? || text == "!#{zip}!") # skip zml # TODO: this should better check that version content is really useless elsif text.blank? && klass == 'Page' && children # skip zml else File.open(File.join(path, title.to_filename + '.zml'), 'wb') do |f| f.puts self.to_yaml end end if kind_of?(Document) data = kind_of?(TextDocument) ? StringIO.new(text) : file File.open(File.join(path, filename), 'wb') { |f| f.syswrite(data.read) } end if children content_folder = File.join(path, title.to_filename) if !FileUtils::mkpath(content_folder) puts "Problem..." end children.each do |child| child.export_to_folder(content_folder) end end end |
#find_node_by_pseudo(string, base_node = nil) ⇒ Object
This is needed during ‘unparse_assets’ when the node is it’s own helper
1471 1472 1473 |
# File 'app/models/node.rb', line 1471 def find_node_by_pseudo(string, base_node = nil) secure(Node) { Node.find_node_by_pseudo(string, base_node || self) } end |
#get_discussion_id ⇒ Object
Return current discussion id (used by query_builder)
1243 1244 1245 |
# File 'app/models/node.rb', line 1243 def get_discussion_id (discussion && !discussion.new_record?) ? discussion[:id] : '0' end |
#get_project_id ⇒ Object
Return self if the node is a kind of Project. Return project_id otherwise.
1254 1255 1256 1257 |
# File 'app/models/node.rb', line 1254 def get_project_id # root node is it's own section and project self[:parent_id].nil? ? self[:id] : self[:project_id] end |
#get_section_id ⇒ Object
Return self if the node is a kind of Section. Return section_id otherwise.
1248 1249 1250 1251 |
# File 'app/models/node.rb', line 1248 def get_section_id # root node is it's own section and project self[:parent_id].nil? ? self[:id] : self[:section_id] end |
#klass ⇒ Object
1058 1059 1060 |
# File 'app/models/node.rb', line 1058 def klass @new_klass || @set_klass || vclass.to_s end |
#klass=(str) ⇒ Object
1066 1067 1068 1069 |
# File 'app/models/node.rb', line 1066 def klass=(str) return if str == klass @new_klass = str end |
#klass_changed? ⇒ Boolean
116 117 118 |
# File 'app/models/node.rb', line 116 def klass_changed? kpath_changed? end |
#kpath_match?(kpath) ⇒ Boolean
check inheritance chain through kpath
1047 1048 1049 |
# File 'app/models/node.rb', line 1047 def kpath_match?(kpath) self.kpath =~ /^#{kpath}/ end |
#login_info ⇒ Object
1218 1219 1220 |
# File 'app/models/node.rb', line 1218 def login_info secure(User) { User.find(:first, :conditions => {:node_id => self.id})} end |
#m_author ⇒ Object
1320 |
# File 'app/models/node.rb', line 1320 def ; ''; end |
#m_author=(str) ⇒ Object
1332 1333 1334 1335 |
# File 'app/models/node.rb', line 1332 def (str) @add_comment ||= {} @add_comment[:author] = str end |
#m_text ⇒ Object
FIXME: use nested_attributes_alias and try to use native Rails to create the comment comment_attributes=, …
1318 |
# File 'app/models/node.rb', line 1318 def m_text; ''; end |
#m_text=(str) ⇒ Object
1322 1323 1324 1325 |
# File 'app/models/node.rb', line 1322 def m_text=(str) @add_comment ||= {} @add_comment[:text] = str end |
#m_title ⇒ Object
1319 |
# File 'app/models/node.rb', line 1319 def m_title; ''; end |
#m_title=(str) ⇒ Object
1327 1328 1329 1330 |
# File 'app/models/node.rb', line 1327 def m_title=(str) @add_comment ||= {} @add_comment[:title] = str end |
#merge_multi_errors(key, object) ⇒ Object
This is an adaptation of Versions::Multi code to use our special v_ shortcut to access version attributes.
353 354 355 356 357 358 359 |
# File 'app/models/node.rb', line 353 def merge_multi_errors(key, object) if key == 'version' super('v', object) else super end end |
#new_child(opts = {}, transform = true) ⇒ Object
Create a child and let him inherit from rwp groups and section_id
1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 |
# File 'app/models/node.rb', line 1189 def new_child(opts={}, transform = true) c = Node.new_node(opts, transform) c.parent_id = self[:id] c.instance_variable_set(:@parent, self) c.visitor = visitor c.inherit = 1 c.rgroup_id = self.rgroup_id c.wgroup_id = self.wgroup_id c.dgroup_id = self.dgroup_id c.section_id = self.get_section_id c.project_id = self.get_project_id c end |
#o_skin ⇒ Object
1183 |
# File 'app/models/node.rb', line 1183 alias o_skin skin |
#o_user ⇒ Object
1211 |
# File 'app/models/node.rb', line 1211 alias o_user user |
#parent(is_secure = true) ⇒ Object
Find parent
1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 |
# File 'app/models/node.rb', line 1137 def parent(is_secure = true) # make sure the cache is in sync with 'parent_id' (used during validation) if self[:parent_id].nil? nil elsif is_secure # cache parent result (done through secure query) return @parent if @parent && @parent[:id] == self[:parent_id] @parent = secure(Node) { Node.find(self[:parent_id]) } else # not secured (inside an exclusive scope) return @parent_insecure if @parent_insecure && @parent_insecure[:id] == self[:parent_id] @parent_insecure = secure(Node, :secure => false) { Node.find(self[:parent_id]) } end end |
#parent_zip ⇒ Object
Id to zip mapping for parent_id. Used by zafu and forms.
1260 1261 1262 |
# File 'app/models/node.rb', line 1260 def parent_zip @parent_zip || parent.try(:zip) end |
#parent_zip=(zip) ⇒ Object
When setting parent trough controllers, we receive parent_zip=.
1265 1266 1267 |
# File 'app/models/node.rb', line 1265 def parent_zip=(zip) @parent_zip = zip end |
#parse_assets(text, helper, key) ⇒ Object
Parse text content and replace all relative urls (‘../projects/art’) by ids (‘34’)
1092 1093 1094 1095 |
# File 'app/models/node.rb', line 1092 def parse_assets(text, helper, key) # helper is used in textdocuments ZazenParser.new(text,:helper=>helper).render(:translate_ids => :zip, :node => self) end |
#parse_keys ⇒ Object
List of attribute keys to transform (change references, etc).
1466 1467 1468 |
# File 'app/models/node.rb', line 1466 def parse_keys export_keys[:zazen].keys end |
#project ⇒ Object
Return self if the current node is a project else find project.
1169 1170 1171 |
# File 'app/models/node.rb', line 1169 def project self.kind_of?(Project) ? self : real_project end |
#project_zip ⇒ Object
Id to zip mapping for project_id. Used by zafu and forms.
1284 1285 1286 |
# File 'app/models/node.rb', line 1284 def project_zip project[:zip] end |
#rcast(key, type) ⇒ Object
Read with cast to an appropriate instance. This is used along with custom select in QueryBuilder queries.
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 |
# File 'app/models/node.rb', line 329 def rcast(key, type) @rcast_cache ||= {} @rcast_cache[key] ||= begin value = @attributes[key] return nil if value.nil? case type when :string then value when :text then value when :integer then value.to_i rescue value ? 1 : 0 when :float then value.to_f when :decimal then Caster.value_to_decimal(value) when :datetime then Caster.string_to_time(value) when :timestamp then Caster.string_to_time(value) when :time then Caster.string_to_time(value) when :date then Caster.string_to_time(value) when :binary then Caster.binary_to_string(value) when :boolean then Caster.value_to_boolean(value) else value end end end |
#real_project(is_secure = true) ⇒ Object
Find real project (Project’s project if node is a Project)
1174 1175 1176 1177 1178 1179 1180 1181 |
# File 'app/models/node.rb', line 1174 def real_project(is_secure = true) return self if self[:parent_id].nil? if is_secure secure(Project) { Project.find(self[:project_id]) } else secure(Node, :secure => false) { Project.find(self[:project_id]) } end end |
#real_section(is_secure = true) ⇒ Object
Find real section
1158 1159 1160 1161 1162 1163 1164 1165 1166 |
# File 'app/models/node.rb', line 1158 def real_section(is_secure = true) return self if self[:parent_id].nil? # root # we cannot use Section to find because the root node behaves like a Section but is a Project. if is_secure secure(Node) { Node.find(self[:section_id]) } else secure(Node, :secure => false) { Node.find(self[:section_id]) } end end |
#reload ⇒ Object
Remove loaded version and properties on reload.
1026 1027 1028 1029 1030 |
# File 'app/models/node.rb', line 1026 def reload @version = nil @properties = nil super end |
#replace_attributes_in_values(hash) ⇒ Object
Replace [id], [title], etc in attributes values
1084 1085 1086 1087 1088 |
# File 'app/models/node.rb', line 1084 def replace_attributes_in_values(hash) hash.each do |k,v| hash[k] = safe_eval_string(v) end end |
#safe_method_type(signature, receiver = nil) ⇒ Object
We use the virtual_class as proxy for method type resolution. def safe_eval(code)
eval RubyLess.translate(schema, code)
end
144 145 146 |
# File 'app/models/node.rb', line 144 def safe_method_type(signature, receiver = nil) schema.safe_method_type(signature, receiver) end |
#safe_send(method) ⇒ Object
Safe dynamic method dispatching when the method is not known during compile time. Currently this only works for methods without arguments.
1479 1480 1481 1482 1483 |
# File 'app/models/node.rb', line 1479 def safe_send(method) return nil unless type = virtual_class.safe_method_type([method]) res = eval(type[:method]) res ? res.to_s : nil end |
#section ⇒ Object
Return self if the current node is a section else find section.
1153 1154 1155 |
# File 'app/models/node.rb', line 1153 def section self.kind_of?(Section) ? self : real_section end |
#section_zip ⇒ Object
Id to zip mapping for section_id. Used by zafu and forms.
1279 1280 1281 |
# File 'app/models/node.rb', line 1279 def section_zip section[:zip] end |
#skin ⇒ Object
1184 1185 1186 |
# File 'app/models/node.rb', line 1184 def skin @skin ||= secure(Skin) { o_skin } end |
#skin_zip ⇒ Object
1274 1275 1276 |
# File 'app/models/node.rb', line 1274 def skin_zip @skin_zip || skin.try(:zip) end |
#skin_zip=(zip) ⇒ Object
When setting skin trough controllers, we receive skin_zip=.
1270 1271 1272 |
# File 'app/models/node.rb', line 1270 def skin_zip=(zip) @skin_zip = zip.to_i end |
#sweep_cache(filter = nil) ⇒ Object
1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 |
# File 'app/models/node.rb', line 1361 def sweep_cache(filter = nil) return true if current_site.being_created? # Clear element cache # Partial cache not used # Cache.sweep(:visitor_id=>self[:user_id], :visitor_groups=>[rgroup_id, wgroup_id, dgroup_id], :kpath=>self.vclass.kpath) # Clear full result cache # we want to be sure to find the project and parent, even if the visitor does not have an # access to these elements. # FIXME: use self + modified relations instead of parent/project [self, self.real_project(false), self.real_section(false), self.parent(false)].compact.uniq.each do |obj| # destroy all pages in project, parent and section ! CachedPage.expire_with(obj, filter) end # clear assets FileUtils::rmtree(asset_path('')) true end |
#to_yaml ⇒ Object
export node as a hash
1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 |
# File 'app/models/node.rb', line 1443 def to_yaml hash = {} export_keys[:zazen].each do |k, v| hash[k] = unparse_assets(v, self, k) end export_keys[:dates].each do |k, v| hash[k] = visitor.tz.utc_to_local(v).strftime("%Y-%m-%d %H:%M:%S") end hash.merge!('class' => self.klass, 'position' => self.position) hash.to_yaml end |
#unparse_assets(text, helper, key) ⇒ Object
Parse text and replace ids ‘!30!’ by their pseudo path ‘!(img/bird)!’ key is used in TextDocument overloaded method.
1099 1100 1101 |
# File 'app/models/node.rb', line 1099 def unparse_assets(text, helper, key) ZazenParser.new(text,:helper=>helper).render(:translate_ids => :relative_path, :node=>self) end |
#update_attributes_with_transformation(new_attributes, change_timezone = true) ⇒ Object
Update a node’s attributes, transforming the attributes first from the visitor’s context to Node context.
1079 1080 1081 |
# File 'app/models/node.rb', line 1079 def update_attributes_with_transformation(new_attributes, change_timezone = true) update_attributes(secure(Node) {Node.transform_attributes(new_attributes, self, change_timezone)}) end |
#user ⇒ Object
TODO: why do we need secure here ?
1214 1215 1216 |
# File 'app/models/node.rb', line 1214 def user secure!(User) { o_user } end |
#user_zip ⇒ Object
Id to zip mapping for user_id. Used by zafu and forms.
1289 |
# File 'app/models/node.rb', line 1289 def user_zip; self[:user_id]; end |
#v_number ⇒ Object
303 304 305 |
# File 'app/models/node.rb', line 303 def v_number version.number end |
#vclass ⇒ Object
virtual class FIXME: alias vclass to virtual_class alias vclass virtual_class
1054 1055 1056 |
# File 'app/models/node.rb', line 1054 def vclass virtual_class || self.class end |
#versions_with_secure(*args) ⇒ Object
TODO: remove when :inverse_of works.
1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 |
# File 'app/models/node.rb', line 1033 def versions_with_secure(*args) proxy = versions_without_secure(*args) if frozen? proxy = [] elsif proxy.loaded? proxy.each do |v| v.node = self end end proxy end |
#virtual_class ⇒ Object Also known as: schema
108 109 110 111 112 113 114 |
# File 'app/models/node.rb', line 108 def virtual_class @virtual_class ||= if self[:vclass_id] VirtualClass.find_by_id(self[:vclass_id]) else VirtualClass.find_by_name(self.class.name) end end |
#virtual_class=(vclass) ⇒ Object
120 121 122 123 124 |
# File 'app/models/node.rb', line 120 def virtual_class=(vclass) @virtual_class = vclass self[:vclass_id] = vclass.id self[:kpath] = vclass.kpath end |
#vkind_of?(klass) ⇒ Boolean
include virtual classes to check inheritance chain
1072 1073 1074 1075 1076 |
# File 'app/models/node.rb', line 1072 def vkind_of?(klass) if virt = VirtualClass[klass.to_s] kpath_match?(virt.kpath) end end |
#zafu_eval(str, opts = {}) ⇒ Object
Enable dynamic property evaluation
1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 |
# File 'app/models/node.rb', line 1010 def zafu_eval(str, opts = {}) value = safe_eval(str) if value.kind_of?(String) value elsif value.kind_of?(Time) format_date(value, opts) elsif value.kind_of?(Array) value.join(',') else value.to_s end rescue RubyLess::Error nil end |
#zafu_versions ⇒ Object
1005 1006 1007 |
# File 'app/models/node.rb', line 1005 def zafu_versions versions.all(:order => 'number desc') end |