infopark_fiona_connector 6.9.4 → 7.0.1.5.2.3.rc4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/app/assets/images/admin/minus.gif +0 -0
- data/app/assets/images/bg80.png +0 -0
- data/app/assets/images/edit.png +0 -0
- data/app/assets/images/icons/mm_generic.png +0 -0
- data/app/assets/images/icons/mm_menu.png +0 -0
- data/app/assets/javascripts/editmarker.js +240 -0
- data/app/assets/javascripts/infopark_rails_connector.js.erb +0 -0
- data/app/assets/stylesheets/editmarker.css +70 -0
- data/app/assets/stylesheets/infopark_rails_connector.css.erb +0 -0
- data/app/controllers/cms_controller.rb +7 -0
- data/app/controllers/rails_connector/default_cms_controller.rb +43 -0
- data/app/helpers/cms_helper.rb +7 -0
- data/app/helpers/cms_routing_helper.rb +7 -0
- data/app/helpers/rails_connector/cms_asset_helper.rb +2 -1
- data/app/helpers/rails_connector/default_cms_helper.rb +23 -0
- data/app/helpers/rails_connector/default_cms_routing_helper.rb +120 -0
- data/app/helpers/rails_connector/display_helper.rb +5 -5
- data/app/helpers/rails_connector/layout_helper.rb +29 -0
- data/app/helpers/rails_connector/marker_helper.rb +1 -3
- data/app/helpers/rails_connector/table_of_contents_helper.rb +22 -0
- data/app/models/named_link.rb +2 -0
- data/app/views/cms/_index.html.erb +7 -0
- data/app/views/cms/index.html.erb +1 -0
- data/app/views/errors/403_forbidden.html.erb +3 -0
- data/app/views/errors/410_gone.html.erb +7 -0
- data/config/cms_routes.rb +17 -0
- data/config/locales/de.rails_connector.errors.yml +11 -0
- data/config/locales/de.rails_connector.lib.yml +6 -0
- data/config/locales/de.rails_connector.views.yml +9 -0
- data/config/locales/en.rails_connector.errors.yml +10 -0
- data/config/locales/en.rails_connector.lib.yml +6 -0
- data/config/locales/en.rails_connector.views.yml +9 -0
- data/lib/gem_dependencies.rb +67 -0
- data/lib/infopark_fiona_connector.rb +26 -3
- data/lib/meta_eager_loader.rb +1 -0
- data/lib/obj.rb +3 -0
- data/lib/rails_connector/attr_dict.rb +31 -16
- data/lib/rails_connector/attribute.rb +96 -0
- data/lib/rails_connector/authenticable.rb +30 -0
- data/lib/rails_connector/basic_obj.rb +82 -95
- data/lib/rails_connector/blob.rb +14 -6
- data/lib/rails_connector/blob_mapping.rb +16 -0
- data/lib/rails_connector/blob_mysql.rb +1 -1
- data/lib/rails_connector/blob_oracle.rb +1 -1
- data/lib/rails_connector/channel.rb +19 -0
- data/lib/rails_connector/cms_accessible.rb +112 -0
- data/lib/rails_connector/cms_base_model.rb +9 -0
- data/lib/rails_connector/cms_dispatch_controller.rb +46 -0
- data/lib/rails_connector/cms_env.rb +68 -0
- data/lib/rails_connector/cms_test_request.rb +8 -0
- data/lib/rails_connector/configuration.rb +3 -1
- data/lib/rails_connector/content.rb +11 -0
- data/lib/rails_connector/core_extensions.rb +1 -0
- data/lib/rails_connector/core_extensions/time.rb +18 -0
- data/lib/rails_connector/date_attribute.rb +1 -1
- data/lib/rails_connector/engine.rb +68 -0
- data/lib/rails_connector/fiona_datetime.rb +14 -0
- data/lib/rails_connector/html_string.rb +19 -0
- data/lib/rails_connector/job.rb +13 -0
- data/lib/rails_connector/link_list.rb +32 -0
- data/lib/rails_connector/link_resolvable.rb +9 -0
- data/lib/rails_connector/markdown_string.rb +19 -0
- data/lib/rails_connector/meta.rb +148 -0
- data/lib/rails_connector/meta/eager_loader.rb +82 -0
- data/lib/rails_connector/named_link.rb +16 -1
- data/lib/rails_connector/news.rb +7 -3
- data/lib/rails_connector/obj_class.rb +140 -0
- data/lib/rails_connector/obj_class_attr.rb +5 -0
- data/lib/rails_connector/object_with_meta_data.rb +18 -0
- data/lib/rails_connector/rack_middlewares.rb +1 -2
- data/lib/rails_connector/string_tagging.rb +29 -0
- data/lib/version.rb +7 -0
- metadata +168 -42
- data/README +0 -5
@@ -25,6 +25,15 @@ module RailsConnector
|
|
25
25
|
true
|
26
26
|
end
|
27
27
|
|
28
|
+
# FIXME: Find a more stable solution for this
|
29
|
+
#
|
30
|
+
# Since ActiveRecord 4.2 finding Objs by name is no longer consistent when
|
31
|
+
# switching between edited and released content at runtime. As long as we
|
32
|
+
# find a better solution this mitigates the behaviour.
|
33
|
+
def self.find_by(arg, *args)
|
34
|
+
where(arg, *args).take
|
35
|
+
end
|
36
|
+
|
28
37
|
def readonly?
|
29
38
|
true
|
30
39
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module RailsConnector
|
2
|
+
|
3
|
+
class CmsDispatchController < ActionController::Metal
|
4
|
+
def process(action)
|
5
|
+
CmsEnv.new(request.env).load
|
6
|
+
controller = target_controller(request.env)
|
7
|
+
request.env["action_dispatch.request.path_parameters"][:controller] = controller.controller_path
|
8
|
+
|
9
|
+
if !obj_not_found? && action == 'index'
|
10
|
+
action = loaded_obj.controller_action_name
|
11
|
+
end
|
12
|
+
|
13
|
+
request.env["action_dispatch.request.path_parameters"][:action] = action
|
14
|
+
|
15
|
+
self.response = controller.action(action).call(request.env)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def target_controller(env)
|
21
|
+
return default_controller if obj_not_found?
|
22
|
+
controller = "#{loaded_obj.controller_name}Controller".constantize
|
23
|
+
|
24
|
+
if controller.respond_to?(:use_for_obj_dispatch?) && controller.use_for_obj_dispatch?
|
25
|
+
controller
|
26
|
+
else
|
27
|
+
default_controller
|
28
|
+
end
|
29
|
+
rescue NameError
|
30
|
+
default_controller
|
31
|
+
end
|
32
|
+
|
33
|
+
def loaded_obj
|
34
|
+
request.env[CmsEnv::OBJ_ENV_KEY]
|
35
|
+
end
|
36
|
+
|
37
|
+
def obj_not_found?
|
38
|
+
loaded_obj.is_a?(StandardError)
|
39
|
+
end
|
40
|
+
|
41
|
+
def default_controller
|
42
|
+
CmsController
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module RailsConnector
|
2
|
+
|
3
|
+
class CmsEnv
|
4
|
+
|
5
|
+
OBJ_ENV_KEY = "INFOPARK_OBJ"
|
6
|
+
|
7
|
+
def initialize(env)
|
8
|
+
@env = env
|
9
|
+
end
|
10
|
+
|
11
|
+
def load
|
12
|
+
return if env[OBJ_ENV_KEY]
|
13
|
+
load_object
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
attr_reader :env
|
19
|
+
|
20
|
+
def load_object
|
21
|
+
env[OBJ_ENV_KEY] =
|
22
|
+
begin
|
23
|
+
find_obj
|
24
|
+
rescue StandardError => e
|
25
|
+
e
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def find_obj
|
30
|
+
found_obj =
|
31
|
+
if params[:id]
|
32
|
+
Obj.find(params[:id])
|
33
|
+
elsif params[:permalink].present?
|
34
|
+
self.class.find_permalink_by_param(params[:permalink])
|
35
|
+
else
|
36
|
+
if callback = RailsConnector::Configuration.choose_homepage_callback
|
37
|
+
callback_result = callback.call(env)
|
38
|
+
if callback_result.is_a?(Obj)
|
39
|
+
callback_result
|
40
|
+
else
|
41
|
+
raise "choose_homepage callback did not return an Obj. "\
|
42
|
+
"Instead saw #{callback_result.class}."
|
43
|
+
end
|
44
|
+
else
|
45
|
+
Obj.homepage
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
if found_obj.suppressed?
|
50
|
+
raise RailsConnector::ResourceNotFound,
|
51
|
+
"Tried to access Obj #{found_obj.inspect}, but it is suppressed (suppress_export is set)!"
|
52
|
+
end
|
53
|
+
|
54
|
+
found_obj
|
55
|
+
end
|
56
|
+
|
57
|
+
def params
|
58
|
+
env["action_dispatch.request.path_parameters"]
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.find_permalink_by_param(permalink_param)
|
62
|
+
permalink = Array(permalink_param).join("/")
|
63
|
+
Obj.find_by_permalink!(permalink)
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -77,6 +77,8 @@ module RailsConnector
|
|
77
77
|
end
|
78
78
|
|
79
79
|
def to_prepare
|
80
|
+
BasicObj.reset_reflections
|
81
|
+
|
80
82
|
unless Rails.configuration.cache_classes
|
81
83
|
after_initialize
|
82
84
|
NamedLink.reset_cache
|
@@ -86,7 +88,7 @@ module RailsConnector
|
|
86
88
|
end
|
87
89
|
|
88
90
|
def configure_cms_database
|
89
|
-
RailsConnector::CmsBaseModel.configure_database(
|
91
|
+
RailsConnector::CmsBaseModel.configure_database(:cms)
|
90
92
|
end
|
91
93
|
|
92
94
|
attr_accessor :choose_homepage_callback
|
@@ -0,0 +1 @@
|
|
1
|
+
require "rails_connector/core_extensions/time"
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Time
|
2
|
+
|
3
|
+
# Returns a new Time created from the ISO date format String "YYYYMMDDhhmmss"
|
4
|
+
def self.from_iso(t)
|
5
|
+
return nil unless t
|
6
|
+
if t.to_s =~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$/
|
7
|
+
utc($1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i)
|
8
|
+
else
|
9
|
+
raise ArgumentError, "invalid iso time format: #{t}"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns the Time as ISO date format String "YYYYMMDDhhmmss"
|
14
|
+
def to_iso
|
15
|
+
getutc.strftime('%Y%m%d%H%M%S')
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'rails'
|
2
|
+
require 'active_record/railtie'
|
3
|
+
require 'action_view'
|
4
|
+
|
5
|
+
require 'jquery-rails'
|
6
|
+
|
7
|
+
require 'rails_connector/configuration'
|
8
|
+
|
9
|
+
module ::RailsConnector
|
10
|
+
class Engine < Rails::Engine
|
11
|
+
config.to_prepare { RailsConnector::Configuration.to_prepare }
|
12
|
+
config.after_initialize { RailsConnector::Configuration.after_initialize }
|
13
|
+
|
14
|
+
# make sure our exceptions cause an adequate error page and http status code
|
15
|
+
config.action_dispatch.rescue_responses.merge!("RailsConnector::ResourceNotFound" => :not_found)
|
16
|
+
|
17
|
+
initializer 'rails_connector.add_fiona_time_type' do
|
18
|
+
ActiveRecord::Type.register(:fiona_datetime, ::FionaDateTime)
|
19
|
+
end
|
20
|
+
|
21
|
+
initializer "rails_connector.add_cms_routing_paths", :after => :add_routing_paths do |app|
|
22
|
+
cms_route = File.expand_path("cms_routes.rb", paths['config'].to_a.first)
|
23
|
+
app.routes_reloader.paths.push(cms_route)
|
24
|
+
end
|
25
|
+
|
26
|
+
initializer "rails_connector.add_assets" do |app|
|
27
|
+
app.config.assets.precompile += %w[
|
28
|
+
editmarker.css editmarker.js
|
29
|
+
]
|
30
|
+
end
|
31
|
+
|
32
|
+
# Expose rails connector runtime to controller for logging.
|
33
|
+
initializer "rails_connector.log_runtime" do |app|
|
34
|
+
runtime =
|
35
|
+
begin
|
36
|
+
RailsConnector::ControllerRuntime
|
37
|
+
rescue NameError
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
|
41
|
+
if runtime
|
42
|
+
RailsConnector::LogSubscriber.attach_to :rails_connector
|
43
|
+
ActiveSupport.on_load(:action_controller) do
|
44
|
+
include runtime
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
initializer "rails_connector.configure_database", :before => :load_config_initializers do |app|
|
50
|
+
RailsConnector::Configuration.configure_cms_database
|
51
|
+
end
|
52
|
+
|
53
|
+
initializer "rails_connector.routing_helpers" do
|
54
|
+
if Rails.env == 'test'
|
55
|
+
ActionDispatch::TestRequest.__send__(:include, RailsConnector::CmsTestRequest)
|
56
|
+
end
|
57
|
+
|
58
|
+
ActionController::Base.__send__(:include, CmsRoutingHelper)
|
59
|
+
ActionView::Base.__send__(:include, CmsRoutingHelper)
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
config.autoload_paths += paths['lib'].to_a
|
64
|
+
config.autoload_once_paths += paths['lib'].to_a
|
65
|
+
|
66
|
+
RailsConnector.rack_middlewares.each { |middleware| config.app_middleware.use middleware }
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'active_record/type'
|
2
|
+
class FionaDateTime < ActiveRecord::Type::Time
|
3
|
+
def type
|
4
|
+
:fiona_datetime
|
5
|
+
end
|
6
|
+
|
7
|
+
def serialize(value)
|
8
|
+
value.to_iso if value
|
9
|
+
end
|
10
|
+
|
11
|
+
def deserialize(value)
|
12
|
+
Time.from_iso(value).in_time_zone if value
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module RailsConnector
|
2
|
+
|
3
|
+
# Include this module in order to tag the string as one with HTML content
|
4
|
+
module HtmlString
|
5
|
+
include LinkResolvable
|
6
|
+
# Returns whether the String contains HTML (default to true, overrides String.html?).
|
7
|
+
def html?
|
8
|
+
true
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
class String
|
15
|
+
# Returns whether the String contains HTML (default to false).
|
16
|
+
def html?
|
17
|
+
false
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module RailsConnector
|
2
|
+
|
3
|
+
# @api public
|
4
|
+
class LinkList < Array
|
5
|
+
|
6
|
+
def initialize(link_definitions)
|
7
|
+
if link_definitions.blank?
|
8
|
+
super([])
|
9
|
+
else
|
10
|
+
super(build_links(link_definitions.map(&:symbolize_keys)))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# @api public
|
15
|
+
def destination_objects
|
16
|
+
self.map {|link| link.destination_object}.compact
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def build_links(link_definitions)
|
22
|
+
object_ids = link_definitions.map { |link_data| link_data[:destination] }.compact.uniq
|
23
|
+
objects = object_ids.empty? ? [] : Obj.find(object_ids)
|
24
|
+
link_definitions.each_with_object([]) do |link_data, links|
|
25
|
+
link = Link.new(link_data, objects.detect { |o| o && o.id == link_data[:destination] })
|
26
|
+
links << link if link.active?
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module RailsConnector
|
2
|
+
|
3
|
+
# Include this module in order to tag the string as one with Markdown content
|
4
|
+
module MarkdownString
|
5
|
+
include LinkResolvable
|
6
|
+
# Returns whether the String contains Markdown (default to true, overrides String.markdown?).
|
7
|
+
def markdown?
|
8
|
+
true
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
class String
|
15
|
+
# Returns whether the String contains Markdown (default to false).
|
16
|
+
def markdown?
|
17
|
+
false
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
module RailsConnector
|
2
|
+
|
3
|
+
module Meta
|
4
|
+
|
5
|
+
# This method is an equivalent of Rails.logger.silence, which has been deprecated
|
6
|
+
def self.hello_im_rails_and_im_retarted_so_please_be_patient(&block)
|
7
|
+
begin
|
8
|
+
old_logger_level, Rails.logger.level = Rails.logger.level, Logger::ERROR
|
9
|
+
yield self
|
10
|
+
ensure
|
11
|
+
Rails.logger.level = old_logger_level
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.included(base) #:nodoc:
|
16
|
+
# Class enhancements
|
17
|
+
base.extend(ClassMethods)
|
18
|
+
end
|
19
|
+
|
20
|
+
# The RailsConnector::ObjClass object for this file format.
|
21
|
+
# This will always return a proper object, even if no custom
|
22
|
+
# Ruby class exists.
|
23
|
+
def obj_class_definition
|
24
|
+
@obj_class_definition ||= RailsConnector::Meta::EagerLoader.instance.obj_class(self.obj_class)
|
25
|
+
end
|
26
|
+
alias_method :obj_class_def, :obj_class_definition
|
27
|
+
|
28
|
+
# Returns true, if there is a custom Ruby class defined for the object
|
29
|
+
# or false, if it is represented by RailsConnector::Obj
|
30
|
+
def has_custom_ruby_class?
|
31
|
+
self.class.is_custom_ruby_class?
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns the custom attributes in the form of a Hash.
|
35
|
+
def custom_attributes
|
36
|
+
self.obj_class_definition.custom_attributes
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns true, if the file format has an attribute of the given name.
|
40
|
+
def custom_attribute?(attr)
|
41
|
+
self.obj_class_definition.custom_attribute?(attr)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns an Array of String of all mandatory attributes, no mather if it's
|
45
|
+
# custom or built-in. Built-in attributes are underscored (valid_from,
|
46
|
+
# not validFrom).
|
47
|
+
# Possible +options+ are:
|
48
|
+
# <tt>:only_custom_attributes</tt>:: Return only custom attributes, omit
|
49
|
+
# built-in attributes like content_type or valid_from.
|
50
|
+
def mandatory_attribute_names(options = {})
|
51
|
+
self.obj_class_definition.mandatory_attribute_names(options)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns true, if the file format has an mandatory attribute of the given name.
|
55
|
+
def mandatory_attribute?(attr)
|
56
|
+
self.obj_class_definition.mandatory_attribute?(attr)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns the version of this object. This number is increased every time
|
60
|
+
# this object is released.
|
61
|
+
def version
|
62
|
+
load_meta_details
|
63
|
+
@object_with_meta_data.version.presence.to_i || 0
|
64
|
+
end
|
65
|
+
|
66
|
+
# Returns the time of the reminder, if it is set.
|
67
|
+
def reminder_from
|
68
|
+
load_meta_details
|
69
|
+
@object_with_meta_data.reminder_from.presence &&
|
70
|
+
::ActiveModel::Type::DateTime.new.deserialize(@object_with_meta_data.reminder_from)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns the reminder comment, if a reminder is set.
|
74
|
+
def reminder_comment
|
75
|
+
load_meta_details
|
76
|
+
@object_with_meta_data.reminder_comment
|
77
|
+
end
|
78
|
+
|
79
|
+
# Return the name of the workflow, that is assigned to this object.
|
80
|
+
def workflow_name
|
81
|
+
load_meta_details
|
82
|
+
@object_with_meta_data.workflow_name
|
83
|
+
end
|
84
|
+
|
85
|
+
# Return the current editor as a String. If there is no edited content,
|
86
|
+
# which is always the case in live mode, an empty String is returned.
|
87
|
+
# The 'contents' table is queried for this information.
|
88
|
+
def editor
|
89
|
+
return @editor if @editor
|
90
|
+
|
91
|
+
load_meta_details
|
92
|
+
|
93
|
+
content_id = if self.edited?
|
94
|
+
@object_with_meta_data.edited_content_id
|
95
|
+
else
|
96
|
+
@object_with_meta_data.released_cont_id
|
97
|
+
end
|
98
|
+
|
99
|
+
if content_id
|
100
|
+
content = RailsConnector::Content.find(content_id)
|
101
|
+
@editor = content.editor
|
102
|
+
else
|
103
|
+
@editor = ''
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def committed?
|
108
|
+
load_meta_details
|
109
|
+
!@object_with_meta_data.committed_cont_id.nil?
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
# Load the objects details from the `objects' tables.
|
115
|
+
def load_meta_details #:nodoc:
|
116
|
+
return if @object_with_meta_data
|
117
|
+
|
118
|
+
@object_with_meta_data = RailsConnector::ObjectWithMetaData.find(self.id)
|
119
|
+
|
120
|
+
# reset depending instance variables
|
121
|
+
@editor = nil
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
# the methods in this module will become class methods
|
126
|
+
module ClassMethods
|
127
|
+
|
128
|
+
# The RailsConnector::ObjClass object for this file format.
|
129
|
+
# This will only return a proper object if a custom Ruby class exists
|
130
|
+
# and will throw a RuntimeError otherwise.
|
131
|
+
def obj_class_definition
|
132
|
+
raise "Obtaining the obj_class_definition of an Obj without custom Ruby class " \
|
133
|
+
"is logically impossible." unless is_custom_ruby_class?
|
134
|
+
@obj_class_definition ||= RailsConnector::Meta::EagerLoader.instance.obj_class(self.name)
|
135
|
+
end
|
136
|
+
alias_method :obj_class_def, :obj_class_definition
|
137
|
+
|
138
|
+
# RailsConnector::AbstractObj returns false, everything else true.
|
139
|
+
# TODO: RailsConnector::AbstractObj does not exist here
|
140
|
+
def is_custom_ruby_class?
|
141
|
+
self != RailsConnector::BasicObj
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|