ACTS_PER_PAGE = Card.config.acts_per_page def history? true end # must be called on all actions and before :set_name, :process_subcards and # :validate_delete_children def actionable? history? end event :assign_action, :initialize, when: proc { |c| c.actionable? } do act = director.need_act @current_action = Card::Action.create( card_act_id: act.id, action_type: @action, draft: (Env.params["draft"] == "true") ) if @supercard && @supercard != self @current_action.super_action = @supercard.current_action end end # stores changes in the changes table and assigns them to the current action # removes the action if there are no changes event :finalize_action, :finalize, when: :finalize_action? do if changed_fields.present? @current_action.update_attributes! card_id: id # Note: #last_change_on uses the id to sort by date # so the changes for the create changes have to be created before the first change store_card_changes_for_create_action if first_change? store_card_changes if @current_action.action_type != :create elsif @current_action.card_changes.reload.empty? @current_action.delete @current_action = nil end end def first_change? # = update or delete @current_action.action_type != :create && @current_action.card.actions.size == 2 && create_action.card_changes.empty? end def create_action @create_action ||= actions.first end # changes for the create action are stored after the first update def store_card_changes_for_create_action Card::Change::TRACKED_FIELDS.each do |f| Card::Change.create field: f, value: attribute_before_act(f), card_action_id: create_action.id end end def store_card_changes # FIXME: should be one bulk insert changed_fields.each do |f| Card::Change.create field: f, value: self[f], card_action_id: @current_action.id end end def changed_fields Card::Change::TRACKED_FIELDS & (changed_attribute_names_to_save | saved_changes.keys) end def finalize_action? actionable? && current_action end event :finalize_act, after: :finalize_action, when: :act_card? do Card::ActManager.act.update_attributes! card_id: id end event :remove_empty_act, :integrate_with_delay_final, when: :remove_empty_act? do #Card::ActManager.act.delete #Card::ActManager.act = nil end def remove_empty_act? act_card? && ActManager.act&.actions&.reload&.empty? end def act_card? self == Card::ActManager.act_card end event :rollback_actions, :prepare_to_validate, on: :update, when: :rollback_request? do update_args = { subcards: {} } revert_actions.each do |action| rev = action.card.revision(action, revert_to_previous_action?) if action.card_id == id update_args.merge! rev else update_args[:subcards][action.card.name] = rev end end Env.params["revert_actions"] = nil update_attributes! update_args clear_drafts abort :success end def revert_to_previous_action? Env.params["revert_to"] == "previous" end def revert_actions Env.params["revert_actions"].map do |a_id| Action.fetch(a_id) || nil end.compact end def rollback_request? history? && Env&.params["revert_actions"]&.class == Array end # all acts with actions on self and on cards that are descendants of self and # included in self def intrusive_family_acts args={} @intrusive_family_acts ||= begin Act.find_all_with_actions_on((included_descendant_card_ids << id), args) end end # all acts with actions on self and on cards included in self def intrusive_acts args={ with_drafts: true } @intrusive_acts ||= begin Act.find_all_with_actions_on((included_card_ids << id), args) end end def current_rev_nr @current_rev_nr ||= begin if intrusive_acts.first.actions.last.draft @intrusive_acts.size - 1 else @intrusive_acts.size end end end def included_card_ids @included_card_ids ||= Card::Reference.select(:referee_id).where( ref_type: "I", referer_id: id ).pluck("referee_id").compact.uniq end def descendant_card_ids parent_ids=[id] more_ids = Card.where("left_id IN (?)", parent_ids).pluck("id") more_ids += descendant_card_ids more_ids unless more_ids.empty? more_ids end def included_descendant_card_ids included_card_ids & descendant_card_ids end format :html do view :history, cache: :never do voo.show :toolbar class_up "d0-card-body", "history-slot" frame do bs_layout container: true, fluid: true do html _render_history_legend(with_drafts: true) row 12 do html _render_act_list acts: history_acts end row 12 do col act_paging end end end end view :history_legend do |args| bs_layout do row md: [12, 12], lg: [7, 5] do col action_legend(args[:with_drafts]) col content_legend, class: "text-right" end end end def revert_actions_link act, link_text, revert_to: :this, slot_selector: nil, html_args: {} return unless card.ok? :update html_args.merge! remote: true, method: :post, rel: "nofollow", path: { action: :update, view: :open, look_in_trash: true, revert_actions: act.actions.map(&:id), revert_to: revert_to } html_args[:path]["data-slot-selector"] = slot_selector if slot_selector add_class html_args, "slotter" link_to link_text, html_args end def history_acts card.intrusive_acts.page(page_from_params).per(ACTS_PER_PAGE) end def act_paging intrusive_acts = card.intrusive_acts .page(page_from_params).per(ACTS_PER_PAGE) wrap_with :span, class: "slotter" do paginate intrusive_acts, remote: true, theme: "twitter-bootstrap-4" end end def page_from_params params["page"] || 1 end def action_legend with_drafts=true types = %i[create update delete] legend = types.map do |action_type| "#{action_icon(action_type)} #{action_type}d" end legend << "#{action_icon(:draft)} unsaved draft" if with_drafts "Actions: #{legend.join ' | '}" end def content_legend legend = [Card::Content::Diff.render_added_chunk("Additions"), Card::Content::Diff.render_deleted_chunk("Subtractions")] "Content changes: #{legend.join ' | '}" end view :content_changes do |args| action = args[:action] if args[:hide_diff] action.raw_view else action.content_diff(args[:diff_type]) end end end def diff_args { diff_format: :text } end def has_edits? Card::Act.where(actor_id: id).where("card_id IS NOT NULL").present? end