Commit 4f26e0865e16a9f41f456279f52937ae2fc1fddd

Authored by Karpikau Andrei
1 parent 1ffe9c0c

add support alternative language domain

1 PATH 1 PATH
2 remote: . 2 remote: .
3 specs: 3 specs:
4 - kanjai (0.0.198) 4 + kanjai (0.0.203)
5 acts-as-taggable-on (~> 6.5) 5 acts-as-taggable-on (~> 6.5)
6 acts_as_list 6 acts_as_list
7 acts_as_tree 7 acts_as_tree
@@ -65,53 +65,53 @@ GEM @@ -65,53 +65,53 @@ GEM
65 activerecord (>= 3.0.0) 65 activerecord (>= 3.0.0)
66 arel (9.0.0) 66 arel (9.0.0)
67 aws-eventstream (1.1.0) 67 aws-eventstream (1.1.0)
68 - aws-partitions (1.326.0) 68 + aws-partitions (1.349.0)
69 aws-sdk (3.0.1) 69 aws-sdk (3.0.1)
70 aws-sdk-resources (~> 3) 70 aws-sdk-resources (~> 3)
71 - aws-sdk-accessanalyzer (1.7.0)  
72 - aws-sdk-core (~> 3, >= 3.71.0) 71 + aws-sdk-accessanalyzer (1.9.0)
  72 + aws-sdk-core (~> 3, >= 3.99.0)
73 aws-sigv4 (~> 1.1) 73 aws-sigv4 (~> 1.1)
74 - aws-sdk-acm (1.31.0)  
75 - aws-sdk-core (~> 3, >= 3.71.0) 74 + aws-sdk-acm (1.34.0)
  75 + aws-sdk-core (~> 3, >= 3.99.0)
76 aws-sigv4 (~> 1.1) 76 aws-sigv4 (~> 1.1)
77 - aws-sdk-acmpca (1.24.0)  
78 - aws-sdk-core (~> 3, >= 3.71.0) 77 + aws-sdk-acmpca (1.26.0)
  78 + aws-sdk-core (~> 3, >= 3.99.0)
79 aws-sigv4 (~> 1.1) 79 aws-sigv4 (~> 1.1)
80 - aws-sdk-alexaforbusiness (1.36.0)  
81 - aws-sdk-core (~> 3, >= 3.71.0) 80 + aws-sdk-alexaforbusiness (1.40.0)
  81 + aws-sdk-core (~> 3, >= 3.99.0)
82 aws-sigv4 (~> 1.1) 82 aws-sigv4 (~> 1.1)
83 - aws-sdk-amplify (1.17.0)  
84 - aws-sdk-core (~> 3, >= 3.71.0) 83 + aws-sdk-amplify (1.21.0)
  84 + aws-sdk-core (~> 3, >= 3.99.0)
85 aws-sigv4 (~> 1.1) 85 aws-sigv4 (~> 1.1)
86 - aws-sdk-apigateway (1.42.0)  
87 - aws-sdk-core (~> 3, >= 3.71.0) 86 + aws-sdk-apigateway (1.48.0)
  87 + aws-sdk-core (~> 3, >= 3.99.0)
88 aws-sigv4 (~> 1.1) 88 aws-sigv4 (~> 1.1)
89 - aws-sdk-apigatewaymanagementapi (1.14.0)  
90 - aws-sdk-core (~> 3, >= 3.71.0) 89 + aws-sdk-apigatewaymanagementapi (1.16.0)
  90 + aws-sdk-core (~> 3, >= 3.99.0)
91 aws-sigv4 (~> 1.1) 91 aws-sigv4 (~> 1.1)
92 - aws-sdk-apigatewayv2 (1.21.0)  
93 - aws-sdk-core (~> 3, >= 3.71.0) 92 + aws-sdk-apigatewayv2 (1.23.0)
  93 + aws-sdk-core (~> 3, >= 3.99.0)
94 aws-sigv4 (~> 1.1) 94 aws-sigv4 (~> 1.1)
95 - aws-sdk-appconfig (1.6.0)  
96 - aws-sdk-core (~> 3, >= 3.71.0) 95 + aws-sdk-appconfig (1.9.0)
  96 + aws-sdk-core (~> 3, >= 3.99.0)
