infopark_fiona_connector 7.0.1.5.2.3.rc8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +5 -0
  3. data/app/assets/images/admin/minus.gif +0 -0
  4. data/app/assets/images/bg80.png +0 -0
  5. data/app/assets/images/edit.png +0 -0
  6. data/app/assets/images/icons/mm_generic.png +0 -0
  7. data/app/assets/images/icons/mm_menu.png +0 -0
  8. data/app/assets/javascripts/editmarker.js +240 -0
  9. data/app/assets/javascripts/infopark_rails_connector.js.erb +0 -0
  10. data/app/assets/stylesheets/editmarker.css +70 -0
  11. data/app/assets/stylesheets/infopark_rails_connector.css.erb +0 -0
  12. data/app/controllers/cms_controller.rb +7 -0
  13. data/app/controllers/rails_connector/default_cms_controller.rb +41 -0
  14. data/app/helpers/cms_helper.rb +7 -0
  15. data/app/helpers/cms_routing_helper.rb +7 -0
  16. data/app/helpers/rails_connector/cms_asset_helper.rb +54 -0
  17. data/app/helpers/rails_connector/cms_tag_helper.rb +4 -0
  18. data/app/helpers/rails_connector/default_cms_helper.rb +21 -0
  19. data/app/helpers/rails_connector/default_cms_routing_helper.rb +119 -0
  20. data/app/helpers/rails_connector/display_helper.rb +124 -0
  21. data/app/helpers/rails_connector/editing_helper.rb +7 -0
  22. data/app/helpers/rails_connector/layout_helper.rb +26 -0
  23. data/app/helpers/rails_connector/marker_helper.rb +371 -0
  24. data/app/helpers/rails_connector/table_of_contents_helper.rb +20 -0
  25. data/app/models/named_link.rb +2 -0
  26. data/app/views/cms/_index.html.erb +7 -0
  27. data/app/views/cms/index.html.erb +1 -0
  28. data/app/views/errors/403_forbidden.html.erb +3 -0
  29. data/app/views/errors/410_gone.html.erb +7 -0
  30. data/config/cms_routes.rb +17 -0
  31. data/config/locales/de.rails_connector.errors.yml +11 -0
  32. data/config/locales/de.rails_connector.lib.yml +6 -0
  33. data/config/locales/de.rails_connector.views.yml +9 -0
  34. data/config/locales/en.rails_connector.errors.yml +10 -0
  35. data/config/locales/en.rails_connector.lib.yml +6 -0
  36. data/config/locales/en.rails_connector.views.yml +9 -0
  37. data/lib/gem_dependencies.rb +68 -0
  38. data/lib/generators/rails_connector/install/install_generator.rb +37 -0
  39. data/lib/generators/rails_connector/install/templates/app/models/obj.rb.erb +5 -0
  40. data/lib/generators/rails_connector/install/templates/initializers/rails_connector.rb +2 -0
  41. data/lib/generators/rails_connector/install/templates/local/configuration.rb +2 -0
  42. data/lib/infopark_fiona_connector.rb +44 -0
  43. data/lib/meta_eager_loader.rb +1 -0
  44. data/lib/obj.rb +3 -0
  45. data/lib/rails_connector/attr_dict.rb +169 -0
  46. data/lib/rails_connector/attr_value_provider.bundle +0 -0
  47. data/lib/rails_connector/attr_value_provider.so +0 -0
  48. data/lib/rails_connector/attr_value_provider64.so +0 -0
  49. data/lib/rails_connector/attribute.rb +93 -0
  50. data/lib/rails_connector/authenticable.rb +37 -0
  51. data/lib/rails_connector/basic_obj.rb +571 -0
  52. data/lib/rails_connector/blob.rb +75 -0
  53. data/lib/rails_connector/blob_mapping.rb +15 -0
  54. data/lib/rails_connector/blob_mysql.rb +30 -0
  55. data/lib/rails_connector/blob_oracle.rb +37 -0
  56. data/lib/rails_connector/cache_middleware.rb +13 -0
  57. data/lib/rails_connector/channel.rb +18 -0
  58. data/lib/rails_connector/cms_accessible.rb +111 -0
  59. data/lib/rails_connector/cms_base_model.rb +62 -0
  60. data/lib/rails_connector/cms_dispatch_controller.rb +43 -0
  61. data/lib/rails_connector/cms_env.rb +65 -0
  62. data/lib/rails_connector/cms_test_request.rb +8 -0
  63. data/lib/rails_connector/configuration.rb +174 -0
  64. data/lib/rails_connector/content.rb +7 -0
  65. data/lib/rails_connector/core_extensions.rb +1 -0
  66. data/lib/rails_connector/core_extensions/time.rb +17 -0
  67. data/lib/rails_connector/date_attribute.rb +26 -0
  68. data/lib/rails_connector/default_search_request.rb +6 -0
  69. data/lib/rails_connector/engine.rb +65 -0
  70. data/lib/rails_connector/errors.rb +6 -0
  71. data/lib/rails_connector/fiona_datetime.rb +14 -0
  72. data/lib/rails_connector/fiona_engine.rb +8 -0
  73. data/lib/rails_connector/html_string.rb +17 -0
  74. data/lib/rails_connector/job.rb +10 -0
  75. data/lib/rails_connector/link.rb +148 -0
  76. data/lib/rails_connector/link_list.rb +28 -0
  77. data/lib/rails_connector/link_resolvable.rb +5 -0
  78. data/lib/rails_connector/lucene_search_request.rb +102 -0
  79. data/lib/rails_connector/markdown_string.rb +17 -0
  80. data/lib/rails_connector/meta.rb +143 -0
  81. data/lib/rails_connector/meta/eager_loader.rb +84 -0
  82. data/lib/rails_connector/named_link.rb +90 -0
  83. data/lib/rails_connector/news.rb +20 -0
  84. data/lib/rails_connector/obj_class.rb +138 -0
  85. data/lib/rails_connector/obj_class_attr.rb +5 -0
  86. data/lib/rails_connector/object_with_meta_data.rb +13 -0
  87. data/lib/rails_connector/permission.rb +37 -0
  88. data/lib/rails_connector/rack_middlewares.rb +5 -0
  89. data/lib/rails_connector/ses.rb +78 -0
  90. data/lib/rails_connector/ses/verity_accessor.rb +127 -0
  91. data/lib/rails_connector/string_tagging.rb +26 -0
  92. data/lib/rails_connector/verity_search_request.rb +86 -0
  93. data/lib/search_request.rb +2 -0
  94. data/lib/version.rb +7 -0
  95. metadata +277 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 52e7967736171b19173187521650f26ea417ab846a1e1a1c14a35d08293fce09
