'use strict' angular.module('Engine2') .directive 'e2Action', (E2Actions) -> scope: true controller: ($scope, $attrs, $parse, $element, $http) -> if action_attr = $attrs.action action_names = $parse(action_attr)($scope) throw "Invalid action path: '#{action_attr}'" unless action_names action_names = action_names.split('/') if _.isString(action_names) create = (action) -> action.create_action_path(action_names, $scope, $element).then (act) -> act.invoke($parse($attrs.invoke)($scope)) if $attrs.invoke? sc = $scope if sc.action sc = sc.$parent until sc.action instanceof E2Actions.action create(sc.action) else bootstrap_action_off = $scope.$on "bootstrap_action", (evt, action) -> bootstrap_action_off() create(action) else $http.get("api/meta").then (mresponse) -> $scope.$broadcast "bootstrap_action", $scope.action = new E2Actions.root(mresponse.data, $scope, null, $element, action_resource: 'api') .factory 'E2Actions', (E2, $http, $timeout, $injector, $compile, $templateCache, $q, localStorageService, $rootScope, $location, angularLoad, $websocket, MetaCache, $stateRegistry, $urlRouter) -> globals = E2.globals action: class Action constructor: (response, scope, parent, element, action_info) -> @find_action_info = (name, raise = true) -> act = response.actions[name] throw "Undefined action '#{name}' for action #{@parent()?.action_info().action_resource}/#{@action_info().name}" if raise && !act act _.each response.actions, (act, nm) -> act.name = nm @meta = response.meta (@scope = -> scope) if scope @element = -> element @action_info = -> action_info @parent = -> parent if @meta.panel @default_action_name = _(response.actions).find((o) -> o.default)?.name scope = scope.$new(true) scope.$on "$destroy", (e) => @destroy(e) scope.action = @ act = parent act = act.parent() while act && !act.meta.panel unless act # no modal for top level panels @meta.panel.modal_action = false @meta.panel.footer = true unless @meta.panel.footer == false @websocket_connect() if @meta.websocket @initialize() initialize: -> @process_static_meta() console.info "CREATE #{@action_info().action_resource}" process_static_meta: -> if @meta.menus _.each @meta.menus, (menu, name) => E2.process_menu @, name process_meta: -> handle_error: (err, action_info, element, create) -> if err.status == 401 if action_info.access $rootScope.$broadcast "relogin", element?, create else @globals().modal().error("#{err.status}: #{err.data.message}", err.data.cause || err.data.message) $q.reject(err) save_state: () -> _.each @meta.state, (s) => localStorageService.set("#{@globals().application}/#{@action_info().action_resource}/#{s}", @[s]) load_state: () -> _.each @meta.state, (s) => _.merge(@[s], localStorageService.get("#{@globals().application}/#{@action_info().action_resource}/#{s}")) destroy: (e) -> console.log "DESTROY #{@action_info().action_resource}" create_action: (name, sc, el) -> info = @find_action_info(name) info.action_resource = "#{@action_info().action_resource}/#{info.name}" get_meta = if !info.terminal || info.meta $http.get("#{info.action_resource}/meta", cache: MetaCache).then (response) => if info.recheck_access $http.get("#{info.action_resource}/meta", params: (access: true, parent_id: @current_id())).then (aresponse) -> response.data.actions[k].access = v for k, v of aresponse.data response else response # $q.when ^ else $q.when(data: (meta: {}, actions: [])) E2A = $injector.get("E2Actions") get_meta.then (mresponse) => new (E2A[info.action_type] ? E2A.default_action)(mresponse.data, sc, @, el, info) , (err) => @handle_error(err, info, el) invoke_action: (name, args) -> @create_action(name, @scope()).then (act) -> act.invoke(args) create_action_path: (action_names, sc, elem) -> last_name = action_names.pop() _.reduce(action_names, ((pr, nm) -> pr.then (act) -> act.create_action(nm)), $q.when(@)).then (act) -> act.create_action(last_name, sc, elem).then (act) -> sc.action = act globals: -> globals action_pending: -> globals.action_pending == @ pre_invoke: -> post_invoke: -> invoke: (params) -> params ?= {} @globals().action_pending = if @meta.panel then @ else @parent() @pre_invoke(params) _.merge(params, @meta.arguments) if @meta.arguments info = @action_info() get_invoke = if @meta.invokable == false then $q.when(data: (response: {})) else params.initial = true if @meta.panel && !@action_invoked && info.method == 'get' $http[info.method](info.action_resource, if info.method == 'post' then params else (params: params)) get_invoke.then (response) => @arguments = _.keys(response.data) E2.merge(@, response.data) @process_meta() promise = if @meta.panel # persistent action if !@action_invoked @action_invoked = true @panel_render() else prnt = @parent() throw "Attempted parent merge for root action: #{info.name}" unless prnt E2.merge(prnt, response.data) @post_invoke(params) @execute_commands() if @meta.execute if @meta.repeat @scope().$on "$destroy", => @destroyed = true $timeout (=> @invoke(params)), @meta.repeat unless @destroyed delete @meta.repeat @globals().action_pending = false promise , (err) => @globals().action_pending = false @handle_error(err, info, @element()) panel_render: -> if @meta.panel.modal_action if @element() E2.fetch_panel(@meta.panel, true).then (template) => @panel_show?() compiled = $compile(template)(@scope()) @element().empty().append(compiled.contents()) @panel_shown?() else @globals().modal().show(@).then => @ else @panel_scope?().$destroy() act = @ act = act.parent() until act.element() element = act.element() # @element() is_modal = @globals().modal().is_modal() && !@element() E2.fetch_panel(@meta.panel, is_modal).then (template) => @panel_show?() # @panel_scope().$destroy() # @panel_scope = -> @scope().$new(false) # @scope().$broadcast "$destroy" @panel_scope = -> @scope().$new() compiled = $compile(template)(@panel_scope()) if is_modal element.empty().append(compiled.contents()) else # element.empty().$destroy() element.empty().append(compiled) @panel_shown?() # panel_refresh: -> panel_hidden: -> @scope().$destroy() panel_close: -> if @meta.panel.modal_action @modal_hide() else if @parent().parent() # @parent().panel_refresh() @panel_hide?() @panel_hidden() @scope().$destroy() @parent().action_invoked = false @parent().invoke() panel_menu_cancel: -> @panel_close() panel_menu_close: -> @panel_close() websocket_connect: -> l = $location ws_meta = @meta.websocket ws = $websocket "ws#{l.protocol().slice(4, 5)}://#{l.host()}:#{l.port()}/#{@action_info().action_resource}", undefined, ws_meta.options _.each @globals().ws_methods, (method) => ws_method_impl = @["ws_#{method}"] ws["on#{_.capitalize(method)}"] (evt) => if method == 'message' msg = JSON.parse(evt.data) if msg.error then @globals().modal().error("WebSocket [#{evt.origin}] - #{msg.error.method}", msg.error.exception) else E2.merge(@, msg) @process_meta() else msg = evt ws_method_impl.bind(@)(msg, ws, evt) if ws_method_impl @execute_commands() if @meta.execute delete @meta.execute @web_socket = -> ws @scope().$on "$destroy", -> ws.close() execute_commands: -> scope = @scope() _.reduce(@meta.execute, ((pr, cmd) -> pr.then -> scope.$eval(cmd)), $q.when()) console_log: (o) -> console.log o root: class RootAction extends Action initialize: -> super() _.merge(globals, @meta) @meta = {} invoke: (args) -> console.warn "Root action invoked" default_action: class DefaultAction extends Action initialize: -> super() # console.log "DEFAULT ACTION: #{@action_info().action_resource}" inspect: class InspectAction extends Action initialize: -> super() @tree = actions: [name: 'api', number: 0, access: true] @invoke_action('models') @invoke_action('environment') @local_storage = localStorageService open: (stack, node, collapsed, expand) -> tree = @tree path = [] _.each stack, (index) -> # fold ? tree = tree.actions[index] path.push tree.name # if !expand @number = tree.number if _.size(stack) > 1 $http.get("#{_.dropRight(path).join('/')}/meta").then (response) => @action_json = _.toArray(response.data.actions)[_.last(stack)] else @action_json = {} # if expand && collapsed $http.get("#{path.join('/')}/meta").then (response) => get_meta = if tree.recheck_access $http.get("#{path.join('/')}/meta", params: (access: true)).then (aresponse) -> response.data.actions[k].access = v for k, v of aresponse.data response else $q.when(response) get_meta.then (response) => _.each response.data.actions, (act, nm) -> act.name = nm tree.actions ?= _.toArray(response.data.actions) @meta_json = response.data.meta if @meta_json.state @action_state = {} _.each @meta_json.state, (s) => @action_state[s] = localStorageService.get("#{@globals().application}/#{path.join('/')}/#{s}") @action_state , (err) => delete @meta_json @handle_error(err, access: false) has_assoc: (model) -> _.size(model.assoc) > 0 ws_message: (msg, ws, evt) -> @event = evt menu: class MenuAction extends Action process_static_meta: -> initialize: -> super() $stateRegistry.load_routes = (init) => @invoke().then => menu = @meta.menus.menu _.each $stateRegistry.get(), (s) -> $stateRegistry.deregister(s.name) unless _.isEmpty(s.name) otherwise = menu.properties.default ? menu.entries[0].name $urlRouter.otherwise(otherwise) @register(menu.entries) @scope().routes = menu.entries out = if _.size(menu.entries) == 0 then angular.element("
") else $compile(@traverse(menu.entries))(@scope()) @element().replaceWith(out) @element = -> out loc = $location.path().slice(1) @globals().state().go(if init && !_.isEmpty($location.path()) && $stateRegistry.get(loc)? then loc else otherwise) $stateRegistry.load_routes(true) register: (routes) -> _.each routes, (route) => unless route.divider if route.menu then @register(route.menu.entries) else route.href = route.name if route.bootstrap? action = if route.bootstrap == true then '' else route.bootstrap + '/' $templateCache.put(route.name + '_route_template!', "
") $stateRegistry.register name: route.name templateUrl: if route.bootstrap? then route.name + '_route_template!' else route.name url: '/' + route.name # reloadOnSearch: true traverse: (routes) -> menu_tmpl = _.template("
  • {{icon}} {{loc}}
  • ") menu_sub_tmpl = _.template("
  • {{icon}} {{loc}}
  • ") animation = @meta.menus.menu.properties.animation out = routes.map (route, i) -> if route.menu menu_sub_tmpl dropdown: "routes[#{i}].menu.entries" animation: animation loc: route.menu.loc show: route.show && "ng-show=\"#{route.show}\"" || '' hide: route.hide && "ng-hide=\"#{route.hide}\"" || '' icon: route.menu.icon && E2.icon(route.menu.icon) || "" else menu_tmpl href: "ui-sref='#{route.name}'" loc: route.loc show: route.show && "ng-show=\"#{route.show}\"" || '' hide: route.hide && "ng-hide=\"#{route.hide}\"" || '' icon: route.icon && E2.icon(route.icon) || '' out.join('') list: class ListAction extends Action initialize: -> super() @query = page: 0, asc: true, search: {} #, search_tab: 0 @ui_state = {} @load_state() delete @query.order unless @meta.fields[@query.order]?.sort # _.includes(@meta.field_list, @query.order) _.each @query.search, ((sv, sn) => delete @query.search[sn] unless _.includes(@meta.search_field_list, sn)) destroy: -> @save_state() super() process_meta: -> super() meta = @meta meta.field_list = meta.field_list.filter((f) => !meta.fields[f].hidden) if meta.field_list # confirm_create, view, confirm_modify, confirm_delete, assocs - implicit render_table: -> @scope().$broadcast 'render_table' menu_search_toggle: -> @ui_state.search_active = !@ui_state.search_active @save_state() unless @ui_state.search_active menu_refresh: -> @invoke(refresh: true) menu_default_order: -> delete @query.order @invoke() menu_select_toggle: -> if @selection then delete @selection else @selection = {} @render_table() menu_show_meta: -> @globals().modal().show the_meta: @meta meta: panel: (panel_template: "close_m", template_string: "
    {{action.the_meta | json}}
    ", title: "Meta", class: "modal-huge", backdrop: true, footer: true) # show_assoc: (index, assoc) -> # # parent_id = E2.id_for(@entries[index], @meta) # # @create_action(assoc, @scope(), null, parent_id).then (action) => # # action.query.parent_id = parent_id # E2.id_for(@entries[index], @meta) # # action.invoke() # @current_id = E2.id_for(@entries[index], @meta) # @invoke_action(assoc) current_entry: -> @entries[@current_index] current_id: -> E2.id_for(@current_entry(), @meta) list_cell: (e, f) -> E2.render_field(e, f, @meta, "
    ") invoke: (args = {}) -> @save_state() query = _.cloneDeep(@query) delete query.search if _.isEmpty(E2.compact(query.search)) _.merge(query, args) super(query).then => @ui = _.pick @query, ['order', 'asc', 'page'] @ui.pagination_active = @ui.page != 0 || @entries.length >= @meta.config.per_page @render_table() load_new: -> @query.page = 0 @invoke() order: (col) -> @query.asc = if @query.order == col then !@query.asc else true @query.order = col @load_new() prev_active: -> !@action_pending() && @query.page > 0 prev: -> if @prev_active() @query.page = Math.max(0, @query.page - @meta.config.per_page) @invoke() next_active: -> !@action_pending() && @entries.length == @meta.config.per_page next: -> if @next_active() @query.page += @meta.config.per_page # min & count @invoke() page_info: -> page = @ui.page / @meta.config.per_page + 1 @meta.loc.page + ": " + if @count then "#{page} / #{Math.ceil(@count / @meta.config.per_page)} (#{@count})" else page || '' search_reset: -> E2.clean(@query.search) @scope().$broadcast "search_reset" @load_new() search_field_change: (f) -> info = @meta.fields[f] @scope().$eval(info.onchange) if info.onchange if remote_onchange = info.remote_onchange params = value: @query.search[f] params.record = @query.search if info.remote_onchange_record @invoke_action(remote_onchange, params).then => @load_new() if info.search_live else @load_new() if info.search_live selected_class: (index) -> (entry = @entries[index]) && @selection && @selection[E2.id_for(entry, @meta)] && 'info' select: (index, ev) -> if ev.target.nodeName == "TD" if @selection rec = @entries[index] id = E2.id_for(rec, @meta) if @selection[id] then delete @selection[id] else @selection[id] = rec selected_size: -> _.size(@selection) selected_info: -> @meta.loc.selected + ": " + @selected_size() entry_dropped: (moved_to, render = true) -> from = @entries[@moved_from] @entries.splice(@moved_from, 1) @entries.splice((if moved_to > @moved_from then moved_to - 1 else moved_to), 0, from) delete @moved_from @render_table() if render true entry_moved: (index) -> @moved_from = index bulk_delete: class BulkDeleteAction extends Action invoke: -> super(ids: [_.keys(@parent().parent().selection)]).then => @parent().parent().selection = {} view: class ViewAction extends Action view_cell: (e, f) -> E2.render_field(e, f, @meta, "
    ") form_base_action: class FormBaseAction extends Action initialize: -> super() _.each @meta.fields, (info, name) => if info.remote_onchange @scope().$watch (=> @record?[name]), (n) => if n? #if typeof(n) != "undefined" params = value: @record[name] params.record = @record if info.remote_onchange_record @invoke_action(info.remote_onchange, params) if info.onchange @scope().$watch (=> @record?[name]), (n) => if n? @scope().$eval(info.onchange) if @meta.tab_list @scope().$watch "action.activeTab", (tab) => if tab? # && tab >= 0 @panel_shown() @["panel_menu_#{@default_action_name}"] = -> @panel_menu_default_action() post_invoke: (args) -> super() _.each @meta.fields, (info, name) => if _.isString(@record[name]) && !info.dont_strip @record[name] = @record[name].trim() panel_menu_default_action: -> _.each @meta.fields, (v, n) => @record[n] = null if @record[n] is undefined params = record: @record params.parent_id ?= @parent().query?.parent_id # and StarToManyList ? @invoke_action(@default_action_name, params).then => dfd = $q.defer() if @errors if @meta.tab_list [i, first, curr] = [0, null, false] for tab_name in @meta.tab_list tab = @meta.tabs[tab_name] if _(tab.field_list).find((f) => @errors[f]) first = i if not first? act = true if @activeTab == i i++ @activeTab = first unless act if @activeTab? field = _(@meta.tabs[@meta.tab_list[@activeTab]].field_list).find((f) => @errors[f]) # console.log field undefined ? else @activeTab = 0 @alert = @errors else field = _(@meta.field_list).find((f) => @errors[f]) @alert = @errors if (!field || !@meta.fields[field] || @meta.fields[field].hidden) # ? $timeout => @scope().$broadcast("focus_field", field) #e.scope.$eval(meta.execute) if meta.execute # ? dfd.resolve() else dfd.resolve(@record) # $q.when(true) ? dfd.promise panel_shown: -> field = if @meta.tab_list tab = @meta.tabs[@meta.tab_list[@activeTab]] if @errors _(tab.field_list).find((f) => @errors[f]) || _(tab.field_list).find((f) => !@meta.fields[f].hidden) else tab ?= @meta.tabs[@meta.tab_list[0]] _(tab.field_list).find((f) => !@meta.fields[f].hidden && !@meta.fields[f].disabled) else _(@meta.field_list).find((f) => !@meta.fields[f].hidden && !@meta.fields[f].disabled) $timeout (=> @scope().$broadcast("focus_field", field)), 300 # hack, on shown ? infra: class InfraAction extends Action initialize: -> super() @scope().$on "relogin", (evt, reload_routes, create) => if @user @invoke_action('login_form').then (act) => act.record = name: @user.name act.meta.fields.name.disabled = true act.dont_reload_routes = !reload_routes # true else @invoke().then => @set_access(true, true, @user) @scope().$on "set_access", (evt, login, load_routes, user) => @set_access(login, load_routes, user) set_access: (login, load_routes, user) -> if user || !login @user = user @find_action_info('logout_form').access = login @find_action_info('inspect_modal').access = login @find_action_info('login_form').access = !login $stateRegistry.load_routes() if load_routes login_form: class LoginFormAction extends FormBaseAction panel_menu_default_action: -> super().then => $rootScope.$broadcast "set_access", true, !@dont_reload_routes, @user logout_form: class LogoutForm extends Action panel_menu_logout: -> @invoke_action('logout').then => $rootScope.$broadcast "set_access", false, true, null @panel_close() MetaCache.removeAll() form: class FormAction extends FormBaseAction create: class CreateAction extends FormAction invoke: (args) -> if parent_id = @parent().query.parent_id args ?= {} args.parent_id = parent_id super(args) modify: class ModifyAction extends FormAction # invoke: (args) -> # super(args).then => # _.each @meta.primary_fields, (f) => @meta.fields[f].disabled = true confirm: class ConfirmAction extends Action panel_menu_approve: -> @initial_arguments ?= @arguments @invoke_action(@default_action_name, _.pick(@, @initial_arguments)) decode_action: class DecodeAction extends Action initialize: -> super() @decode_field = @scope().f @dinfo = @parentp().meta.fields[@decode_field] throw "Primary and foreign key list lengths dont match: [#{@meta.primary_fields}] and [#{@dinfo.fields}]" unless @meta.primary_fields.length == @dinfo.fields.length @scope().$on "search_reset", => @clean() if_fk_values: (f) -> fk_values = @dinfo.fields.map((f) => @record()[f]) f(fk_values) if _(fk_values).every((f) -> f?) # null_value record: -> @parentp().query?.search || @parentp().record clear_record: -> _.each @dinfo.fields, (fk) => @record()[fk] = null # null_value reset: -> @clean() @parentp().search_field_change?(@decode_field) decode_description: (entry) -> @meta.decode_fields.map((f) => E2.render_field(entry, f, @meta, ', ')).join(@meta.separator) parentp: -> @parent().parent() decode_list: class DecodeListAction extends DecodeAction initialize: -> super() @multiple = @dinfo.render.multiple @clear_selected() @if_fk_values (fk_values) => @selected = if @multiple then E2.transpose(fk_values).map((e) -> E2.join_keys(e)) else E2.join_keys(fk_values) @invoke() clear_selected: -> @selected = if @multiple then [] else null # no need to null complex keys post_invoke: (args) -> super() @values = @entries.map (e) => id: E2.id_for(e, @meta), value: @decode_description(e) delete @entries change: -> record = @record() if @multiple if @selected.length > 0 _.each @dinfo.fields, (fk) -> record[fk] = [] _.each @selected, (sel) => _(@dinfo.fields).zip(E2.split_keys(sel)).each(([fk, k]) => record[fk].push E2.parse_entry(k, @parentp().meta.fields[fk])).value else @clear_record() else if @selected _(@dinfo.fields).zip(E2.split_keys(@selected)).each(([fk, k]) => record[fk] = E2.parse_entry(k, @parentp().meta.fields[fk])).value else @clear_record() @parentp().search_field_change?(@decode_field) clean: -> @clear_selected() @clear_record() decode_entry: class DecodeEntryAction extends DecodeAction initialize: -> super() @multiple = @dinfo.render.multiple @if_fk_values (fk_values) => @invoke_decode (if @multiple then E2.transpose(fk_values) else [fk_values]) @scope().$on "picked", (ev, sel, sel_meta) => ev.stopPropagation() record = @record() if @multiple _.each @dinfo.fields, (fk) => record[fk] = [] _.each sel, (rec, ids) => _(@dinfo.fields).zip(E2.split_keys(ids)).each(([k, v]) => record[k].push E2.parse_entry(v, @parentp().meta.fields[k])).value @invoke_decode _.values(sel) delete @decode if _.isEmpty(sel) else [ids, rec] = _(sel).toPairs().head() _(@dinfo.fields).zip(E2.split_keys(ids)).each(([k, v]) => record[k] = E2.parse_entry(v, @parentp().meta.fields[k])).value @invoke_decode [rec] @parentp().search_field_change?(@decode_field) invoke_decode: (recs, f) -> if @multiple && _.size(recs) > @meta.show_max_selected @decode = "#{_.size(recs)} #{@meta.loc.decode_selected}" else decode_descriptions = (recs) => @decode = recs.map((fields) => @decode_description(fields)).join(' | ') recs = recs.map (r) => if _.isArray(r) then E2.from_id(r, @meta) else r if _(recs).every((r) => _(@meta.field_list).every((f) -> r[f]?)) && !@meta.dynamic_meta then decode_descriptions(recs) else @invoke(ids: [recs.map((r) => @meta.primary_fields.map (k) -> r[k])]).then => decode_descriptions(@entries) open: -> fk_values = @dinfo.fields.map((f) => @record()[f]).filter((f) -> f?) @create_action('list', @scope()).then (action) => if @multiple action.selection = E2.transpose(fk_values).reduce(((rec, keys) => rec[E2.join_keys(keys)] = E2.from_id(keys, @meta); rec), {}) else action.selection[E2.join_keys(fk_values)] = E2.from_id(fk_values, @meta) if fk_values.length > 0 action.invoke() clean: -> delete @decode @clear_record() typeahead: class TypeAheadAction extends DecodeAction initialize: -> super() @if_fk_values (fk_values) => @invoke(id: E2.join_keys(fk_values)).then => if @entry @decode = id: E2.id_for(@entry, @meta), value: @decode_description(@entry) @scope().$on "$typeahead.select", (e, v, index) => e.stopPropagation() _(@dinfo.fields).zip(E2.split_keys(@values[index].id)).each(([fk, k]) => @record()[fk] = E2.parse_entry(k, @parentp().meta.fields[fk])).value @parentp().search_field_change?(@decode_field) @scope().$watch "action.decode", (e) => if e? @reset() if e.length == 0 load: (value) -> if value? && value.length > 0 && @key_pressed # check again after strap updates ? @invoke(query: value).then => if @entries # ? @values = @entries.map (e) => id: E2.id_for(e, @meta), value: @decode_description(e) delete @entries @values clean: -> delete @decode @clear_record() many_to_one_list: class ManyToOneListAction extends ListAction initialize: -> super() @selection = {} select: (index, ev) -> if ev.target.nodeName == "TD" @selection = {} unless @parent().multiple super(index, ev) unless @parent().multiple @scope().$emit "picked", @selection, @meta @panel_close() panel_menu_choose: (e) -> # if _.size(@selection) > 0 @scope().$emit "picked", @selection, @meta @panel_close() star_to_many_list: class StarToManyList extends ListAction initialize: -> super() @query.parent_id = @parent().current_id() # link_list: implicit item_menu_confirm_unlink: (args) -> args.parent_id = @query.parent_id @item_menu_confirm_unlink_super(args) star_to_many_bulk_unlink: class StarToManyBulkUnlinkAction extends Action invoke: -> parent = @parent().parent() super(ids: [_.keys(parent.selection)], parent_id: parent.query.parent_id).then => parent.selection = {} star_to_many_link_list: class StarToManyLinkList extends ListAction initialize: -> super() @query.negate = true @query.parent_id = @parent().query.parent_id @selection = {} panel_menu_link: -> selection = _.keys(@selection) if selection.length > 0 @invoke_action('link', parent_id: @query.parent_id, ids: selection) star_to_many_field: class StarToManyField extends ListAction initialize: -> super() @query.parent_id = E2.id_for(@parent().record, @parent().meta) @changes = (@parent().record[@scope().$parent.f] ?= (link: [], unlink: [], create: [], modify: [], delete: [])) @invoke() invoke: -> @query.changes = @changes super() entry_dropped: (moved_to) -> pos_field = @meta.draggable.position_field positions = @entries.map (e) -> e[pos_field] super(moved_to, false) _.each positions, (p, i) => if @entries[i][pos_field] != p if entry = @current_entry_is('create', @entries[i]) ? @current_entry_is('modify', @entries[i]) entry[pos_field] = p else @changes.modify.push(@entries[i]) @entries[i][pos_field] = p @render_table() # @invoke() true current_entry_is: (mode, entry = @current_entry()) -> key = E2.id_for(entry, @meta) _.find(@changes[mode], (e) => E2.id_for(e, @meta) == key) star_to_many_field_view: class StarToManyFieldView extends ViewAction invoke: (args) -> if entry = @parent().current_entry_is('create') ? @parent().current_entry_is('modify') @meta.invokable = false @record = entry super(args) star_to_many_field_modify: class StarToManyFieldModifyAction extends ModifyAction invoke: (args) -> if entry = @parent().current_entry_is('create') ? @parent().current_entry_is('modify') @meta.invokable = false @record = entry super(args) star_to_many_field_approve: class StarToManyFieldApprove extends Action post_invoke: (args) -> super(args) unless @errors pparent = @parent().parent() if @parent() instanceof StarToManyFieldModifyAction if entry = pparent.current_entry_is('create') ? pparent.current_entry_is('modify') _.assign(entry, @parent().record) else pparent.changes.modify.push @parent().record else # CreateAction _(@parent().meta.primary_fields).each (k) => @parent().record[k] = E2.uuid(5) if draggable = pparent.meta.draggable max = _.maxBy(pparent.entries, (e) -> e.position) @parent().record[draggable.position_field] = if max then max[draggable.position_field] + 1 else 1 pparent.changes.create.push @parent().record star_to_many_field_delete: class StarToManyFieldDelete extends Action invoke: (args) -> pparent = @parent().parent() if entry = pparent.current_entry_is('create') _.remove(pparent.changes.create, entry) else if entry = pparent.current_entry_is('modify') _.remove(pparent.changes.modify, entry) pparent.changes.delete.push args.id else pparent.changes.delete.push args.id @meta.invokable = false super(args) star_to_many_field_link_list: class StarToManyFieldLinkList extends ListAction initialize: -> super() @query.negate = true @query.parent_id = @parent().query.parent_id @selection = {} invoke: -> @query.changes = @parent().changes super() panel_menu_link: -> if @selected_size() > 0 _.each @selection, (v, k) => id = k if _.includes(@parent().changes.unlink, id) then _.pull(@parent().changes.unlink, id) else @parent().changes.link.push id @parent().invoke() @panel_close() star_to_many_field_unlink: class StarToManyFieldUnlink extends Action invoke: (args) -> id = args.id pparent = @parent().parent() if _.includes(pparent.changes.link, id) then _.pull(pparent.changes.link, id) else pparent.changes.unlink.push id @meta.invokable = false super(args) file_store: class FileStoreAction extends Action initialize: -> super() @progress = 0 id = E2.id_for(@parent().record, @parent().meta) files = @parent().record[@scope().f] @invoke(owner: id).then => @parent().record[@scope().f] = if files? then files else @files delete @files select: (files) -> _.each files, (file) => upload = $injector.get('Upload').upload url: "#{@action_info().action_resource}/upload", file: file upload.progress (e) => @globals().action_pending = false @progress = parseInt(100.0 * e.loaded / e.total) upload.success (data, status, headers, config) => @parent().record[@scope().f].push mime: file.type, name: file.name, rackname: data.rackname, id: data.id, new: true @message = "Wysłano, #{file.name}" @globals().action_pending = false delete_file: (file) -> @scope().$broadcast 'confirm_delete', confirm: => if file.new then _.pull(@parent().record[@scope().f], file) else file.deleted = true @scope().$broadcast 'confirm_delete_close' show_file: (file) -> @file = file @scope().$broadcast 'show_file', file: file blob_store: class BlobStore extends Action initialize: -> super() @record_id = E2.id_for(@parent().record, @parent().meta) @progress = 0 if @record_id.length > 0 @invoke(owner: @record_id) # .then => @sync_record() else # @file = {} # @sync_record() sync_record: -> @parent().record[@scope().f] = @file select: (files) -> _.each files, (file) => upload = $injector.get('Upload').upload url: "#{@action_info().action_resource}/upload", file: file upload.progress (e) => @progress = parseInt(100.0 * e.loaded / e.total) upload.success (data, status, headers, config) => @file = mime: file.type, name: file.name, rackname: data.rackname, id: data.id @message = "Wysłano, #{file.name}" @sync_record()