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).
-
#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, #set_current_transition, #traductions, #transition_allowed?, #transition_for, #unpublish, #update_attributes, #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.
463 464 465 |
# File 'app/models/node.rb', line 463 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
197 198 199 200 201 202 203 204 205 206 207 |
# File 'app/models/node.rb', line 197 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
992 993 994 |
# File 'app/models/node.rb', line 992 def auto_create_discussion false end |
.cast_to_class(type) ⇒ Object
Return class of cast value.
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 |
# File 'app/models/node.rb', line 302 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
458 459 460 |
# File 'app/models/node.rb', line 458 def change_to_classes_for_form classes_for_form(:class => 'Node', :without => 'Document') end |
.class_for_relation(rel) ⇒ Object
FIXME: Where is this used ?
874 875 876 877 878 879 880 881 882 883 884 885 |
# File 'app/models/node.rb', line 874 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
468 469 470 |
# File 'app/models/node.rb', line 468 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.
653 654 655 656 657 658 659 |
# File 'app/models/node.rb', line 653 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.
662 663 664 665 666 667 668 669 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 |
# File 'app/models/node.rb', line 662 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
568 569 570 571 572 573 574 575 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 |
# File 'app/models/node.rb', line 568 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
827 828 829 830 831 832 833 834 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 |
# File 'app/models/node.rb', line 827 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
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 |
# File 'app/models/node.rb', line 390 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.
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 |
# File 'app/models/node.rb', line 408 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
867 868 869 870 871 |
# File 'app/models/node.rb', line 867 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.
518 519 520 521 522 523 524 525 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 |
# File 'app/models/node.rb', line 518 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
972 973 974 975 976 |
# File 'app/models/node.rb', line 972 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
484 485 486 487 488 489 490 491 492 493 494 |
# File 'app/models/node.rb', line 484 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.
497 498 499 500 501 |
# File 'app/models/node.rb', line 497 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
382 383 384 385 386 387 388 |
# File 'app/models/node.rb', line 382 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.
988 989 990 |
# File 'app/models/node.rb', line 988 def inspect to_s end |
.kpath_match?(kpath) ⇒ Boolean
check inheritance chain through kpath
453 454 455 |
# File 'app/models/node.rb', line 453 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
474 475 476 477 478 479 480 |
# File 'app/models/node.rb', line 474 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
442 443 444 445 446 447 448 449 450 |
# File 'app/models/node.rb', line 442 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.
431 432 433 434 |
# File 'app/models/node.rb', line 431 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.
437 438 439 440 |
# File 'app/models/node.rb', line 437 def native_classes_by_name load_unhandled_children @@native_node_classes_by_name end |
.new(hash = {}, vclass = nil) ⇒ Object Also known as: new_instance
368 369 370 371 372 373 374 |
# File 'app/models/node.rb', line 368 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.
620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 |
# File 'app/models/node.rb', line 620 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
887 888 889 890 891 892 893 894 895 896 897 898 |
# File 'app/models/node.rb', line 887 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.
902 903 904 905 906 907 908 909 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 |
# File 'app/models/node.rb', line 902 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.
504 505 506 507 508 509 510 511 512 513 514 515 |
# File 'app/models/node.rb', line 504 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.
979 980 981 982 983 984 985 |
# File 'app/models/node.rb', line 979 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
1124 1125 1126 |
# File 'app/models/node.rb', line 1124 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
1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 |
# File 'app/models/node.rb', line 1382 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)
1096 1097 1098 1099 1100 |
# File 'app/models/node.rb', line 1096 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 |
#author ⇒ Object
ACCESSORS
1199 1200 1201 |
# File 'app/models/node.rb', line 1199 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
1301 1302 1303 1304 1305 1306 |
# File 'app/models/node.rb', line 1301 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
1349 1350 1351 |
# File 'app/models/node.rb', line 1349 def can_comment? visitor.commentator? && discussion && discussion.open? end |
#comments ⇒ Object
Comments for the current context. Returns nil when there is no discussion.
1330 1331 1332 1333 1334 1335 1336 1337 |
# File 'app/models/node.rb', line 1330 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…
1340 1341 1342 1343 1344 1345 1346 |
# File 'app/models/node.rb', line 1340 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.
1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 |
# File 'app/models/node.rb', line 1103 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
1215 1216 1217 1218 |
# File 'app/models/node.rb', line 1215 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
1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 |
# File 'app/models/node.rb', line 1285 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
1054 1055 1056 |
# File 'app/models/node.rb', line 1054 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.
1376 1377 1378 1379 |
# File 'app/models/node.rb', line 1376 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.
1450 1451 1452 1453 1454 1455 |
# File 'app/models/node.rb', line 1450 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
1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 |
# File 'app/models/node.rb', line 1402 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
1463 1464 1465 |
# File 'app/models/node.rb', line 1463 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)
1235 1236 1237 |
# File 'app/models/node.rb', line 1235 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.
1246 1247 1248 1249 |
# File 'app/models/node.rb', line 1246 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.
1240 1241 1242 1243 |
# File 'app/models/node.rb', line 1240 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
1050 1051 1052 |
# File 'app/models/node.rb', line 1050 def klass @new_klass || @set_klass || vclass.to_s end |
#klass=(str) ⇒ Object
1058 1059 1060 1061 |
# File 'app/models/node.rb', line 1058 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
1039 1040 1041 |
# File 'app/models/node.rb', line 1039 def kpath_match?(kpath) vclass.kpath =~ /^#{kpath}/ end |
#login_info ⇒ Object
1210 1211 1212 |
# File 'app/models/node.rb', line 1210 def login_info secure(User) { User.find(:first, :conditions => {:node_id => self.id})} end |
#m_author ⇒ Object
1312 |
# File 'app/models/node.rb', line 1312 def ; ''; end |
#m_author=(str) ⇒ Object
1324 1325 1326 1327 |
# File 'app/models/node.rb', line 1324 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=, …
1310 |
# File 'app/models/node.rb', line 1310 def m_text; ''; end |
#m_text=(str) ⇒ Object
1314 1315 1316 1317 |
# File 'app/models/node.rb', line 1314 def m_text=(str) @add_comment ||= {} @add_comment[:text] = str end |
#m_title ⇒ Object
1311 |
# File 'app/models/node.rb', line 1311 def m_title; ''; end |
#m_title=(str) ⇒ Object
1319 1320 1321 1322 |
# File 'app/models/node.rb', line 1319 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.
345 346 347 348 349 350 351 |
# File 'app/models/node.rb', line 345 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
1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 |
# File 'app/models/node.rb', line 1181 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
1175 |
# File 'app/models/node.rb', line 1175 alias o_skin skin |
#o_user ⇒ Object
1203 |
# File 'app/models/node.rb', line 1203 alias o_user user |
#parent(is_secure = true) ⇒ Object
Find parent
1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 |
# File 'app/models/node.rb', line 1129 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.
1252 1253 1254 |
# File 'app/models/node.rb', line 1252 def parent_zip @parent_zip || parent.try(:zip) end |
#parent_zip=(zip) ⇒ Object
When setting parent trough controllers, we receive parent_zip=.
1257 1258 1259 |
# File 'app/models/node.rb', line 1257 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’)
1084 1085 1086 1087 |
# File 'app/models/node.rb', line 1084 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).
1458 1459 1460 |
# File 'app/models/node.rb', line 1458 def parse_keys export_keys[:zazen].keys end |
#project ⇒ Object
Return self if the current node is a project else find project.
1161 1162 1163 |
# File 'app/models/node.rb', line 1161 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.
1276 1277 1278 |
# File 'app/models/node.rb', line 1276 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.
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 |
# File 'app/models/node.rb', line 321 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)
1166 1167 1168 1169 1170 1171 1172 1173 |
# File 'app/models/node.rb', line 1166 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
1150 1151 1152 1153 1154 1155 1156 1157 1158 |
# File 'app/models/node.rb', line 1150 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.
1018 1019 1020 1021 1022 |
# File 'app/models/node.rb', line 1018 def reload @version = nil @properties = nil super end |
#replace_attributes_in_values(hash) ⇒ Object
Replace [id], [title], etc in attributes values
1076 1077 1078 1079 1080 |
# File 'app/models/node.rb', line 1076 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.
1471 1472 1473 1474 1475 |
# File 'app/models/node.rb', line 1471 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.
1145 1146 1147 |
# File 'app/models/node.rb', line 1145 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.
1271 1272 1273 |
# File 'app/models/node.rb', line 1271 def section_zip section[:zip] end |
#skin ⇒ Object
1176 1177 1178 |
# File 'app/models/node.rb', line 1176 def skin @skin ||= secure(Skin) { o_skin } end |
#skin_zip ⇒ Object
1266 1267 1268 |
# File 'app/models/node.rb', line 1266 def skin_zip @skin_zip || skin.try(:zip) end |
#skin_zip=(zip) ⇒ Object
When setting skin trough controllers, we receive skin_zip=.
1262 1263 1264 |
# File 'app/models/node.rb', line 1262 def skin_zip=(zip) @skin_zip = zip.to_i end |
#sweep_cache(filter = nil) ⇒ Object
1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 |
# File 'app/models/node.rb', line 1353 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
1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 |
# File 'app/models/node.rb', line 1435 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.
1091 1092 1093 |
# File 'app/models/node.rb', line 1091 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.
1071 1072 1073 |
# File 'app/models/node.rb', line 1071 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 ?
1206 1207 1208 |
# File 'app/models/node.rb', line 1206 def user secure!(User) { o_user } end |
#user_zip ⇒ Object
Id to zip mapping for user_id. Used by zafu and forms.
1281 |
# File 'app/models/node.rb', line 1281 def user_zip; self[:user_id]; end |
#v_number ⇒ Object
295 296 297 |
# File 'app/models/node.rb', line 295 def v_number version.number end |
#vclass ⇒ Object
virtual class FIXME: alias vclass to virtual_class alias vclass virtual_class
1046 1047 1048 |
# File 'app/models/node.rb', line 1046 def vclass virtual_class || self.class end |
#versions_with_secure(*args) ⇒ Object
TODO: remove when :inverse_of works.
1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 |
# File 'app/models/node.rb', line 1025 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
1064 1065 1066 1067 1068 |
# File 'app/models/node.rb', line 1064 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
1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 |
# File 'app/models/node.rb', line 1002 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
997 998 999 |
# File 'app/models/node.rb', line 997 def zafu_versions versions.all(:order => 'number desc') end |