97 aws-sigv4 (~> 1.1) 97 aws-sigv4 (~> 1.1)
98 - aws-sdk-applicationautoscaling (1.40.0)  
99 - aws-sdk-core (~> 3, >= 3.71.0) 98 + aws-sdk-applicationautoscaling (1.44.0)
  99 + aws-sdk-core (~> 3, >= 3.99.0)
100 aws-sigv4 (~> 1.1) 100 aws-sigv4 (~> 1.1)
101 - aws-sdk-applicationdiscoveryservice (1.27.0)  
102 - aws-sdk-core (~> 3, >= 3.71.0) 101 + aws-sdk-applicationdiscoveryservice (1.30.0)
  102 + aws-sdk-core (~> 3, >= 3.99.0)
103 aws-sigv4 (~> 1.1) 103 aws-sigv4 (~> 1.1)
104 - aws-sdk-applicationinsights (1.10.0)  
105 - aws-sdk-core (~> 3, >= 3.71.0) 104 + aws-sdk-applicationinsights (1.12.0)
  105 + aws-sdk-core (~> 3, >= 3.99.0)
106 aws-sigv4 (~> 1.1) 106 aws-sigv4 (~> 1.1)
107 - aws-sdk-appmesh (1.24.0)  
108 - aws-sdk-core (~> 3, >= 3.71.0) 107 + aws-sdk-appmesh (1.28.0)
  108 + aws-sdk-core (~> 3, >= 3.99.0)
109 aws-sigv4 (~> 1.1) 109 aws-sigv4 (~> 1.1)
110 - aws-sdk-appstream (1.41.0)  
111 - aws-sdk-core (~> 3, >= 3.71.0) 110 + aws-sdk-appstream (1.44.0)
  111 + aws-sdk-core (~> 3, >= 3.99.0)
112 aws-sigv4 (~> 1.1) 112 aws-sigv4 (~> 1.1)
113 - aws-sdk-appsync (1.26.0)  
114 - aws-sdk-core (~> 3, >= 3.71.0) 113 + aws-sdk-appsync (1.30.0)
  114 + aws-sdk-core (~> 3, >= 3.99.0)
115 aws-sigv4 (~> 1.1) 115 aws-sigv4 (~> 1.1)
116 aws-sdk-athena (1.27.0) 116 aws-sdk-athena (1.27.0)
117 aws-sdk-core (~> 3, >= 3.71.0) 117 aws-sdk-core (~> 3, >= 3.71.0)
@@ -227,7 +227,7 @@ GEM @@ -227,7 +227,7 @@ GEM
227 aws-sdk-connectparticipant (1.3.0) 227 aws-sdk-connectparticipant (1.3.0)
228 aws-sdk-core (~> 3, >= 3.71.0) 228 aws-sdk-core (~> 3, >= 3.71.0)
229 aws-sigv4 (~> 1.1) 229 aws-sigv4 (~> 1.1)
230 - aws-sdk-core (3.98.0) 230 + aws-sdk-core (3.104.3)
231 aws-eventstream (~> 1, >= 1.0.2) 231 aws-eventstream (~> 1, >= 1.0.2)
232 aws-partitions (~> 1, >= 1.239.0) 232 aws-partitions (~> 1, >= 1.239.0)
233 aws-sigv4 (~> 1.1) 233 aws-sigv4 (~> 1.1)
@@ -968,8 +968,8 @@ GEM @@ -968,8 +968,8 @@ GEM
968 aws-sdk-core (~> 3, >= 3.71.0) 968 aws-sdk-core (~> 3, >= 3.71.0)
969 aws-sigv4 (~> 1.1) 969 aws-sigv4 (~> 1.1)
970 aws-sigv2 (1.0.1) 970 aws-sigv2 (1.0.1)
971 - aws-sigv4 (1.1.4)  
972 - aws-eventstream (~> 1.0, >= 1.0.2) 971 + aws-sigv4 (1.2.1)
  972 + aws-eventstream (~> 1, >= 1.0.2)