4
+ data.tar.gz: 8ddf90ebd43a0fadc9ce86bbd9554b885859a4f6ec4bb013f6dc568a9e69e1fa
5
+ SHA512:
6
+ metadata.gz: 5c0952856eae335e95c6cdf8b905d272c4956624b68cfe9f0d8b63c5db8e85d193b819a6986933a50c8b742217293fc266238f64f40d17f3d3918af712b2f778
7
+ data.tar.gz: 3ba5d698cd79ff19780aa4aaa3a184ddb31bbfcc91b973077eb7aa9d45369fcaaa5304341fec0f3c4f2b9316101fafd7eee71a7eecf3bd7b314e1d480fd39bee
@@ -0,0 +1,5 @@
1
+ --hide-void-return
2
+ --protected
3
+ --private
4
+ --non-transitive-tag api
5
+ --api public
@@ -0,0 +1,240 @@
1
+ var inline_editing = (function() {
2
+
3
+ /********************************************************************************************/
4
+ /* Global vars */
5
+ var prevElement = false;
6
+ var actionMarkerVisible = true;
7
+ var actionMarkerArray;
8
+ var rememberBackgroundColor;
9
+ var rememberZoom;
10
+ var editMarkerDefinitions;
11
+ var parentWindow = parent;
12
+
13
+ /********************************************************************************************/
14
+ /* Global Functions */
15
+
16
+ var findInfoFromClassName = function(element, regexp) {
17
+ var info;
18
+ var matches = (jQuery(element).attr("class") || "").match(regexp);
19
+ if (matches && matches.length > 1) {
20
+ return matches[1];
21
+ }
22
+ };
23
+
24
+ /****************************************/
25
+ /* Hilfsfunktionen für die Hervorhebung */
26
+
27
+ var stashStyle = function(element)
28
+ {
29
+ var nps_style_stash = {};
30
+ jQuery.each(['backgroundColor', 'zoom', 'color'], function(i, property) {
31
+ nps_style_stash[property] = element.css(property);
32
+ });
33
+ element.data('nps_style_stash', nps_style_stash);
34
+ };
35
+
36
+ var highlight = function(element)
37
+ {
38
+ // setting the zoom works around incomplete redraws on ie6
39
+ element.css({
40
+ backgroundColor: '#ffffaa',
41
+ zoom: 1,
42
+ color: '#000000'
43
+ });
44
+ };
45
+
46
+ var unstashStyle = function(element)
47
+ {
48
+ if (element.data('nps_style_stash')) {
49
+ element.css(element.data('nps_style_stash'));
50
+ element.removeData('nps_style_stash');
51
+ }
52
+ };
53
+
54
+ /********************************************************************************************/
55
+ /* Marker Definitions */
56
+
57
+ /********************************************************************************************/
58
+ /* Marker Permission Checking */
59
+
60
+ var that = {
61
+ setParentWindow: function(new_parent) {
62
+ parentWindow = new_parent;
63
+ },
64
+
65
+ attachMarkerMenu: function(marker_menu_target){
66
+ var id = findInfoFromClassName(marker_menu_target, /nps_marker_menu_target_([0-9]+)/);
67
+ var marker_menu = jQuery("#nps_marker_menu_"+id);
68
+ if (marker_menu.length) {
69
+ var definition = that.findMarkerDefinitionForElement(marker_menu);
70
+ var target_pos = marker_menu_target.offset();
71
+ var parent_pos = marker_menu.offsetParent().offset();
72
+
73
+ marker_menu.css({
74
+ position: 'absolute',
75
+ left: target_pos.left + definition.offset_left - parent_pos.left,
76
+ top: target_pos.top + definition.offset_top - parent_pos.top
77
+ });
78
+
79
+ jQuery(marker_menu).unbind('mouseenter');
80
+ jQuery(marker_menu).mouseenter(function(){
81
+ stashStyle(marker_menu_target);
82
+ highlight(marker_menu_target);
83
+ });
84
+ jQuery(marker_menu).unbind('mouseleave');
85
+ jQuery(marker_menu).mouseleave(function(){
86
+ unstashStyle(marker_menu_target);
87
+ });
88
+ }
89
+ },
90
+
91
+ initMarkerMenus: function() {
92
+ var visible = false;
93
+ jQuery('.nps_marker_menu_button').click(function() {
94
+ if (visible) {
95
+ jQuery(this).next().slideUp(200);
96
+ visible = false;
97
+ } else {
98
+ jQuery(this).next().slideDown(200);
99
+ visible = true;
100
+ }
101
+ return false;
102
+ });
103
+ },
104
+
105
+ attachMarkerMenus: function() {
106
+ jQuery('.nps_marker_menu_target').each(function() {
107
+ that.attachMarkerMenu(jQuery(this));
108
+ });
109
+ },
110
+
111
+ initActionMarker: function() {
112
+ jQuery('.nps_action_marker').each(function(ignore, marker_element) {
113
+ if (!that.hasRequiredPermissionsForMarkerElement(marker_element)) {
114
+ jQuery(marker_element).hide();
115
+ }
116
+ });
117
+ },
118
+
119
+ bindEditMarkerEvents: function(marker, target){
120
+ marker.unbind();
121
+ marker.mouseenter(function() {
122
+ target.find("*").each(function() { stashStyle(jQuery(this)); });
123
+ target.find("*").each(function() { highlight(jQuery(this)); });
124
+ stashStyle(target);
125
+ highlight(target);
126
+ });
127
+ marker.mouseleave(function() {
128
+ target.find("*").each(function() { unstashStyle(jQuery(this)); });
129
+ unstashStyle(target);
130
+ });
131
+ marker.click(function() { return that.startEditing(this); });
132
+ },
133
+
134
+ initEditMarker: function() {
135
+ // add functions and behaviour to all edit marker elements
136
+ jQuery(".nps_edit_marker").each(function(i, marker) {
137
+ marker = jQuery(marker);
138
+ if (that.hasRequiredPermissionsForMarkerElement(marker)) {
139
+ var id = findInfoFromClassName(marker, /nps_marker_id_([0-9]+)/);
140
+ var target = jQuery("#nps_marker_id_" + id);
141
+ if (target.length) {
142
+ var pos = target.offset();
143
+ var parent_pos = marker.offsetParent().offset();
144
+ that.bindEditMarkerEvents(marker, target);
145
+ marker.css({
146
+ left: pos.left - parent_pos.left,
147
+ top: pos.top - parent_pos.top,
148
+ position: "absolute",
149
+ display: "inline"
150
+ });
151
+ } else {
152
+ marker.hide();
153
+ }
154
+ }
155
+ else {
156
+ marker.hide();
157
+ }
158
+ });
159
+ },
160
+
161
+ openEditDialog: function(obj_id, attribute, context_id, size, target) {
162
+ // pass control to Fiona GUI
163
+ parentWindow.openEditDialog(obj_id, attribute, context_id, size, target);
164
+ },
165
+
166
+ initMarker: function() {
167
+ that.initEditMarker();
168
+ that.initActionMarker();
169
+ that.initMarkerMenus();
170
+ that.attachMarkerMenus();
171
+ },
172
+
173
+ init: function() {
174
+ jQuery(document).ready(that.initMarker);
175
+ jQuery(window).load(that.initMarker);
176
+ jQuery(window).resize(that.initMarker);
177
+ },
178
+
179
+ storeMarkerDefinitions: function(definitions) {
180
+ editMarkerDefinitions = editMarkerDefinitions || {};
181
+ jQuery.extend(editMarkerDefinitions, definitions);
182
+ },
183
+
184
+ markerDefinition: function(id) {
185
+ return editMarkerDefinitions[id];
186
+ },
187
+
188
+ findMarkerDefinitionForElement: function(element) {
189
+ return that.markerDefinition(findInfoFromClassName(element, /nps_marker_id_([0-9]+)/));
190
+ },
191
+
192
+ currentUserHasGlobalPermission: function(perm) {
193
+ if (parentWindow.currentUserIsMemberOf) {
194
+ return parentWindow.currentUserHasGlobalPermission(perm);
195
+ }
196
+ else {
197
+ return true;
198
+ }
199
+ },
200
+
201
+ currentUserIsMemberOf: function(groups) {
202
+ if (parentWindow.currentUserIsMemberOf) {
203
+ return parentWindow.currentUserIsMemberOf(groups);
204
+ }
205
+ else {
206
+ return true;
207
+ }
208
+ },
209
+
210
+ hasRequiredPermissionsForMarkerElement: function(element) {
211
+ if (that.currentUserHasGlobalPermission("permissionGlobalRoot")) { return true; }
212
+
213
+ var definition = that.findMarkerDefinitionForElement(element);
214
+ var member_in_group = 0;
215
+ jQuery.each(definition.memberships, function() {
216
+ if (that.currentUserIsMemberOf(this)) {
217
+ member_in_group += 1;
218
+ return true;
219
+ }
220
+ return false;
221
+ });
222
+ return member_in_group === definition.memberships.length;
223
+ },
224
+
225
+ startEditing: function(element) {
226
+ var definition = that.findMarkerDefinitionForElement(element);
227
+
228
+ // pass control to Fiona GUI
229
+ that.openEditDialog(
230
+ definition.obj_id, definition.attribute, definition.context_id,
231
+ definition.size, definition.target
232
+ );
233
+ return false;
234
+ }
235
+
236
+ };
237
+
238
+ return that;
239
+
240
+ }());
@@ -0,0 +1,70 @@
1
+ @charset "UTF-8";
2
+
3
+ /*** Edit Marker ***/
4
+ a.nps_edit_marker {
5
+ -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
6
+ filter: alpha(opacity=50);
7
+ }
8
+ a.nps_edit_marker:hover,a.nps_edit_marker:active {
9
+ -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
10
+ filter: alpha(opacity=100);
11
+ }
12
+
13
+ a.nps_edit_marker {
14
+ background-color: transparent;
15
+ display:none;
16
+ background:url(edit.png) 0 0 no-repeat;
17
+ min-width:16px;
18
+ min-height:16px;
19
+ z-index: 1000;
20
+ position:absolute;
21
+ opacity:0.3;
22
+ }
23
+ a.nps_edit_marker:hover,a.nps_edit_marker:active {opacity:1;}
24
+
25
+ /*** Marker Menu ***/
26
+ .nps_marker_menu {
27
+ position:absolute;
28
+ z-index:10000;
29
+ display:block;
30
+ /*** IE 6-7 braucht leider eine Breitenangabe***/
31
+ width:200px;
32
+ }
33
+
34
+
35
+ /*** Marker Menu Transparenz IE 6 ***/
36
+ * html .nps_marker_menu div.core { filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='bg80.png', sizingMethod='scale');}
37
+
38
+ /*** Marker Menu Button Transparenz IE ***/
39
+ a.nps_marker_menu_button {
40
+ -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
41
+ filter: alpha(opacity=50);
42
+ }
43
+ a.nps_marker_menu_button:hover,a.nps_marker_menu_button:active {
44
+ -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
45
+ filter: alpha(opacity=100);
46
+ }
47
+
48
+ a.nps_marker_menu_button {display: block; text-align:left;text-indent:-1000em; width: 100%; height: 16px; background: transparent url(icons/mm_menu.png) 0 0 no-repeat;opacity:0.3;}
49
+ a.nps_marker_menu_button:hover,a.nps_marker_menu_button:active {opacity:1;}
50
+
51
+ .nps_marker_menu div.b1,
52
+ .nps_marker_menu div.b2,
53
+ .nps_marker_menu div.b3,
54
+ .nps_marker_menu div.b4 { background:url(bg80.png) 0 0 repeat; display:block;padding:0;overflow:hidden;font-size:1px;}
55
+ .nps_marker_menu div.b1 { margin:0pt 5px; height:1px;}
56
+ .nps_marker_menu div.b2 { margin:0pt 3px; height:1px;}
57
+ .nps_marker_menu div.b3 { margin:0pt 2px; height:1px;}
58
+ .nps_marker_menu div.b4 { margin:0pt 1px; height:2px;}
59
+
60
+ .nps_marker_menu_content div.core {background:url(bg80.png) 0 0 repeat; text-align:left;color:#fff;}
61
+ .nps_marker_menu_content div.core p{padding:0 5px; text-align:left;color:#fff;}
62
+
63
+ .nps_marker_menu ul { display:block; margin:0; padding:0;}
64
+ .nps_marker_menu ul li {padding:3px 1px; margin:0; line-height: 20px; list-style-type:none; list-style-image:none;}
65
+ .nps_marker_menu ul li a {display:block; color:#fff; font-weight:bold;}
66
+ .nps_marker_menu ul li a:hover,
67
+ .nps_marker_menu ul li a:active { background-color: #777; text-decoration:none; color:#fff; }
68
+ .nps_marker_menu ul li a:focus { text-decoration:none; color:#fff; }
69
+
70
+ .nps_marker_menu ul li a span {padding: 0 6px 0 30px;display: block; text-align: left; background: transparent url(icons/mm_generic.png) 7px 2px no-repeat;}
@@ -0,0 +1,7 @@
1
+ # This controller inherits all of its behavior from
2
+ # {RailsConnector::DefaultCmsController}.
3
+ #
4
+ # {CmsController} is referenced by the Rails Connector routes.
5
+ # @api public
6
+ class CmsController < RailsConnector::DefaultCmsController
7
+ end
@@ -0,0 +1,41 @@
1
+ module RailsConnector
2
+ # This controller provides some default includes ({CmsAccessible}),
3
+ # before filters (+load_object+, among others), and actions to simplify
4
+ # CMS object handling. It should never be used directly, but only as a
5
+ # super class to {CmsController}.
6
+ # @api public
7
+ class DefaultCmsController < ApplicationController
8
+ include CmsAccessible
9
+ before_action :load_object
10
+ before_action :ensure_object_is_active
11
+ before_action :ensure_object_is_permitted
12
+ before_action :set_google_expire_header
13
+
14
+ # Default Action. Delivers files directly if their mime type is not text/html
15
+ # @api public
16
+ def index
17
+ deliver_file if @obj.mime_type != "text/html"
18
+ end
19
+
20
+ # Toggles edit markers. The current status is stored in the session.
21
+ # @api public
22
+ def toggle_editmarkers
23
+ session[:hide_editmarkers] = !session[:hide_editmarkers]
24
+ redirect_to cms_id_url(@obj)
25
+ end
26
+
27
+ # This method indicates if this controller should be used automatically when an Obj is
28
+ # requested via the Rails Connector's standard routes. It returns true by default.
29
+ #
30
+ # Overwrite it to return false if you do want your controller to be excluded from Obj dispatching.
31
+ #
32
+ # You may also implement a method with the name {use_for_obj_dispatch?} in controllers not
33
+ # descending from {DefaultCmsController} to include them in Obj dispatching.
34
+ #
35
+ # @see Obj#controller_name
36
+ # @api public
37
+ def self.use_for_obj_dispatch?
38
+ true
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,7 @@
1
+ # {CmsHelper} is a wrapper around {RailsConnector::DefaultCmsHelper}.
2
+ # It can be replaced in your application in order to add or
3
+ # modify helpers.
4
+ # @api public
5
+ module CmsHelper
6
+ include RailsConnector::DefaultCmsHelper
7
+ end
@@ -0,0 +1,7 @@
1
+ # {CmsRoutingHelper} is a wrapper around {RailsConnector::DefaultCmsRoutingHelper}.
2
+ # It can be replaced in your application in order to add or
3
+ # modify helpers.
4
+ # @api public
5
+ module CmsRoutingHelper
6
+ include RailsConnector::DefaultCmsRoutingHelper
7
+ end
@@ -0,0 +1,54 @@
1
+ module RailsConnector
2
+ # This module contains helpers that can be used to reference images and other assets stored in the CMS.
3
+ #
4
+ # Use this helper to take advantage of rails' asset host mechanism.
5
+ # (See http://api.rubyonrails.com/classes/ActionView/Helpers/AssetTagHelper.html for details about asset hosts.)
6
+ # If your application does not use any asset hosts, the helper will generate regular (=non asset host) pathes.
7
+ # @api public
8
+ module CmsAssetHelper
9
+ include ActionView::Helpers::AssetUrlHelper
10
+
11
+ # Returns an html image tag for an image stored in the CMS.
12
+ #
13
+ # +target+ is the image from the cms to be referenced in the image tag.
14
+ # +target+ can be an +Obj+ or a +Link+ or a +LinkList+.
15
+ # +options+ can be used to specify additional html attributes for the tag.
16
+ # If you do not specify an html alt attribute, the helper method will use
17
+ # +target+'s +display_title+ as the html alt attribute.
18
+ # If your application is configured to use asset hosts, the images tag will reference
19
+ # the image using one of your asset hosts.
20
+ # @api public
21
+ def cms_image_tag(target, options = {})
22
+ options.symbolize_keys!
23
+
24
+ options[:src] = cms_asset_path(target)
25
+ options[:alt] ||= display_title(target)
26
+
27
+ tag("img", options)
28
+ end
29
+
30
+ # Calculate the path to an asset stored in the CMS.
31
+ #
32
+ # The path can be used to reference a cms object inside an html page.
33
+ # +target+ can be an +Obj+ or a +Link+ or a +LinkList+.
34
+ # If your application is configured to use asset hosts, the path will reference
35
+ # the object using one of your asset hosts.
36
+ # @api public
37
+ def cms_asset_path(target)
38
+ regular_path = cms_path(target)
39
+ return regular_path if regular_path.first == "#"
40
+
41
+ asset_path(regular_path)
42
+ end
43
+
44
+ private
45
+
46
+ def display_title(target)
47
+ if target.respond_to?(:display_title)
48
+ target.display_title
49
+ elsif target.respond_to?(:first) && target.first.respond_to?(:display_title)
50
+ target.first.display_title
51
+ end
52
+ end
53
+ end
54
+ end