2020-07-22 02:23:36 +00:00
|
|
|
require "net/http"
|
|
|
|
require "uri"
|
|
|
|
require 'json'
|
|
|
|
|
|
|
|
module Admin::EventNewsHelper
|
2021-08-23 11:44:45 +00:00
|
|
|
module FormHelper
|
|
|
|
extend self
|
|
|
|
extend ActionView::Helpers::FormTagHelper
|
|
|
|
extend ActionView::Helpers::FormOptionsHelper
|
|
|
|
extend ActionView::Helpers::DateHelper
|
|
|
|
extend ActionView::Helpers::TagHelper
|
|
|
|
extend ActionView::Helpers::RenderingHelper
|
|
|
|
extend ActionView::Context
|
|
|
|
extend OrbitBasis::RenderAnywhere
|
|
|
|
extend ActionView::Helpers::UrlHelper
|
|
|
|
extend OrbitFormHelper
|
|
|
|
extend Ckeditor::Helpers::FormHelper
|
|
|
|
def available_locales
|
|
|
|
@available_locales = @available_locales || Site.first.in_use_locales || I18n.available_locales
|
|
|
|
end
|
|
|
|
def set_input_name(input_name)
|
|
|
|
@input_name = input_name
|
|
|
|
end
|
|
|
|
|
|
|
|
def get_input_name
|
|
|
|
@input_name.to_s
|
|
|
|
end
|
|
|
|
def create_lang_panel(field)
|
|
|
|
tmp2 = content_tag(:div,:class => 'btn-group', :data=>{:toggle=>"buttons-radio"}) do
|
|
|
|
available_locales.collect do |key|
|
|
|
|
link_entry_ary = ["##{field}","_#{key}"]
|
|
|
|
link_entry = link_entry_ary.join
|
|
|
|
link_to(I18n.t(key),link_entry,:data=>{:toggle=>"tab"},:class=>"btn #{(key == I18n.locale ? "active" : nil)}",:for=>key)
|
|
|
|
end.join.html_safe
|
|
|
|
end
|
|
|
|
end
|
|
|
|
def multiple_lang_tag(index1,type_of_tag,field,value=nil,custom_options={},combine_element='',exteral_options={},sortable=false)
|
|
|
|
if !index1.nil?
|
|
|
|
all_field = (get_input_name + "[#{index1}][#{field}][parant]").gsub(/\[/,'_').gsub(/\]/,'')
|
|
|
|
else
|
|
|
|
all_field = (get_input_name + "[#{field}][parant]").gsub(/\[/,'_').gsub(/\]/,'')
|
|
|
|
end
|
|
|
|
tmp = (available_locales.collect do |locale|
|
|
|
|
active_flag = ((locale == I18n.locale) ? ' in active' : '')
|
|
|
|
content_tag(:div,:class => "tab-content fade#{active_flag}",:id=>"#{all_field}_#{locale}") do
|
|
|
|
value_locale = value[locale.to_s] rescue nil
|
|
|
|
if !index1.nil?
|
|
|
|
self.__send__("#{type_of_tag}_tag","#{get_input_name}[#{index1}][#{field}][#{locale}]",value_locale,custom_options)
|
|
|
|
else
|
|
|
|
self.__send__("#{type_of_tag}_tag","#{get_input_name}[#{field}][#{locale}]",value_locale,custom_options)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end.join + create_lang_panel(all_field)).html_safe + combine_element
|
|
|
|
|
|
|
|
if sortable
|
|
|
|
if exteral_options['style'].nil?
|
|
|
|
exteral_options['style'] = 'display: flex;align-items: center;flex-wrap: nowrap;'
|
|
|
|
else
|
|
|
|
exteral_options['style'] = exteral_options['style'] + 'display: flex;align-items: center;flex-wrap: nowrap;'
|
|
|
|
end
|
|
|
|
content_tag(:div,{:class => "tab-panel border"}.merge(exteral_options)) do
|
|
|
|
("<i class=\"icons-list-2\" style=\"cursor: grab;font-size: x-large;\"></i>" +content_tag(:div) do
|
|
|
|
tmp
|
|
|
|
end).html_safe
|
|
|
|
end
|
|
|
|
else
|
|
|
|
content_tag(:div,{:class => "tab-panel"}.merge(exteral_options)) do
|
|
|
|
tmp
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.thead(field,center=false,enable_sort=true)
|
|
|
|
sort = field.to_s.include?('.') ? field.to_s.split('.')[1] : field.to_s
|
|
|
|
active = OrbitHelper.params[:sort].eql? sort
|
|
|
|
order = active ? (["asc", "desc"]-[OrbitHelper.params[:order]]).first : "asc"
|
|
|
|
arrow = (order.eql? "desc") ? "<b class='icons-arrow-up-3'></b>" : "<b class='icons-arrow-down-4'></b>"
|
|
|
|
klass = field.eql?(:title) ? "span5" : "span2"
|
|
|
|
th_data = (sort=="preview" || !enable_sort) ? EventNewsCustomTitle.get_trans(field) : "<a href='?sort=#{sort}&order=#{order}'>#{EventNewsCustomTitle.get_trans(field)} #{active ? arrow : ""}</a>"
|
|
|
|
"<th class='#{klass} #{active ? "active" : ""}' style='#{center ? "text-align:center" : ""}'>#{th_data}</th>".html_safe
|
|
|
|
end
|
|
|
|
|
2020-07-22 02:23:36 +00:00
|
|
|
def page_for_event_news(event_news)
|
|
|
|
ann_page = nil
|
|
|
|
pages = Page.where(:module=>'event_news')
|
|
|
|
|
|
|
|
pages.each do |page|
|
|
|
|
if page.categories.count ==1
|
|
|
|
if page.categories.include?(event_news.category.id.to_s)
|
|
|
|
ann_page = page
|
|
|
|
end
|
|
|
|
end
|
|
|
|
break if !ann_page.nil?
|
|
|
|
end
|
|
|
|
|
|
|
|
if ann_page.nil?
|
|
|
|
pages.each do |page|
|
|
|
|
if page.categories.include?(event_news.category.id.to_s)
|
|
|
|
ann_page = page
|
|
|
|
end
|
|
|
|
break if !ann_page.nil?
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
ann_page = pages.first if ann_page.nil?
|
|
|
|
request.protocol+(request.host_with_port+ann_page.url+'/'+event_news.to_param).gsub('//','/') rescue "/"
|
|
|
|
end
|
|
|
|
|
|
|
|
def import_this_event_news(row,categories,tags)
|
|
|
|
value = {}
|
|
|
|
anns = EventNews.new
|
|
|
|
row.cells.each_with_index do |cell,index|
|
2021-07-16 07:49:43 +00:00
|
|
|
val = cell.nil? ? nil : cell.value
|
2020-07-22 02:23:36 +00:00
|
|
|
next if val.nil? || val == ""
|
|
|
|
case index
|
|
|
|
when 0
|
|
|
|
anns.category = categories[val.to_i]
|
|
|
|
when 1
|
|
|
|
new_tags = []
|
|
|
|
if (val.include?(",") rescue false)
|
|
|
|
ts = val.split(",")
|
|
|
|
ts.each do |t|
|
|
|
|
new_tags << tags[t.to_i]
|
|
|
|
end
|
|
|
|
else
|
|
|
|
new_tags << tags[val.to_i]
|
|
|
|
end
|
|
|
|
anns.tags=new_tags
|
|
|
|
when 2
|
|
|
|
anns.event_date = val
|
|
|
|
when 3
|
|
|
|
anns.postdate = val
|
|
|
|
when 4
|
|
|
|
anns.deadline = val
|
|
|
|
when 5
|
|
|
|
anns.is_top = (val.to_i == 1 ? true : false)
|
|
|
|
when 6
|
|
|
|
anns.is_hot = (val.to_i == 1 ? true : false)
|
|
|
|
when 7
|
|
|
|
anns.is_hidden = (val.to_i == 1 ? true : false)
|
|
|
|
when 8
|
|
|
|
anns.remote_image_url = val
|
|
|
|
when 9
|
|
|
|
value["en"] = val
|
2021-07-16 10:41:00 +00:00
|
|
|
anns.image_description_translations = value.clone
|
2020-07-22 02:23:36 +00:00
|
|
|
when 10
|
|
|
|
value["zh_tw"] = val
|
2021-07-16 10:41:00 +00:00
|
|
|
anns.image_description_translations = value.clone
|
2020-07-22 02:23:36 +00:00
|
|
|
value = {}
|
|
|
|
when 11
|
|
|
|
value["en"] = val
|
2021-07-16 10:41:00 +00:00
|
|
|
anns.title_translations = value.clone
|
2020-07-22 02:23:36 +00:00
|
|
|
when 12
|
|
|
|
value["zh_tw"] = val
|
2021-07-16 10:41:00 +00:00
|
|
|
anns.title_translations = value.clone
|
2020-07-22 02:23:36 +00:00
|
|
|
value = {}
|
|
|
|
when 13
|
|
|
|
value["en"] = val
|
2021-07-16 10:41:00 +00:00
|
|
|
anns.speaker_translations = value.clone
|
2020-07-22 02:23:36 +00:00
|
|
|
when 14
|
|
|
|
value["zh_tw"] = val
|
2021-07-16 10:41:00 +00:00
|
|
|
anns.speaker_translations = value.clone
|
2020-07-22 02:23:36 +00:00
|
|
|
value = {}
|
|
|
|
when 15
|
|
|
|
value["en"] = val
|
2021-07-16 10:41:00 +00:00
|
|
|
anns.host_translations = value.clone
|
2020-07-22 02:23:36 +00:00
|
|
|
when 16
|
|
|
|
value["zh_tw"] = val
|
2021-07-16 10:41:00 +00:00
|
|
|
anns.host_translations = value.clone
|
2020-07-22 02:23:36 +00:00
|
|
|
value = {}
|
|
|
|
when 17
|
|
|
|
value["en"] = val
|
2021-07-16 10:41:00 +00:00
|
|
|
anns.subtitle_translations = value.clone
|
2020-07-22 02:23:36 +00:00
|
|
|
when 18
|
|
|
|
value["zh_tw"] = val
|
2021-07-16 10:41:00 +00:00
|
|
|
anns.subtitle_translations = value.clone
|
2020-07-22 02:23:36 +00:00
|
|
|
value = {}
|
|
|
|
when 19
|
|
|
|
value["en"] = val
|
2021-07-16 10:41:00 +00:00
|
|
|
anns.text_translations = value.clone
|
2020-07-22 02:23:36 +00:00
|
|
|
when 20
|
|
|
|
value["zh_tw"] = val
|
2021-07-16 10:41:00 +00:00
|
|
|
anns.text_translations = value.clone
|
2020-07-22 02:23:36 +00:00
|
|
|
value = {}
|
|
|
|
when 21
|
|
|
|
value["en"] = val
|
2021-07-16 10:41:00 +00:00
|
|
|
anns.notes_translations = value.clone
|
2020-07-22 02:23:36 +00:00
|
|
|
when 22
|
|
|
|
value["zh_tw"] = val
|
2021-07-16 10:41:00 +00:00
|
|
|
anns.notes_translations = value.clone
|
2020-07-22 02:23:36 +00:00
|
|
|
value = {}
|
|
|
|
when 23
|
|
|
|
links = val.split(";") rescue []
|
|
|
|
desc_en = row.cells[24].value.split(";") rescue []
|
|
|
|
desc_zh_tw = row.cells[25].value.split(";") rescue []
|
|
|
|
links.each_with_index do |link,i|
|
|
|
|
bl = EventNewsLink.new
|
|
|
|
bl.url = link.strip
|
|
|
|
bl.title_translations = {"en" => desc_en[i], "zh_tw" => desc_zh_tw[i]}
|
|
|
|
bl.event_news_id = anns.id
|
|
|
|
bl.save
|
|
|
|
end
|
|
|
|
when 26
|
|
|
|
files = val.split(";") rescue []
|
|
|
|
desc_en = row.cells[27].value.split(";") rescue []
|
|
|
|
desc_zh_tw = row.cells[28].value.split(";") rescue []
|
|
|
|
alt_en = row.cells[29].value.split(";") rescue []
|
|
|
|
alt_zh_tw = row.cells[30].value.split(";") rescue []
|
|
|
|
files.each_with_index do |file, i|
|
|
|
|
bf = EventNewsFile.new
|
|
|
|
bf.remote_file_url = file.strip rescue nil
|
2021-08-09 04:22:45 +00:00
|
|
|
bf.title_translations = {"en" => (alt_en[i] rescue ""), "zh_tw" => (alt_zh_tw[i] rescue "")}
|
|
|
|
bf.description_translations = {"en" => (desc_en[i] rescue ""), "zh_tw" => (desc_zh_tw[i] rescue "")}
|
2020-07-22 02:23:36 +00:00
|
|
|
bf.event_news_id = anns.id
|
|
|
|
bf.save
|
|
|
|
end
|
2021-07-16 08:17:32 +00:00
|
|
|
when 31
|
|
|
|
value["en"] = val
|
2021-07-16 10:41:00 +00:00
|
|
|
anns.place_translations = value.clone
|
2021-07-16 08:17:32 +00:00
|
|
|
when 32
|
|
|
|
value["zh_tw"] = val
|
2021-07-16 10:41:00 +00:00
|
|
|
anns.place_translations = value.clone
|
2021-07-16 08:17:32 +00:00
|
|
|
value = {}
|
2021-08-09 04:22:45 +00:00
|
|
|
when 33
|
|
|
|
anns.event_end_date = val
|
|
|
|
when 34
|
|
|
|
carousel_images = val.split(";") rescue []
|
|
|
|
desc_en = row.cells[35].value.split(";") rescue []
|
|
|
|
desc_zh_tw = row.cells[36].value.split(";") rescue []
|
|
|
|
carousel_images.each_with_index do |image, i|
|
|
|
|
bc = EventCarouselImage.new
|
|
|
|
bc.remote_file_url = image.strip rescue nil
|
|
|
|
bc.description_translations = {"en" => (desc_en[i] rescue ""), "zh_tw" => (desc_zh_tw[i] rescue "")}
|
|
|
|
bc.event_news_id = anns.id
|
|
|
|
bc.save
|
|
|
|
end
|
2020-07-22 02:23:36 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
anns.create_user_id = current_user.id.to_s
|
|
|
|
anns.update_user_id = current_user.id.to_s
|
|
|
|
anns.approved = true
|
|
|
|
anns.save
|
|
|
|
end
|
|
|
|
|
|
|
|
def send_rejection_email(event_news,locale)
|
|
|
|
user = User.find(event_news.create_user_id) rescue nil
|
|
|
|
if !user.nil?
|
|
|
|
email = user.member_profile.email
|
|
|
|
if !email.nil? && email != ""
|
|
|
|
url = "http://#{request.host_with_port}/admin/event_news/#{event_news.id}/edit"
|
|
|
|
datatosend = "<h3>Hello #{user.name},</h3><p>#{current_user.name} #{t("event_news.rejected_event_news")} : #{event_news.rejection_reason} <a href='#{url}'> #{t("event_news.click_here_to_see")}</a></p>"
|
|
|
|
mail = Email.new(:mail_to => email, :mail_subject => "EventNews rejected公告未通過 : #{event_news.title_translations[locale]}.", :template => "email/event_news_email.html.erb", :template_data => {"html" => datatosend})
|
|
|
|
mail.save
|
|
|
|
mail.deliver rescue nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def send_notification_mail_to_managers(event_news, type, locale)
|
|
|
|
users = []
|
|
|
|
if @event_news_setting.email_to.include?("managers")
|
|
|
|
authorizations = Authorization.where(:module_app_id => @module_app.id)
|
|
|
|
users = authorizations.collect do |auth|
|
|
|
|
auth.user
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if @event_news_setting.email_to.include?("admins")
|
|
|
|
wg = Workgroup.where(:key => "admin").first
|
|
|
|
admins = User.where(:workgroup_id => wg.id)
|
|
|
|
users.delete(nil)
|
|
|
|
users = users.concat(admins.to_a)
|
|
|
|
end
|
|
|
|
if @event_news_setting.email_to.include?("approvers")
|
|
|
|
approvers = User.find(@event_news_setting.approvers).to_a rescue []
|
|
|
|
auths = Authorization.where(:category_id => event_news.category_id).collect{|a| a.user}
|
|
|
|
users = users.concat(approvers & auths)
|
|
|
|
end
|
|
|
|
users.each do |user|
|
|
|
|
email = user.member_profile.email
|
|
|
|
if !email.nil? && email != ""
|
|
|
|
send_email(user.name, email, event_news, type, locale)
|
|
|
|
# sleep(1)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def send_email(name, useremail, event_news, type, locale)
|
|
|
|
url = "http://#{request.host_with_port}/admin/event_news?url=#{page_for_event_news(event_news).sub("http://" + request.host_with_port, "")}&id=#{event_news.id}"
|
|
|
|
|
|
|
|
case type
|
|
|
|
when "approval"
|
|
|
|
datatosend = "<h3>#{t("event_news.approval_mail_hi", :name => name)},</h3><p>#{t("event_news.submitted_new_event_news", :poster => current_user.name)}<br /><br />#{t("event_news.approval_event_news_title")} : #{event_news.title_translations[locale]} <br /> #{t("event_news.click_here_to_see")} : <a href='#{url}'> #{url} </a></p>"
|
|
|
|
when "reapproval"
|
|
|
|
datatosend = "<h3>#{t("event_news.approval_mail_hi", :name => name)},</h3><p>#{t("event_news.updated_event_news", :poster => current_user.name)}<br /><br />#{t("event_news.approval_event_news_title")} : #{event_news.title_translations[locale]} <br /> #{t("event_news.click_here_to_see")} : <a href='#{url}'> #{url} </a></p>"
|
|
|
|
end
|
|
|
|
email = Email.new(:mail_to => useremail, :mail_subject => " #{t("event_news.event_news_subject")} : #{event_news.title_translations[locale]}.", :template => "email/event_news_email.html.erb", :template_data => {"html" => datatosend})
|
|
|
|
email.save
|
|
|
|
email.deliver rescue nil
|
|
|
|
end
|
|
|
|
|
|
|
|
def download_tmp_xml(url)
|
|
|
|
xml = File.join(Rails.root, "tmp", "ann_cc_ntu.xml")
|
|
|
|
open(xml, 'wb') do |fo|
|
|
|
|
fo.print open(url).read
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def import_from_tmp_xml(file)
|
|
|
|
xml = Nokogiri::XML(file)
|
|
|
|
return if xml.nil?
|
|
|
|
event_news = []
|
|
|
|
xml.xpath("//channel").xpath("//item").each do |anns|
|
|
|
|
event_news << {
|
|
|
|
:title => (anns>"title").text,
|
|
|
|
:category => (anns>"category").text,
|
|
|
|
:postdate => (anns>"pubDate").text,
|
|
|
|
:text => (anns>"description").text,
|
|
|
|
:rss2_sn => (anns>"link").text.split("=").last
|
|
|
|
}
|
|
|
|
end
|
|
|
|
event_news.each do |anns|
|
|
|
|
ma = ModuleApp.where(:key => "event_news").first
|
|
|
|
cat = Category.where(:title => anns[:category]).first rescue nil
|
|
|
|
if cat.nil?
|
|
|
|
cat = Category.create(:title_translations => {"en" => anns[:category], "zh_tw" => anns[:category]}, :module_app_id => ma.id)
|
|
|
|
end
|
|
|
|
ann = EventNews.where(:rss2_sn => anns[:rss2_sn]).first rescue nil
|
|
|
|
if ann.nil?
|
|
|
|
ann = EventNews.new(:title_translations => {"en" => "", "zh_tw" => anns[:title]}, :postdate => anns[:postdate], :subtitle_translations => {"en" => "", "zh_tw" => anns[:title]}, :text_translations => {"en" => "", "zh_tw" => anns[:text]}, :rss2_sn => anns[:rss2_sn], :category_id => cat.id, :approved => true, :create_user_id => current_user.id)
|
|
|
|
else
|
|
|
|
ann.update_attributes(:title_translations => {"en" => "", "zh_tw" => anns[:title]}, :postdate => anns[:postdate], :subtitle_translations => {"en" => "", "zh_tw" => anns[:title]}, :text_translations => {"en" => "", "zh_tw" => anns[:text]})
|
|
|
|
end
|
|
|
|
ann.save
|
|
|
|
end
|
|
|
|
File.delete(file)
|
|
|
|
end
|
|
|
|
|
|
|
|
def import_from_wordpress(xmlfile)
|
|
|
|
xml_file = File.read(xmlfile)
|
|
|
|
doc = Nokogiri::XML.parse(xml_file)
|
|
|
|
|
|
|
|
doc.xpath("//channel").each do|channel_data|
|
|
|
|
channel_data.xpath('//item').each do|itme|
|
|
|
|
|
|
|
|
bu = EventNews.where(:rss2_sn => itme.xpath('wp:post_id').text ).first rescue nil
|
|
|
|
if bu.nil?
|
|
|
|
bu = EventNews.new
|
|
|
|
bu.approved = true
|
|
|
|
bu.rss2_sn = itme.xpath('wp:post_id').text
|
|
|
|
bu.title_translations = {"en" => itme.xpath('title').text, "zh_tw" => itme.xpath('title').text}
|
|
|
|
bu.text_translations = {"en" => itme.xpath('content:encoded').text, "zh_tw" => itme.xpath('content:encoded').text}
|
|
|
|
bu.postdate = itme.xpath('wp:post_date').text
|
|
|
|
|
|
|
|
itme.xpath('category').each do |i_cate|
|
|
|
|
if i_cate["domain"].to_s == "category"
|
|
|
|
|
|
|
|
cat = @module_app.categories.where(:title => i_cate.text.to_s).first rescue nil
|
|
|
|
if cat.nil?
|
|
|
|
cat = Category.new
|
|
|
|
cat.module_app = @module_app
|
|
|
|
cat.title_translations = {"en" => i_cate.text.to_s, "zh_tw" => i_cate.text.to_s}
|
|
|
|
cat.save
|
|
|
|
end
|
|
|
|
bu.category = cat
|
|
|
|
|
|
|
|
elsif i_cate["domain"].to_s == "post_tag"
|
|
|
|
|
|
|
|
tag = Tag.where(:name => i_cate.text.to_s ).first rescue nil
|
|
|
|
if tag.nil?
|
|
|
|
tag = Tag.new
|
|
|
|
tag.name_translations = {"en" => i_cate.text.to_s, "zh_tw" => i_cate.text.to_s}
|
|
|
|
tag.module_app_ids << @module_app.id
|
|
|
|
tag.save
|
|
|
|
end
|
|
|
|
|
|
|
|
bu.tags = tag
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
bu.save
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
end
|
|
|
|
File.delete(xmlfile)
|
|
|
|
end
|
|
|
|
|
|
|
|
def load_access_level
|
|
|
|
if (current_user.is_admin? rescue false)
|
|
|
|
@access_level = "admin"
|
|
|
|
elsif (current_user.is_manager?(@module_app) rescue false)
|
|
|
|
@access_level = "manager"
|
|
|
|
else
|
|
|
|
@access_level = "users"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def user_can_approve?(anns=nil)
|
|
|
|
can_approve = false
|
|
|
|
setting = EventNewsSetting.first
|
|
|
|
case @access_level
|
|
|
|
when "admin"
|
|
|
|
can_approve = true
|
|
|
|
when "manager"
|
|
|
|
can_approve = true
|
|
|
|
else
|
|
|
|
can_approve = false
|
|
|
|
end
|
|
|
|
if !can_approve
|
|
|
|
if !anns.nil?
|
|
|
|
if setting.approvers.include?(current_user.id.to_s)
|
|
|
|
if (current_user.approved_categories_for_module(@module_app).include?(anns.category) rescue false)
|
|
|
|
can_approve = true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
else
|
|
|
|
can_approve = setting.approvers.include?(current_user.id.to_s)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
can_approve
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|