Merge pull request #252 from abf/rosa-build:242-restart_last_build_list
#242 Add ability to clone build lists
This commit is contained in:
commit
bed90dfb63
|
@ -1,6 +1,6 @@
|
|||
$(document).ready(function() {
|
||||
|
||||
$('.autocomplete-form .button.add').click(function() {
|
||||
$(document).on('click', '.autocomplete-form .button.add', function() {
|
||||
var form = $(this).parent();
|
||||
var field = form.attr('field');
|
||||
var subject = $('#' + field + '_field');
|
||||
|
@ -24,7 +24,7 @@ $(document).ready(function() {
|
|||
width: 500
|
||||
});
|
||||
|
||||
$('.autocomplete-form .icon-question-sign').click(function() {
|
||||
$(document).on('click', '.autocomplete-form .icon-question-sign', function() {
|
||||
var field = $(this).parent().attr('field');
|
||||
var dialog = $('#' + field + '_dialog');
|
||||
if (dialog.is(':visible')) { dialog.dialog('close'); } else { dialog.dialog('open'); }
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
$(document).ready(function() {
|
||||
// TODO: Refactor this handler!! It's too complicated.
|
||||
$('#build_list_save_to_repository_id').change(function() {
|
||||
var selected_option = $(this).find("option:selected");
|
||||
var new_form = $('#new_form');
|
||||
var ownership_btn = $('.btn.ownership');
|
||||
var perpage_btn = $('.btn.per_page');
|
||||
|
||||
// TODO: Refactor this handler!! It's too complicated.
|
||||
$(document).on('change', '#build_list_save_to_repository_id', function(){
|
||||
var selected_option = $(this).find("option:selected");
|
||||
var platform_id = selected_option.attr('platform_id');
|
||||
var rep_name = selected_option.text().match(/[\w-]+\/([\w-]+)/)[1];
|
||||
|
||||
|
@ -40,10 +43,10 @@ $(document).ready(function() {
|
|||
}
|
||||
});
|
||||
|
||||
$('#build_list_save_to_repository_id').trigger('change');
|
||||
if($('#from_build_list_id').size() == 0) {
|
||||
$('#build_list_save_to_repository_id').trigger('change');
|
||||
}
|
||||
|
||||
|
||||
var ownership_btn = $('.btn.ownership');
|
||||
ownership_btn.click(function() {
|
||||
ownership_btn.removeClass('active');
|
||||
$('#filter_ownership').val($(this).val());
|
||||
|
@ -51,7 +54,6 @@ $(document).ready(function() {
|
|||
return false;
|
||||
});
|
||||
|
||||
var perpage_btn = $('.btn.per_page');
|
||||
perpage_btn.click(function() {
|
||||
perpage_btn.removeClass('active');
|
||||
$('#per_page').val($(this).val());
|
||||
|
@ -76,6 +78,25 @@ $(document).ready(function() {
|
|||
dateFormat: 'dd/mm/yy',
|
||||
showButtonPanel: true
|
||||
});
|
||||
|
||||
$(document).on('change', '#owner_filter_build_lists, #status_filter_build_lists', function(){
|
||||
$('#datatable').dataTable().fnDraw();
|
||||
});
|
||||
|
||||
$(document).on('click', '#clone_build_list', function() {
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: $(this).attr('href') + '&show=inline',
|
||||
success: function(data){
|
||||
new_form.html(data);
|
||||
$(document).scrollTop(new_form.offset().top);
|
||||
},
|
||||
error: function(data){
|
||||
alert('error') // TODO remove
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
function updatedDefaultArches(selected_option) {
|
||||
|
@ -102,7 +123,7 @@ function addPersonalPlatformToExtraRepos(selected_option, extra_repos) {
|
|||
default_value.attr('label'),
|
||||
default_value.attr('name'),
|
||||
default_value.attr('value')
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
function setBranchSelected(selected_option) {
|
||||
|
@ -117,4 +138,4 @@ function setBranchSelected(selected_option) {
|
|||
// hack for FF to force render of select box.
|
||||
bl_version_sel[0].innerHTML = bl_version_sel[0].innerHTML;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ class Projects::BuildListsController < Projects::BaseController
|
|||
load_and_authorize_resource :build_list, :through => :project, :only => NESTED_ACTIONS, :shallow => true
|
||||
load_and_authorize_resource :except => NESTED_ACTIONS
|
||||
|
||||
before_filter :create_from_build_list, :only => :new
|
||||
|
||||
def index
|
||||
params[:filter].each{|k,v| params[:filter].delete(k) if v.blank? } if params[:filter]
|
||||
|
||||
|
@ -39,6 +41,11 @@ class Projects::BuildListsController < Projects::BaseController
|
|||
end
|
||||
|
||||
def new
|
||||
if params[:show] == 'inline' && params[:build_list_id].present?
|
||||
render '_new_form', :layout => false, :locals => {:project => @project, :build_list => @build_list}
|
||||
else
|
||||
render :new
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
|
@ -50,7 +57,6 @@ class Projects::BuildListsController < Projects::BaseController
|
|||
params[:build_list][:save_to_platform_id] = @platform.id
|
||||
params[:build_list][:auto_publish] = false unless @repository.publish_without_qa?
|
||||
|
||||
|
||||
build_for_platforms = Repository.select(:platform_id).
|
||||
where(:id => params[:build_list][:include_repos]).group(:platform_id).map(&:platform_id)
|
||||
|
||||
|
@ -135,9 +141,38 @@ class Projects::BuildListsController < Projects::BaseController
|
|||
}
|
||||
end
|
||||
|
||||
def list
|
||||
@build_lists = @project.build_lists
|
||||
sort_col = params[:ol_0] || 7
|
||||
sort_dir = params[:sSortDir_0] == 'asc' ? 'asc' : 'desc'
|
||||
order = "build_lists.updated_at #{sort_dir}"
|
||||
|
||||
@build_lists = @build_lists.paginate(:page => (params[:iDisplayStart].to_i/params[:iDisplayLength].to_i).to_i + 1, :per_page => params[:iDisplayLength])
|
||||
@total_build_lists = @build_lists.count
|
||||
@build_lists = @build_lists.where(:user_id => current_user) if params[:owner_filter] == 'true'
|
||||
@build_lists = @build_lists.where(:status => [BuildList::BUILD_ERROR, BuildList::FAILED_PUBLISH, BuildList::REJECTED_PUBLISH]) if params[:status_filter] == 'true'
|
||||
@build_lists = @build_lists.order(order)
|
||||
|
||||
render :partial => 'build_lists_ajax', :layout => false
|
||||
end
|
||||
|
||||
|
||||
protected
|
||||
|
||||
def find_build_list
|
||||
@build_list = BuildList.find(params[:id])
|
||||
end
|
||||
|
||||
def create_from_build_list
|
||||
return if params[:build_list_id].blank?
|
||||
@build_list = BuildList.find params[:build_list_id]
|
||||
|
||||
params[:build_list] ||= {}
|
||||
keys = [:save_to_repository_id, :auto_publish, :include_repos,
|
||||
:project_version, :update_type, :auto_create_container,
|
||||
:extra_repositories, :extra_build_lists, :build_for_platform_id]
|
||||
keys.each { |key| params[:build_list][key] = @build_list.send(key) }
|
||||
params[:arches] = [@build_list.arch_id.to_s]
|
||||
[:owner_filter, :status_filter].each { |t| params[t] = 'true' if %w(true undefined).exclude? params[t] }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -79,7 +79,7 @@ class Ability
|
|||
can [:read, :log, :owned, :everything], BuildList, :user_id => user.id
|
||||
can [:read, :log, :related, :everything], BuildList, :project => {:owner_type => 'User', :owner_id => user.id}
|
||||
can [:read, :log, :related, :everything], BuildList, :project => {:owner_type => 'Group', :owner_id => user.group_ids}
|
||||
can([:read, :log, :everything], BuildList, read_relations_for('build_lists', 'projects')) {|build_list| can? :read, build_list.project}
|
||||
can([:read, :log, :everything, :list], BuildList, read_relations_for('build_lists', 'projects')) {|build_list| can? :read, build_list.project}
|
||||
|
||||
can(:create, BuildList) {|build_list|
|
||||
build_list.project.is_package &&
|
||||
|
@ -212,7 +212,7 @@ class Ability
|
|||
# relations.actor_type = 'User' AND relations.actor_id = :user OR
|
||||
# relations.actor_type = 'Group' AND relations.actor_id IN (:groups)
|
||||
# )
|
||||
|
||||
|
||||
# )",
|
||||
# {
|
||||
# :target_type => parent.classify,
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
%tr{:id => "row#{build_list_counter}", :class => "#{build_list_status_color(build_list.status)}"}
|
||||
%td= link_to build_list.id, build_list
|
||||
%td= [link_to(build_list.id, build_list),
|
||||
(link_to(t('layout.clone'), new_project_build_list_path(build_list.project,
|
||||
:build_list_id => build_list.id)) if can?(:create, build_list))].compact.join('<br/>').html_safe
|
||||
%td
|
||||
= build_list.human_status
|
||||
%br
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
build_lists = @build_lists.map do |build_list|
|
||||
build_for = " (#{build_list.build_for_platform.name})" if build_list.build_for_platform && build_list.save_to_platform.personal?
|
||||
[
|
||||
[link_to(build_list.id, build_list),
|
||||
link_to(t('layout.clone'), new_project_build_list_path(@project, :build_list_id => build_list.id),
|
||||
:id => 'clone_build_list')
|
||||
].join('<br/>').html_safe,
|
||||
build_list.human_status,
|
||||
build_list_version_link(build_list),
|
||||
get_version_release(build_list),
|
||||
link_to("#{build_list.save_to_platform.name}/#{build_list.save_to_repository.name}#{build_for}", [build_list.save_to_platform, build_list.save_to_repository]),
|
||||
build_list.arch.try(:name) || t('layout.arches.unexisted_arch'),
|
||||
link_to(build_list.user.try(:fullname), build_list.user),
|
||||
build_list.updated_at.strftime('%d/%m/%Y')
|
||||
]
|
||||
end
|
||||
|
||||
json.sEcho params[:sEcho].to_i || -1
|
||||
json.iTotalRecords @total_build_lists
|
||||
json.iTotalDisplayRecords @build_lists.count
|
||||
json.aaData build_lists
|
|
@ -1,4 +1,9 @@
|
|||
- Repository.custom_sort(platform.repositories).each do |repo|
|
||||
.both
|
||||
= check_box_tag "build_list[include_repos][]", repo.id, repo.name == 'main' || @project.repositories.map(&:id).include?(repo.id), :id => "include_repos_#{repo.id}", :rep_name => repo.name
|
||||
- if params[:build_list].try(:[], :include_repos).present?
|
||||
- checked = (params[:build_list].try(:[], :include_repos).map(&:to_s) || []).include?(repo.id.to_s)
|
||||
- else checked = false
|
||||
= check_box_tag 'build_list[include_repos][]', repo.id, checked,
|
||||
:disabled => params[:build_list].try(:[], :build_for_platform_id).to_i != platform.id,
|
||||
:id => "include_repos_#{repo.id}", :rep_name => repo.name
|
||||
= label_tag "include_repos_#{repo.id}", repo.name
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
.all
|
||||
%h3= t 'layout.build_lists.last_build_lists'
|
||||
%h4= t 'layout.relations.filters'
|
||||
.both
|
||||
= check_box_tag :owner_filter_build_lists, true, (params[:owner_filter] || 'true') == 'true'
|
||||
= label_tag t('layout.build_lists.only_my_build_lists')
|
||||
.both
|
||||
= check_box_tag :status_filter_build_lists, true, (params[:status_filter] || 'true') == 'true'
|
||||
= label_tag t('layout.build_lists.failed_build_lists')
|
||||
|
||||
- columns, default_column = [], {:type => 'html', :sortable => false, :searchable => false}
|
||||
- 8.times { columns << default_column }
|
||||
= raw datatable(columns, {:sort_by => "[7, 'asc']", :processing => t('layout.processing'), :search => 'false',
|
||||
:pagination_labels => {:previous => t('datatables.previous_label'), :next => t('datatables.next_label')},
|
||||
:empty_label => t('datatables.empty_label'),
|
||||
:info_label => t('datatables.info_label'),
|
||||
:info_empty_label => t('datatables.info_empty_label'),
|
||||
:filtered_label => t('datatables.filtered_label'),
|
||||
:table_dom_id => 'datatable',
|
||||
:auto_width => 'false',
|
||||
:ajax_source => "#{url_for :controller => 'projects/build_lists', :action => :list}",
|
||||
:additional_data => {:owner_filter => "' + $('#owner_filter_build_lists:checked').val() + '",
|
||||
:status_filter => "' + $('#status_filter_build_lists:checked').val() + '"} })
|
||||
|
||||
%table#datatable.tablesorter.list-users{:cellspacing => 0, :cellpadding => 0}
|
||||
%thead
|
||||
%tr
|
||||
%th.th1= t('activerecord.attributes.build_list.id')
|
||||
%th.th2= t('activerecord.attributes.build_list.status')
|
||||
%th.th2= t('diff')
|
||||
%th.th2= t('activerecord.attributes.build_list.project_version')
|
||||
%th.th2= t('activerecord.attributes.build_list.save_to_repository')
|
||||
%th.th1= t('activerecord.attributes.build_list.arch_short')
|
||||
%th.th2= t('activerecord.attributes.build_list.user')
|
||||
%th.th1= t('activerecord.attributes.build_list.updated_at')
|
||||
%tbody
|
||||
%br
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
= form_for new_project_build_list_path(project), :html => { :class => :form, :method => :post } do |f|
|
||||
%section.left
|
||||
%h3= t("activerecord.attributes.build_list.build_for_platform")
|
||||
.all_platforms
|
||||
- availables_main_platforms.each do |pl|
|
||||
.both
|
||||
%div{:id => "build_for_pl_#{pl.id}", :class => 'build_for_pl'}= pl.name
|
||||
.offset25= render 'include_repos', :platform => pl
|
||||
%section.right
|
||||
%h3= t('activerecord.attributes.build_list.save_to_repository')
|
||||
- selected = params[:build_list].try(:fetch, :save_to_repository_id) ? {:selected => params[:build_list][:save_to_repository_id]} : {}
|
||||
.lineForm= f.select :save_to_repository_id, save_to_repositories(project), selected, :id => 'build_list_save_to_repository_id'
|
||||
%h3= t("activerecord.attributes.build_list.project_version")
|
||||
.lineForm= f.select :project_version, versions_for_group_select(project), {}, :selected => params[:build_list].try(:fetch, :project_version) || project.default_branch, :id => 'build_list_project_version'
|
||||
%h3= t("activerecord.attributes.build_list.arch")
|
||||
- Arch.recent.each do |arch|
|
||||
.both
|
||||
- checked = (params[:arches]||[]).include?(arch.id.to_s) || (params[:arches].blank? && controller.action_name == 'new' && Arch::DEFAULT.include?(arch.name))
|
||||
= check_box_tag "arches[]", arch.id, checked, :id => "arches_#{arch.id}"
|
||||
= label_tag "arches_#{arch.id}", arch.name
|
||||
%h3= t('activerecord.attributes.build_list.update_type')
|
||||
- selected = params[:build_list].try(:fetch, :update_type) ? {:selected => params[:build_list][:update_version]} : {}
|
||||
.lineForm= f.select :update_type, BuildList::UPDATE_TYPES, selected, :id => 'build_list_update_types'
|
||||
|
||||
= render 'shared/autocomplete_form',
|
||||
:field => :extra_repositories,
|
||||
:field_class => Repository,
|
||||
:placeholder => 'uxteam_personal',
|
||||
:subject => @build_list,
|
||||
:autocomplete_path => autocomplete_extra_repositories_autocompletes_path,
|
||||
:default_values => project.repositories.select{ |r| r.platform.personal? }
|
||||
= render 'shared/autocomplete_form',
|
||||
:field => :extra_build_lists,
|
||||
:field_class => BuildList,
|
||||
:placeholder => '1000000',
|
||||
:subject => build_list,
|
||||
:autocomplete_path => autocomplete_extra_build_list_autocompletes_path
|
||||
|
||||
%h3= t("activerecord.attributes.build_list.preferences")
|
||||
- [:auto_publish, :auto_create_container].each do |kind|
|
||||
.both
|
||||
- checked = params[:build_list].try(:fetch, kind)
|
||||
= check_box_tag "build_list[#{kind}]", checked, checked
|
||||
= f.label kind
|
||||
%br
|
||||
= hidden_field_tag :from_build_list_id, params[:build_list_id] if params[:build_list_id].present?
|
||||
= f.submit t('layout.projects.build_button'), :data => {'disable-with' => t('layout.processing')}
|
||||
.both
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
= form_for [build_list.project, build_list.project.build_lists.new], :html => { :class => :form, :method => :post } do |f|
|
||||
= hidden_field_tag :build_list_id, build_list.id
|
||||
= f.hidden_field :project_id
|
||||
= f.submit t('layout.build_lists.recreate_build_list'), :data => {'disable-with' => t('layout.processing')}
|
||||
.both
|
|
@ -1,46 +1,5 @@
|
|||
-set_meta_tags :title => [title_object(@project), t('layout.build_lists.new_header')]
|
||||
= form_for [@project, @build_list], :html => { :class => :form, :method => :post } do |f|
|
||||
%section.left
|
||||
%h3= t("activerecord.attributes.build_list.build_for_platform")
|
||||
.all_platforms
|
||||
- availables_main_platforms.each do |pl|
|
||||
.both
|
||||
%div{:id => "build_for_pl_#{pl.id}", :class => 'build_for_pl'}= pl.name
|
||||
.offset25= render 'include_repos', :platform => pl
|
||||
%section.right
|
||||
%h3= t("activerecord.attributes.build_list.save_to_repository")
|
||||
.lineForm= f.select :save_to_repository_id, save_to_repositories(@project)
|
||||
%h3= t("activerecord.attributes.build_list.project_version")
|
||||
.lineForm= f.select :project_version, versions_for_group_select(@project), :selected => params[:build_list].try(:fetch, :project_version) || @project.default_branch
|
||||
%h3= t("activerecord.attributes.build_list.arch")
|
||||
- Arch.recent.each do |arch|
|
||||
.both
|
||||
= check_box_tag "arches[]", arch.id, (params[:arches]||[]).include?(arch.id.to_s) || (controller.action_name == 'new' && Arch::DEFAULT.include?(arch.name)), :id => "arches_#{arch.id}"
|
||||
= label_tag "arches_#{arch.id}", arch.name
|
||||
%h3= t("activerecord.attributes.build_list.update_type")
|
||||
.lineForm= f.select :update_type, BuildList::UPDATE_TYPES
|
||||
|
||||
= render 'shared/autocomplete_form',
|
||||
:field => :extra_repositories,
|
||||
:field_class => Repository,
|
||||
:placeholder => 'uxteam_personal',
|
||||
:subject => @build_list,
|
||||
:autocomplete_path => autocomplete_extra_repositories_autocompletes_path,
|
||||
:default_values => @project.repositories.select{ |r| r.platform.personal? }
|
||||
= render 'shared/autocomplete_form',
|
||||
:field => :extra_build_lists,
|
||||
:field_class => BuildList,
|
||||
:placeholder => '1000000',
|
||||
:subject => @build_list,
|
||||
:autocomplete_path => autocomplete_extra_build_list_autocompletes_path
|
||||
|
||||
%h3= t("activerecord.attributes.build_list.preferences")
|
||||
- [:auto_publish, :auto_create_container].each do |kind|
|
||||
.both
|
||||
= f.check_box kind
|
||||
= f.label kind
|
||||
%br
|
||||
= f.submit t('layout.projects.build_button'), :data => {'disable-with' => t('layout.processing')}
|
||||
.both
|
||||
#new_form= render 'new_form', :project => @project, :build_list => @build_list
|
||||
%br
|
||||
= render 'projects/base/submenu'
|
||||
= render 'last_build_lists', :project => @project
|
||||
= render 'projects/base/submenu'
|
||||
|
|
|
@ -163,6 +163,8 @@
|
|||
- if @build_list.can_create_container? && can?(:create_container, @build_list)
|
||||
= link_to t("layout.build_lists.create_container"), create_container_build_list_path(@build_list),
|
||||
:method => :put, :confirm => t("layout.confirm"), :class => 'button create_container'
|
||||
- if can? :create, @build_list
|
||||
= link_to t('layout.build_lists.recreate_build_list'), new_project_build_list_path(@build_list.project, :build_list_id => @build_list.id), :class => 'button'
|
||||
|
||||
.hr
|
||||
%h3= t("layout.build_lists.items_header")
|
||||
|
|
|
@ -12,8 +12,9 @@
|
|||
.autocomplete-form{:subject_class => subject_class, :field => field, :path => autocomplete_path, :class => field}
|
||||
%h3= t("activerecord.attributes.build_list.#{field}")
|
||||
%span.icon-question-sign
|
||||
.dialog{:title => t("activerecord.attributes.build_list.#{field}"), :id => "#{field}_dialog"}
|
||||
= render "shared/autocomplete_docs/#{field}"
|
||||
- if !(params[:show] == 'inline' && params[:build_list_id].present?)
|
||||
.dialog{:title => t("activerecord.attributes.build_list.#{field}"), :id => "#{field}_dialog"}
|
||||
= render "shared/autocomplete_docs/#{field}"
|
||||
.both
|
||||
= autocomplete_field_tag field, nil, "#{autocomplete_path}?#{{:platform_id => subject.save_to_platform.try(:id)}.to_param}", :placeholder => placeholder, :class => 'autocomplete', :update_elements => {:id => "##{field}_field", :path => "##{field}_field_path", :label => "##{field}_field_label"}
|
||||
= hidden_field_tag field, nil, :id => "#{field}_field"
|
||||
|
@ -24,7 +25,7 @@
|
|||
%table.tablesorter{:cellpadding => "0", :cellspacing => "0"}
|
||||
%tbody
|
||||
- field_name = "#{subject_class}[#{field}][]"
|
||||
- field_class.where(:id => subject.send(field)).each do |extra|
|
||||
- field_class.where(:id => params[:build_list].try(:fetch, field) || subject.send(field)).each do |extra|
|
||||
%tr
|
||||
- if extra.is_a?(BuildList)
|
||||
%td= link_to "#{extra.id} (#{extra.project.name} - #{extra.arch.name})", extra
|
||||
|
|
|
@ -152,6 +152,11 @@ en:
|
|||
show_filter: Show filters
|
||||
hide_filter: Hide filters
|
||||
|
||||
last_build_lists: Last Build Lists
|
||||
recreate_build_list: Recreate Build List
|
||||
only_my_build_lists: Only My
|
||||
failed_build_lists: Only Failed
|
||||
|
||||
flash:
|
||||
build_list:
|
||||
saved: Build list for project version '%{project_version}', platform '%{build_for_platform}' and architecture '%{arch}' has been created successfully
|
||||
|
|
|
@ -151,6 +151,11 @@ ru:
|
|||
show_filter: Показать фильтры
|
||||
hide_filter: Скрыть фильтры
|
||||
|
||||
last_build_lists: Последние сборки
|
||||
recreate_build_list: Пересоздать сборку
|
||||
only_my_build_lists: Только мои
|
||||
failed_build_lists: Только сбойные
|
||||
|
||||
flash:
|
||||
build_list:
|
||||
saved: Билд лист для версии '%{project_version}', платформы '%{build_for_platform}' и архитектуры '%{arch}' создан успешно
|
||||
|
|
|
@ -301,7 +301,10 @@ Rosa::Application.routes.draw do
|
|||
end
|
||||
post "/labels/:label_id" => "issues#destroy_label", :as => :issues_delete_label
|
||||
post "/labels/:label_id/update" => "issues#update_label", :as => :issues_update_label
|
||||
resources :build_lists, :only => [:index, :new, :create]
|
||||
|
||||
resources :build_lists, :only => [:index, :new, :create] do
|
||||
get :list, :on => :collection
|
||||
end
|
||||
resources :collaborators do
|
||||
get :find, :on => :collection
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue