lib/spontaneous/plugins/publishing.rb in spontaneous-0.2.0.alpha2 vs lib/spontaneous/plugins/publishing.rb in spontaneous-0.2.0.alpha3

- old
+ new

@@ -73,53 +73,60 @@ end mark_published = Proc.new do |dataset| dataset.update(:last_published_at => Sequel.datetime_class.now) end + first_published = published = nil - with_editable do - first_published = self.filter(:first_published_at => nil) - published = self.filter + db.transaction do + with_editable do + first_published = self.filter(:first_published_at => nil) + published = self.filter - must_publish_all = (content.nil? || (!revision_exists?(revision-1)) || \ - (content.is_a?(Array) && content.empty?)) + must_publish_all = (content.nil? || (!revision_exists?(revision-1)) || \ + (content.is_a?(Array) && content.empty?)) - if must_publish_all - create_revision(revision) - else - content = content.map do |c| - c.is_a?(Spontaneous::Content) ? c.reload : Spontaneous::Content[c] - end.compact + if must_publish_all + create_revision(revision) + else + content = content.map do |c| + c.is_a?(Spontaneous::Content) ? c.reload : Spontaneous::Content.first(:id => c) + end.compact - first_published = first_published.filter(:id => content.map { |c| c.id }) - published = published.filter(:id => content.map { |c| c.id }) + # pages should be published in depth order because its possible to be publishing a child of + # a page that's never been published + content.sort! { |c1, c2| c1.depth <=> c2.depth } - create_revision(revision, revision-1) - content.each do |c| - c.sync_to_revision(revision) + first_published = first_published.filter(:id => content.map { |c| c.id }) + published = published.filter(:id => content.map { |c| c.id }) + + create_revision(revision, revision-1) + content.each do |c| + c.sync_to_revision(revision, true) + end end - end - # run any passed code and if it fails revert the publish step - if block_given? - begin - with_revision(revision) { yield } - rescue Exception => e - delete_revision(revision) - raise e + # run any passed code and if it fails revert the publish step + if block_given? + begin + with_revision(revision) { yield } + rescue Exception => e + delete_revision(revision) + raise e + end end - end - with_editable do - mark_first_published[first_published] - mark_published[published] + with_editable do + mark_first_published[first_published] + mark_published[published] + end + with_revision(revision) do + mark_first_published[first_published] + mark_published[published] + end end - with_revision(revision) do - mark_first_published[first_published] - mark_published[published] - end end end def publish_all(revision, &block) publish(revision, nil, &block) @@ -137,11 +144,11 @@ end end def delete_revision(revision) return if revision.nil? - database.drop_table(revision_table(revision)) if revision_exists?(revision) + database.drop_table?(revision_table(revision)) end def delete_all_revisions! database.tables.each do |table| database.drop_table(table) if revision_table?(table) @@ -149,63 +156,135 @@ end end # ClassMethods # InstanceMethods + def after_create + super + page.modified!(page?) if page + end + def after_update super page.modified!(page?) if page end def modified!(caller_is_page) unless caller_is_page self.model.where(:id => self.id).update(:modified_at => Sequel.datetime_class.now) end - push_page_change end - def push_page_change - Spontaneous::Change.push(self) if page? - end - - def with_revision(revision, &block) self.class.with_revision(revision, &block) end def with_editable(&block) self.class.with_editable(&block) end - def sync_to_revision(revision, origin=true) + def never_published? + first_published_at.nil? + end + + def before_publish(revision); end + def after_publish(revision); end + + def sync_to_revision(revision, origin=false) # 'publish' is a lock to make sure the duplication doesn't cross # page boundaries unless that's necessary (such as in the case # of a page addition) publish = origin || !self.page? + first_publish = false with_revision(revision) do - published_copy = Spontaneous::Content[self.id] + published_copy = Spontaneous::Content.first(:id => self.id) if published_copy if publish and published_copy.entry_store pieces_to_delete = published_copy.entry_store - self.entry_store pieces_to_delete.each do |entry| - if c = Spontaneous::Content[entry[0]] - c.destroy(false) + if c = Spontaneous::Content.first(:id => entry[0]) + c.destroy(false) rescue ::Sequel::NoExistingObject end end end else # missing content (so force a publish) - Spontaneous::Content.insert({:id => self.id}) + Spontaneous::Content.insert({:id => self.id, :type_sid => values[:type_sid]}) publish = true + first_publish = true end if publish + self.before_publish(revision) with_editable do self.pieces.each do |entry| entry.sync_to_revision(revision, false) end end - Spontaneous::Content.where(:id => self.id).update(self.values) + + if self.page? + sync_children_to_revision(revision) + end + + Spontaneous::Content.where(:id => self.id).update(values) + + published_values = {} + # ancestors can have un-published changes to their paths so we can't just directly publish the current path. + # Instead we re-calculate our path using the published version of the ancestor's path & our (potentially) updated slug. + if self.page? + published = self.class.first :id => self.id + published_values[:path] = published.calculate_path_with_slug(values[:slug]) + end + + # need to calculate the correct visibility for published items. I can't just take this from the editable + # content because up-tree visibility changes might not have been published. This kinda mess is why individual + # page publishing is a pain. + # However, this only applies if the item's visibility is dependent on some up-tree state. So + # if hidden_origin is empty (which means we have a separately calculated visibility) we want + # to take visibility from our own value. + + published_values[:hidden] = self.recalculated_hidden unless self.hidden_origin.blank? + + unless published_values.empty? + Spontaneous::Content.where(:id => self.id).update(published_values) + end + + # Pages that haven't been published before can be published independently of their parents. + # In that case we need to insert an entry for them. We can't guarantee that the published + # parent has the same entries + insert_entry_for_new_page(revision) if first_publish && page? + self.after_publish(revision) end end + end + + def sync_children_to_revision(revision) + published_children = with_revision(revision) { S::Content.filter(:parent_id => self.id) } + published_children.each do |child_page| + deleted = with_editable { S::Content.select(:id).first(:id => child_page.id).nil? } + if deleted + with_revision(revision) do + child_page.destroy + end + end + end + end + + # Finds an entry in the parent page and duplicates it to the parent + # of the newly published page. Positions are not exact as other child pages might not have + # been published. + def insert_entry_for_new_page(revision) + detect_entry = proc { |e| e[0] == self.id } + + parent_entry_store = with_editable { self.parent.entry_store.dup } + entry = parent_entry_store.find(&detect_entry) + index = parent_entry_store.index(&detect_entry) + published_parent = Spontaneous::Content.first :id => parent_id + + published_parent.entry_store ||= [] + + unless published_parent.entry_store.find(&detect_entry) + published_parent.entry_store.insert(index, entry).compact! + end + published_parent.save end end end