973 bcrypt (3.1.15) 973 bcrypt (3.1.15)
974 builder (3.2.4) 974 builder (3.2.4)
975 concurrent-ruby (1.1.6) 975 concurrent-ruby (1.1.6)
@@ -984,10 +984,10 @@ GEM @@ -984,10 +984,10 @@ GEM
984 ffi (1.13.0) 984 ffi (1.13.0)
985 globalid (0.4.2) 985 globalid (0.4.2)
986 activesupport (>= 4.2.0) 986 activesupport (>= 4.2.0)
987 - i18n (1.8.3) 987 + i18n (1.8.5)
988 concurrent-ruby (~> 1.0) 988 concurrent-ruby (~> 1.0)
989 jmespath (1.4.0) 989 jmespath (1.4.0)
990 - loofah (2.5.0) 990 + loofah (2.6.0)
991 crass (~> 1.0.2) 991 crass (~> 1.0.2)
992 nokogiri (>= 1.5.9) 992 nokogiri (>= 1.5.9)
993 mail (2.7.1) 993 mail (2.7.1)
@@ -1001,11 +1001,11 @@ GEM @@ -1001,11 +1001,11 @@ GEM
1001 mini_portile2 (2.4.0) 1001 mini_portile2 (2.4.0)
1002 minitest (5.14.1) 1002 minitest (5.14.1)
1003 nio4r (2.5.2) 1003 nio4r (2.5.2)
1004 - nokogiri (1.10.9) 1004 + nokogiri (1.10.10)
1005 mini_portile2 (~> 2.4.0) 1005 mini_portile2 (~> 2.4.0)
1006 orm_adapter (0.5.0) 1006 orm_adapter (0.5.0)
1007 pg (1.0.0) 1007 pg (1.0.0)
1008 - rack (2.2.2) 1008 + rack (2.2.3)
1009 rack-test (1.1.0) 1009 rack-test (1.1.0)
1010 rack (>= 1.0, < 3) 1010 rack (>= 1.0, < 3)
1011 rails (5.2.4.3) 1011 rails (5.2.4.3)
@@ -1066,7 +1066,7 @@ GEM @@ -1066,7 +1066,7 @@ GEM
1066 railties 1066 railties
1067 warden (1.2.8) 1067 warden (1.2.8)
1068 rack (>= 2.0.6) 1068 rack (>= 2.0.6)
1069 - websocket-driver (0.7.2) 1069 + websocket-driver (0.7.3)
1070 websocket-extensions (>= 0.1.0) 1070 websocket-extensions (>= 0.1.0)
1071 websocket-extensions (0.1.5) 1071 websocket-extensions (0.1.5)
1072 1072
  1 +(function($) {
  2 +
  3 + var cocoon_element_counter = 0;
  4 +
  5 + var create_new_id = function() {
  6 + return (new Date().getTime() + cocoon_element_counter++);
  7 + }
  8 +
  9 + var newcontent_braced = function(id) {
  10 + return '[' + id + ']$1';
  11 + }
  12 +
  13 + var newcontent_underscord = function(id) {
  14 + return '_' + id + '_$1';
  15 + }
  16 +
  17 + var getInsertionNodeElem = function(insertionNode, insertionTraversal, $this){
  18 +
  19 + if (!insertionNode){
  20 + return $this.parent();
  21 + }
  22 +
  23 + if (typeof insertionNode == 'function'){
  24 + if(insertionTraversal){
  25 + console.warn('association-insertion-traversal is ignored, because association-insertion-node is given as a function.')
  26 + }
  27 + return insertionNode($this);
  28 + }
  29 +
  30 + if(typeof insertionNode == 'string'){
  31 + if (insertionTraversal){
  32 + return $this[insertionTraversal](insertionNode);
  33 + }else{
  34 + return insertionNode == "this" ? $this : $(insertionNode);
  35 + }
  36 + }
  37 +
  38 + }
  39 +
  40 + $(document).on('click', '.add_fields', function(e) {
  41 + e.preventDefault();
  42 + var $this = $(this),
  43 + assoc = $this.data('association'),
  44 + assocs = $this.data('associations'),
  45 + content = $this.data('association-insertion-template'),
  46 + insertionMethod = $this.data('association-insertion-method') || $this.data('association-insertion-position') || 'before',
  47 + insertionNode = $this.data('association-insertion-node'),
  48 + insertionTraversal = $this.data('association-insertion-traversal'),
  49 + count = parseInt($this.data('count'), 10),
  50 + regexp_braced = new RegExp('\\[new_' + assoc + '\\](.*?\\s)', 'g'),
  51 + regexp_underscord = new RegExp('_new_' + assoc + '_(\\w*)', 'g'),
  52 + new_id = create_new_id(),
  53 + new_content = content.replace(regexp_braced, newcontent_braced(new_id)),
  54 + new_contents = [],
  55 + originalEvent = e;
  56 +
  57 +
  58 + if (new_content == content) {
  59 + regexp_braced = new RegExp('\\[new_' + assocs + '\\](.*?\\s)', 'g');
  60 + regexp_underscord = new RegExp('_new_' + assocs + '_(\\w*)', 'g');
  61 + new_content = content.replace(regexp_braced, newcontent_braced(new_id));
  62 + }
  63 +
  64 + new_content = new_content.replace(regexp_underscord, newcontent_underscord(new_id));
  65 + new_contents = [new_content];
  66 +
  67 + count = (isNaN(count) ? 1 : Math.max(count, 1));
  68 + count -= 1;
  69 +
  70 + while (count) {
  71 + new_id = create_new_id();
  72 + new_content = content.replace(regexp_braced, newcontent_braced(new_id));
  73 + new_content = new_content.replace(regexp_underscord, newcontent_underscord(new_id));
  74 + new_contents.push(new_content);
  75 +
  76 + count -= 1;
  77 + }
  78 +
  79 + var insertionNodeElem = getInsertionNodeElem(insertionNode, insertionTraversal, $this)
  80 +
  81 + if( !insertionNodeElem || (insertionNodeElem.length == 0) ){
  82 + console.warn("Couldn't find the element to insert the template. Make sure your `data-association-insertion-*` on `link_to_add_association` is correct.")
  83 + }
  84 +
  85 + $.each(new_contents, function(i, node) {
  86 + var contentNode = $(node);
  87 +
  88 + var before_insert = jQuery.Event('cocoon:before-insert');
  89 + insertionNodeElem.trigger(before_insert, [contentNode, originalEvent]);
  90 +
  91 + if (!before_insert.isDefaultPrevented()) {
  92 + // allow any of the jquery dom manipulation methods (after, before, append, prepend, etc)
  93 + // to be called on the node. allows the insertion node to be the parent of the inserted
  94 + // code and doesn't force it to be a sibling like after/before does. default: 'before'
  95 + var addedContent = insertionNodeElem[insertionMethod](contentNode);
  96 +
  97 + insertionNodeElem.trigger('cocoon:after-insert', [contentNode,
  98 + originalEvent]);
  99 + }
  100 + });
  101 + });
  102 +
  103 + $(document).on('click', '.remove_fields.dynamic, .remove_fields.existing', function(e) {
  104 + var $this = $(this),
  105 + wrapper_class = $this.data('wrapper-class') || 'nested-fields',
  106 + node_to_delete = $this.closest('.' + wrapper_class),
  107 + trigger_node = node_to_delete.parent(),
  108 + originalEvent = e;
  109 +
  110 + e.preventDefault();
  111 + e.stopPropagation();
  112 +
  113 + var before_remove = jQuery.Event('cocoon:before-remove');
  114 + trigger_node.trigger(before_remove, [node_to_delete, originalEvent]);
  115 +
  116 + if (!before_remove.isDefaultPrevented()) {
  117 + var timeout = trigger_node.data('remove-timeout') || 0;
  118 +
  119 + setTimeout(function() {
  120 + if ($this.hasClass('dynamic')) {
  121 + node_to_delete.detach();
  122 + } else {
  123 + $this.prev("input[type=hidden]").val("1");
  124 + node_to_delete.hide();
  125 + }
  126 + trigger_node.trigger('cocoon:after-remove', [node_to_delete,
  127 + originalEvent]);
  128 + }, timeout);
  129 + }
  130 + });
  131 +
  132 +
  133 + $(document).on("ready page:load turbolinks:load", function() {
  134 + $('.remove_fields.existing.destroyed').each(function(i, obj) {
  135 + var $this = $(this),
  136 + wrapper_class = $this.data('wrapper-class') || 'nested-fields';
  137 +
  138 + $this.closest('.' + wrapper_class).hide();
  139 + });
  140 + });
  141 +
  142 +})(jQuery);
  143 +
@@ -14,6 +14,8 @@ @@ -14,6 +14,8 @@
14 14
15 //= require kanjai/admin/jquery.fileupload 15 //= require kanjai/admin/jquery.fileupload
16 16
  17 +//= require kanjai/cocoon
  18 +
17 //= require kanjai/froala_editor/froala_editor.min 19 //= require kanjai/froala_editor/froala_editor.min
18 //= require kanjai/froala_editor/plugins/align.min 20 //= require kanjai/froala_editor/plugins/align.min
19 //= require kanjai/froala_editor/plugins/char_counter.min 21 //= require kanjai/froala_editor/plugins/char_counter.min
@@ -45,7 +45,7 @@ module Kanjai @@ -45,7 +45,7 @@ module Kanjai
45 private 45 private
46 46
47 def permitted_params 47 def permitted_params
48 - params.permit(:domain => [:title]) 48 + params.permit(:domain => [:title, language_domains_attributes: [:id, :title, :_destroy]])
49 end 49 end
50 50
51 end 51 end
@@ -55,7 +55,7 @@ module Kanjai @@ -55,7 +55,7 @@ module Kanjai
55 private 55 private
56 56
57 def permitted_params 57 def permitted_params
58 - params.permit(:page_lang => [:code, :title, :default_use, :domain_id]) 58 + params.permit(:page_lang => [:code, :title, :default_use, :domain_id, :language_domain_id])
59 end 59 end
60 60
61 end 61 end
1 module Kanjai 1 module Kanjai
2 module ApplicationHelper 2 module ApplicationHelper
  3 +
  4 +
3 def csrf_meta_tag 5 def csrf_meta_tag
4 if protect_against_forgery? 6 if protect_against_forgery?
5 out = %(<meta name="csrf-param" content="%s"/>\n) 7 out = %(<meta name="csrf-param" content="%s"/>\n)
@@ -91,5 +93,162 @@ module Kanjai @@ -91,5 +93,162 @@ module Kanjai
91 return markers 93 return markers
92 end 94 end
93 95
  96 +
  97 + # COCCON
  98 +
  99 +
  100 + # this will show a link to remove the current association. This should be placed inside the partial.
  101 + # either you give
  102 + # - *name* : the text of the link
  103 + # - *f* : the form this link should be placed in
  104 + # - *html_options*: html options to be passed to link_to (see <tt>link_to</tt>)
  105 + #
  106 + # or you use the form without *name* with a *&block*
  107 + # - *f* : the form this link should be placed in
  108 + # - *html_options*: html options to be passed to link_to (see <tt>link_to</tt>)
  109 + # - *&block*: the output of the block will be show in the link, see <tt>link_to</tt>
  110 +
  111 + def link_to_remove_association(*args, &block)
  112 + if block_given?
  113 + link_to_remove_association(capture(&block), *args)
  114 + elsif args.first.respond_to?(:object)
  115 + form = args.first
  116 + association = form.object.class.to_s.tableize
  117 + name = I18n.translate("cocoon.#{association}.remove", default: I18n.translate('cocoon.defaults.remove'))
  118 +
  119 + link_to_remove_association(name, *args)
  120 + else
  121 + name, f, html_options = *args
  122 + html_options ||= {}
  123 +
  124 + is_dynamic = f.object.new_record?
  125 +
  126 + classes = []
  127 + classes << "remove_fields"
  128 + classes << (is_dynamic ? 'dynamic' : 'existing')
  129 + classes << 'destroyed' if f.object.marked_for_destruction?
  130 + html_options[:class] = [html_options[:class], classes.join(' ')].compact.join(' ')
  131 +
  132 + wrapper_class = html_options.delete(:wrapper_class)
  133 + html_options[:'data-wrapper-class'] = wrapper_class if wrapper_class.present?
  134 +
  135 + f.hidden_field(:_destroy, value: f.object._destroy) + link_to(name, '#', html_options)
  136 + end
  137 + end
  138 +
  139 + # :nodoc:
  140 + def render_association(association, f, new_object, form_name, render_options={}, custom_partial=nil)
  141 + partial = get_partial_path(custom_partial, association)
  142 + locals = render_options.delete(:locals) || {}
  143 + ancestors = f.class.ancestors.map{|c| c.to_s}
  144 + method_name = ancestors.include?('SimpleForm::FormBuilder') ? :simple_fields_for : (ancestors.include?('Formtastic::FormBuilder') ? :semantic_fields_for : :fields_for)
  145 + f.send(method_name, association, new_object, {:child_index => "new_#{association}"}.merge(render_options)) do |builder|
  146 + partial_options = {form_name.to_sym => builder, :dynamic => true}.merge(locals)
  147 + render(partial, partial_options)
  148 + end
  149 + end
  150 +
  151 + # shows a link that will allow to dynamically add a new associated object.
  152 + #
  153 + # - *name* : the text to show in the link
  154 + # - *f* : the form this should come in (the formtastic form)
  155 + # - *association* : the associated objects, e.g. :tasks, this should be the name of the <tt>has_many</tt> relation.
  156 + # - *html_options*: html options to be passed to <tt>link_to</tt> (see <tt>link_to</tt>)
  157 + # - *:render_options* : options passed to `simple_fields_for, semantic_fields_for or fields_for`
  158 + # - *:locals* : the locals hash in the :render_options is handed to the partial
  159 + # - *:partial* : explicitly override the default partial name
  160 + # - *:wrap_object* : a proc that will allow to wrap your object, especially suited when using
  161 + # decorators, or if you want special initialisation
  162 + # - *:form_name* : the parameter for the form in the nested form partial. Default `f`.
  163 + # - *:count* : Count of how many objects will be added on a single click. Default `1`.
  164 + # - *&block*: see <tt>link_to</tt>
  165 +
  166 + def link_to_add_association(*args, &block)
  167 + if block_given?
  168 + link_to_add_association(capture(&block), *args)
  169 + elsif args.first.respond_to?(:object)
  170 + association = args.second
  171 + name = I18n.translate("cocoon.#{association}.add", default: I18n.translate('cocoon.defaults.add'))
  172 +
  173 + link_to_add_association(name, *args)
  174 + else
  175 + name, f, association, html_options = *args
  176 + html_options ||= {}
  177 +
  178 + render_options = html_options.delete(:render_options)
  179 + render_options ||= {}
  180 + override_partial = html_options.delete(:partial)
  181 + wrap_object = html_options.delete(:wrap_object)
  182 + force_non_association_create = html_options.delete(:force_non_association_create) || false
  183 + form_parameter_name = html_options.delete(:form_name) || 'f'
  184 + count = html_options.delete(:count).to_i
  185 +
  186 + html_options[:class] = [html_options[:class], "add_fields"].compact.join(' ')
  187 + html_options[:'data-association'] = association.to_s.singularize
  188 + html_options[:'data-associations'] = association.to_s.pluralize
  189 +
  190 + new_object = create_object(f, association, force_non_association_create)
  191 + new_object = wrap_object.call(new_object) if wrap_object.respond_to?(:call)
  192 +
  193 + html_options[:'data-association-insertion-template'] = CGI.escapeHTML(render_association(association, f, new_object, form_parameter_name, render_options, override_partial).to_str).html_safe
  194 +
  195 + html_options[:'data-count'] = count if count > 0
  196 +
  197 + link_to(name, '#', html_options)
  198 + end
  199 + end
  200 +
  201 + # creates new association object with its conditions, like
  202 + # `` has_many :admin_comments, class_name: "Comment", conditions: { author: "Admin" }
  203 + # will create new Comment with author "Admin"
  204 +
  205 + def create_object(f, association, force_non_association_create=false)
  206 + assoc = f.object.class.reflect_on_association(association)
  207 +
  208 + assoc ? create_object_on_association(f, association, assoc, force_non_association_create) : create_object_on_non_association(f, association)
  209 + end
  210 +
  211 + def get_partial_path(partial, association)
  212 + partial ? partial : association.to_s.singularize + "_fields"
  213 + end
  214 +
  215 + private
  216 +
  217 + def create_object_on_non_association(f, association)
  218 + builder_method = %W{build_#{association} build_#{association.to_s.singularize}}.select { |m| f.object.respond_to?(m) }.first
  219 + return f.object.send(builder_method) if builder_method
  220 + raise "Association #{association} doesn't exist on #{f.object.class}"
  221 + end
  222 +
  223 + def create_object_on_association(f, association, instance, force_non_association_create)
  224 + if instance.class.name.starts_with?('Mongoid::') || force_non_association_create
  225 + create_object_with_conditions(instance)
  226 + else
  227 + assoc_obj = nil
  228 +
  229 + # assume ActiveRecord or compatible
  230 + if instance.collection?
  231 + assoc_obj = f.object.send(association).build
  232 + f.object.send(association).delete(assoc_obj)
  233 + else
  234 + assoc_obj = f.object.send("build_#{association}")
  235 + f.object.send(association).delete
  236 + end
  237 +
  238 + assoc_obj = assoc_obj.dup if assoc_obj.frozen?
  239 +
  240 + assoc_obj
  241 + end
  242 + end
  243 +
  244 + def create_object_with_conditions(instance)
  245 + # in rails 4, an association is defined with a proc
  246 + # and I did not find how to extract the conditions from a scope
  247 + # except building from the scope, but then why not just build from the
  248 + # association???
  249 + conditions = instance.respond_to?(:conditions) ? instance.conditions.flatten : []
  250 + instance.klass.new(*conditions)
  251 + end
  252 +
94 end 253 end
95 end 254 end
@@ -4,5 +4,7 @@ module Kanjai @@ -4,5 +4,7 @@ module Kanjai
4 4
5 has_many :pages, dependent: :destroy 5 has_many :pages, dependent: :destroy
6 has_many :page_langs, dependent: :destroy 6 has_many :page_langs, dependent: :destroy
  7 + has_many :language_domains, dependent: :destroy
  8 + accepts_nested_attributes_for :language_domains, reject_if: :all_blank, allow_destroy: true
7 end 9 end
8 end 10 end
  1 +module Kanjai
  2 + class LanguageDomain < ApplicationRecord
  3 + belongs_to :domain
  4 + has_many :page_langs, dependent: :nullify
  5 + end
  6 +end
@@ -2,6 +2,7 @@ module Kanjai @@ -2,6 +2,7 @@ module Kanjai
2 class PageLang < ActiveRecord::Base 2 class PageLang < ActiveRecord::Base
3 3
4 belongs_to :domain 4 belongs_to :domain
  5 + belongs_to :language_domain
5 6
6 validates :code, :title, presence: true 7 validates :code, :title, presence: true
7 8
@@ -6,6 +6,15 @@ @@ -6,6 +6,15 @@
6 <%= error_messages(@domain, :title) %> 6 <%= error_messages(@domain, :title) %>
7 </div> 7 </div>
8 8
  9 + <div id="language_domains">
  10 + <%=f.fields_for :language_domains do |f2| %>
  11 + <%= render partial: '/kanjai/admin/domains/language_domain_fields', locals: {f: f2} %>
  12 + <% end %>
  13 + <div class="links text-right">
  14 + <%= link_to_add_association t('add_alternative_domain'), f, :language_domains, class: 'btn btn-info' %>
  15 + </div>
  16 + </div>
  17 +
9 18
10 <div class="card-footer mt-20"> 19 <div class="card-footer mt-20">
11 <div class="clearfix"> 20 <div class="clearfix">
  1 +<div class="nested-fields mt-20">
  2 + <div class="row">
  3 + <div class="col-md-8"><%= f.text_field :title, class: 'form-control' %></div>
  4 + <div class="col-md-4"><%= link_to_remove_association t('actions.delete'), f %></div>
  5 + </div>
  6 +</div>
@@ -21,6 +21,14 @@ @@ -21,6 +21,14 @@
21 </div> 21 </div>
22 </div> 22 </div>
23 23
  24 + <% if @page_lang.domain.language_domains.count > 0 %>
  25 + <div class="form-group">
  26 + <%= f.label :language_domain_id, Kanjai::PageLang.human_attribute_name(:language_domain) + ' *', class: 'col-form-label' %>
  27 + <%= f.select :language_domain_id, options_for_select(@page_lang.domain.language_domains.collect{|item| [item.title, item.id]}, @page_lang.language_domain_id.to_i), {include_blank: true}, class: "form-control " %>
  28 + <%= error_messages(@page_lang, :language_domain_id) %>
  29 + </div>
  30 + <% end %>
  31 +
24 32
25 <div class="card-footer mt-20"> 33 <div class="card-footer mt-20">
26 <div class="clearfix"> 34 <div class="clearfix">
@@ -37,14 +37,12 @@ @@ -37,14 +37,12 @@
37 <li class="nav-item dropdown <%= ['admin_users', 'page_langs'].include?(controller_name) ? 'active' : '' %>"> 37 <li class="nav-item dropdown <%= ['admin_users', 'page_langs'].include?(controller_name) ? 'active' : '' %>">
38 <%= link_to t('configuration_menu_title'), kanjai.admin_configuration_url, :class => 'nav-link dropdown-toggle dropdown-toggle-nocaret', data: {toggle: 'dropdown'} %> 38 <%= link_to t('configuration_menu_title'), kanjai.admin_configuration_url, :class => 'nav-link dropdown-toggle dropdown-toggle-nocaret', data: {toggle: 'dropdown'} %>
39 <div class="dropdown-menu animated fadeIn"> 39 <div class="dropdown-menu animated fadeIn">
  40 + <%= link_to t('domains_menu_title'), kanjai.admin_domains_url, :class => 'dropdown-item' %>
40 <%= link_to t('user_menu_title'), kanjai.admin_admin_users_url, :class => 'dropdown-item' %> 41 <%= link_to t('user_menu_title'), kanjai.admin_admin_users_url, :class => 'dropdown-item' %>
41 <%= link_to t('page_langs_menu_title'), kanjai.admin_page_langs_url, :class => 'dropdown-item' %> 42 <%= link_to t('page_langs_menu_title'), kanjai.admin_page_langs_url, :class => 'dropdown-item' %>
42 </div> 43 </div>
43 </li> 44 </li>
44 45
45 - <li class="nav-item <%= controller_name == 'domains' ? 'active' : '' %>">  
46 - <%= link_to t('domains_menu_title'), kanjai.admin_domains_url, :class => 'nav-link' %>  
47 - </li>  
48 <li class="nav-item <%= controller_name == 'pages' ? 'active' : '' %>"> 46 <li class="nav-item <%= controller_name == 'pages' ? 'active' : '' %>">
49 <%= link_to t('pages_menu_title'), kanjai.admin_pages_url, :class => 'nav-link' %> 47 <%= link_to t('pages_menu_title'), kanjai.admin_pages_url, :class => 'nav-link' %>
50 </li> 48 </li>
@@ -44,6 +44,7 @@ en: @@ -44,6 +44,7 @@ en:
44 password_reset_email: "Email address" 44 password_reset_email: "Email address"
45 password_reset_error: "Can not send instrution, please check email" 45 password_reset_error: "Can not send instrution, please check email"
46 rte_choose_image: "Choose Image" 46 rte_choose_image: "Choose Image"
  47 + add_alternative_domain: "Add Alternative Domain"
47 48
48 49
49 date: 50 date:
@@ -82,6 +83,7 @@ en: @@ -82,6 +83,7 @@ en:
82 code: Code 83 code: Code
83 title: Language title 84 title: Language title
84 default_use: "Use Default" 85 default_use: "Use Default"
  86 + language_domain: "Alternative Domain"
85 kanjai/page_template: 87 kanjai/page_template:
86 title: "Title" 88 title: "Title"
87 updated_at: "Updated at" 89 updated_at: "Updated at"
  1 +class CreateKanjaiLanguageDomains < ActiveRecord::Migration[5.2]
  2 + def change
  3 + create_table :kanjai_language_domains do |t|
  4 + t.references :domain
  5 + t.string :title
  6 + t.timestamps
  7 + end
  8 + end
  9 +end
  1 +class AddLanguageDomainToPageLang < ActiveRecord::Migration[5.2]
  2 + def change
  3 + add_column :kanjai_page_langs, :language_domain_id, :integer
  4 + end
  5 +end
@@ -49,6 +49,7 @@ Gem::Specification.new do |s| @@ -49,6 +49,7 @@ Gem::Specification.new do |s|
49 49
50 s.add_dependency 'mini_magick' 50 s.add_dependency 'mini_magick'
51 51
  52 +
52 #s.add_dependency 'json' 53 #s.add_dependency 'json'
53 54
54 #s.add_dependency "daemons" 55 #s.add_dependency "daemons"
1 module Kanjai 1 module Kanjai
2 - VERSION = "0.0.203" 2 + VERSION = "0.0.204"
3 end 3 end
  1 +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
  2 +
  3 +# This model initially had no columns defined. If you add columns to the
  4 +# model remove the '{}' from the fixture names and add the columns immediately
  5 +# below each fixture, per the syntax in the comments below
  6 +#
  7 +one: {}
  8 +# column: value
  9 +#
  10 +two: {}
  11 +# column: value
  1 +require 'test_helper'
  2 +
  3 +module Kanjai
  4 + class LanguageDomainTest < ActiveSupport::TestCase
  5 + # test "the truth" do
  6 + # assert true
  7 + # end
  8 + end
  9 +end