Commit 4f26e0865e16a9f41f456279f52937ae2fc1fddd
1 parent
1ffe9c0c
add support alternative language domain
Showing
20 changed files
with
417 additions
and
46 deletions
| 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 |
app/assets/javascripts/kanjai/cocoon.js
0 → 100644
| 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 |
app/models/kanjai/language_domain.rb
0 → 100644
| @@ -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"> |
| @@ -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" |
| @@ -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" |
test/fixtures/kanjai/language_domains.yml
0 → 100644
| 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 |