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::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}
- @@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
-
.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.
- #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.
-
#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(opts = {}) ⇒ Object
TODO: test.
-
#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_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::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, #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, #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.
411 412 413 |
# File 'app/models/node.rb', line 411 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
196 197 198 199 200 201 202 203 204 205 206 |
# File 'app/models/node.rb', line 196 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
931 932 933 |
# File 'app/models/node.rb', line 931 def auto_create_discussion false end |
.change_to_classes_for_form ⇒ Object
Class list to which this class can change to
406 407 408 |
# File 'app/models/node.rb', line 406 def change_to_classes_for_form classes_for_form(:class => 'Node', :without => 'Document') end |
.class_for_relation(rel) ⇒ Object
FIXME: Where is this used ?
817 818 819 820 821 822 823 824 825 826 827 828 |
# File 'app/models/node.rb', line 817 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
416 417 418 |
# File 'app/models/node.rb', line 416 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.
595 596 597 598 599 600 601 |
# File 'app/models/node.rb', line 595 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.
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 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 651 652 653 654 655 656 657 658 659 660 661 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 |
# File 'app/models/node.rb', line 604 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| 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 = entries[index] path = File.join(folder, filename) if File.stat(path).directory? type = :folder title = filename sub_folder = path attrs = defaults.dup attrs['v_lang'] ||= visitor.lang elsif filename =~ /^(.+?)(\.\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 filename =~ /^((.+?)\.(.+?))(\.\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 title = filename 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] =~ /^#{title}(\.\w\w|)(\.\d+|)\.zml$/ # bird.jpg.en.zml lang = $1.blank? ? visitor.lang : $1[1..-1] path = File.join(folder,entries[index]) # 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' 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
516 517 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 560 561 562 563 564 565 |
# File 'app/models/node.rb', line 516 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
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 |
# File 'app/models/node.rb', line 770 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
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 |
# File 'app/models/node.rb', line 338 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.
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 |
# File 'app/models/node.rb', line 356 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
810 811 812 813 814 |
# File 'app/models/node.rb', line 810 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.
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 |
# File 'app/models/node.rb', line 466 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
911 912 913 914 915 |
# File 'app/models/node.rb', line 911 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
432 433 434 435 436 437 438 439 440 441 442 |
# File 'app/models/node.rb', line 432 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.
445 446 447 448 449 |
# File 'app/models/node.rb', line 445 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
330 331 332 333 334 335 336 |
# File 'app/models/node.rb', line 330 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.
927 928 929 |
# File 'app/models/node.rb', line 927 def inspect to_s end |
.kpath_match?(kpath) ⇒ Boolean
check inheritance chain through kpath
401 402 403 |
# File 'app/models/node.rb', line 401 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
422 423 424 425 426 427 428 |
# File 'app/models/node.rb', line 422 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
390 391 392 393 394 395 396 397 398 |
# File 'app/models/node.rb', line 390 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.
379 380 381 382 |
# File 'app/models/node.rb', line 379 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.
385 386 387 388 |
# File 'app/models/node.rb', line 385 def native_classes_by_name load_unhandled_children @@native_node_classes_by_name end |
.new(hash = {}, vclass = nil) ⇒ Object Also known as: new_instance
316 317 318 319 320 321 322 |
# File 'app/models/node.rb', line 316 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.
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 |
# File 'app/models/node.rb', line 568 def new_node(new_attributes, transform = true) attributes = transform ? transform_attributes(new_attributes) : new_attributes klass_name = attributes.delete('class') || attributes.delete('klass') || '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 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
830 831 832 833 834 835 836 837 838 839 840 841 |
# File 'app/models/node.rb', line 830 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.
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 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 |
# File 'app/models/node.rb', line 845 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'] copy_node ||= Node.find_by_zip(attributes.delete('copy_id')) attributes = copy_node.replace_attributes_in_values(attributes) end if !res['parent_id'] && p = attributes['parent_id'] res['parent_zip'] = p 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(',') 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.
452 453 454 455 456 457 458 459 460 461 462 463 |
# File 'app/models/node.rb', line 452 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.
918 919 920 921 922 923 924 |
# File 'app/models/node.rb', line 918 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
1046 1047 1048 |
# File 'app/models/node.rb', line 1046 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
1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 |
# File 'app/models/node.rb', line 1357 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)
1018 1019 1020 1021 1022 |
# File 'app/models/node.rb', line 1018 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
1121 1122 1123 |
# File 'app/models/node.rb', line 1121 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
1277 1278 1279 1280 1281 1282 |
# File 'app/models/node.rb', line 1277 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
1325 1326 1327 |
# File 'app/models/node.rb', line 1325 def can_comment? visitor.commentator? && discussion && discussion.open? end |
#comments ⇒ Object
Comments for the current context. Returns nil when there is no discussion.
1306 1307 1308 1309 1310 1311 1312 1313 |
# File 'app/models/node.rb', line 1306 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…
1316 1317 1318 1319 1320 1321 1322 |
# File 'app/models/node.rb', line 1316 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.
1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 |
# File 'app/models/node.rb', line 1025 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
1133 1134 1135 1136 |
# File 'app/models/node.rb', line 1133 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
1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 |
# File 'app/models/node.rb', line 1261 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
976 977 978 |
# File 'app/models/node.rb', line 976 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.
1351 1352 1353 1354 |
# File 'app/models/node.rb', line 1351 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.
1425 1426 1427 1428 1429 1430 |
# File 'app/models/node.rb', line 1425 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
1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 |
# File 'app/models/node.rb', line 1377 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
1438 1439 1440 |
# File 'app/models/node.rb', line 1438 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)
1153 1154 1155 |
# File 'app/models/node.rb', line 1153 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.
1164 1165 1166 1167 |
# File 'app/models/node.rb', line 1164 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.
1158 1159 1160 1161 |
# File 'app/models/node.rb', line 1158 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
972 973 974 |
# File 'app/models/node.rb', line 972 def klass @new_klass || @set_klass || vclass.to_s end |
#klass=(str) ⇒ Object
980 981 982 983 |
# File 'app/models/node.rb', line 980 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
961 962 963 |
# File 'app/models/node.rb', line 961 def kpath_match?(kpath) vclass.kpath =~ /^#{kpath}/ end |
#m_author ⇒ Object
1288 |
# File 'app/models/node.rb', line 1288 def ; ''; end |
#m_author=(str) ⇒ Object
1300 1301 1302 1303 |
# File 'app/models/node.rb', line 1300 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=, …
1286 |
# File 'app/models/node.rb', line 1286 def m_text; ''; end |
#m_text=(str) ⇒ Object
1290 1291 1292 1293 |
# File 'app/models/node.rb', line 1290 def m_text=(str) @add_comment ||= {} @add_comment[:text] = str end |
#m_title ⇒ Object
1287 |
# File 'app/models/node.rb', line 1287 def m_title; ''; end |
#m_title=(str) ⇒ Object
1295 1296 1297 1298 |
# File 'app/models/node.rb', line 1295 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.
296 297 298 299 300 301 302 |
# File 'app/models/node.rb', line 296 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
1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 |
# File 'app/models/node.rb', line 1103 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
1097 |
# File 'app/models/node.rb', line 1097 alias o_skin skin |
#o_user ⇒ Object
1125 |
# File 'app/models/node.rb', line 1125 alias o_user user |
#parent(is_secure = true) ⇒ Object
Find parent
1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 |
# File 'app/models/node.rb', line 1051 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.
1170 1171 1172 |
# File 'app/models/node.rb', line 1170 def parent_zip @parent_zip || parent.try(:zip) end |
#parent_zip=(zip) ⇒ Object
When setting parent trough controllers, we receive parent_zip=.
1175 1176 1177 |
# File 'app/models/node.rb', line 1175 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’)
1006 1007 1008 1009 |
# File 'app/models/node.rb', line 1006 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).
1433 1434 1435 |
# File 'app/models/node.rb', line 1433 def parse_keys export_keys[:zazen].keys end |
#project ⇒ Object
Return self if the current node is a project else find project.
1083 1084 1085 |
# File 'app/models/node.rb', line 1083 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.
1194 1195 1196 |
# File 'app/models/node.rb', line 1194 def project_zip project[:zip] end |
#real_project(is_secure = true) ⇒ Object
Find real project (Project’s project if node is a Project)
1088 1089 1090 1091 1092 1093 1094 1095 |
# File 'app/models/node.rb', line 1088 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
1072 1073 1074 1075 1076 1077 1078 1079 1080 |
# File 'app/models/node.rb', line 1072 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.
940 941 942 943 944 |
# File 'app/models/node.rb', line 940 def reload @version = nil @properties = nil super end |
#replace_attributes_in_values(hash) ⇒ Object
Replace [id], [title], etc in attributes values
998 999 1000 1001 1002 |
# File 'app/models/node.rb', line 998 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.
1446 1447 1448 1449 1450 |
# File 'app/models/node.rb', line 1446 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.
1067 1068 1069 |
# File 'app/models/node.rb', line 1067 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.
1189 1190 1191 |
# File 'app/models/node.rb', line 1189 def section_zip section[:zip] end |
#skin ⇒ Object
1098 1099 1100 |
# File 'app/models/node.rb', line 1098 def skin @skin ||= secure(Skin) { o_skin } end |
#skin_zip ⇒ Object
1184 1185 1186 |
# File 'app/models/node.rb', line 1184 def skin_zip @skin_zip || skin.try(:zip) end |
#skin_zip=(zip) ⇒ Object
When setting skin trough controllers, we receive skin_zip=.
1180 1181 1182 |
# File 'app/models/node.rb', line 1180 def skin_zip=(zip) @skin_zip = zip.to_i end |
#sweep_cache(opts = {}) ⇒ Object
TODO: test
1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 |
# File 'app/models/node.rb', line 1330 def sweep_cache(opts = {}) return if current_site.being_created? # Clear element cache 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, opts) end # clear assets FileUtils::rmtree(asset_path('')) end |
#to_yaml ⇒ Object
export node as a hash
1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 |
# File 'app/models/node.rb', line 1410 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) 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.
1013 1014 1015 |
# File 'app/models/node.rb', line 1013 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.
993 994 995 |
# File 'app/models/node.rb', line 993 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 ?
1128 1129 1130 |
# File 'app/models/node.rb', line 1128 def user secure!(User) { o_user } end |
#user_zip ⇒ Object
Id to zip mapping for user_id. Used by zafu and forms.
1199 |
# File 'app/models/node.rb', line 1199 def user_zip; self[:user_id]; end |
#v_number ⇒ Object
290 291 292 |
# File 'app/models/node.rb', line 290 def v_number version.number end |
#vclass ⇒ Object
virtual class FIXME: alias vclass to virtual_class alias vclass virtual_class
968 969 970 |
# File 'app/models/node.rb', line 968 def vclass virtual_class || self.class end |
#versions_with_secure(*args) ⇒ Object
TODO: remove when :inverse_of works.
947 948 949 950 951 952 953 954 955 956 957 |
# File 'app/models/node.rb', line 947 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
986 987 988 989 990 |
# File 'app/models/node.rb', line 986 def vkind_of?(klass) if virt = VirtualClass[klass.to_s] kpath_match?(virt.kpath) end end |
#zafu_versions ⇒ Object
936 937 938 |
# File 'app/models/node.rb', line 936 def zafu_versions versions.all(:order => 'number desc') end |