Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Multisit-Plugin implemented as Gem

  • Loading branch information...
commit dfedc34366c69a627d5f87d4fbccc7a1c7d5b1f7 0 parents
@koa authored
Showing with 632 additions and 0 deletions.
  1. +96 −0 .gitignore
  2. +1 −0  .rvmrc
  3. +4 −0 Gemfile
  4. +2 −0  Rakefile
  5. +10 −0 app/controllers/admin/sites_controller.rb
  6. +8 −0 app/controllers/application_controller.rb
  7. +10 −0 app/helpers/admin/sites_helper.rb
  8. +2 −0  app/helpers/application_helper.rb
  9. +4 −0 app/models/hostname.rb
  10. +34 −0 app/models/site.rb
  11. +15 −0 app/views/admin/sites/_actions.html.erb
  12. +51 −0 app/views/admin/sites/_form.html.erb
  13. +5 −0 app/views/admin/sites/_hostname_fields.html.erb
  14. +22 −0 app/views/admin/sites/_records.html.erb
  15. +20 −0 app/views/admin/sites/_site.html.erb
  16. +5 −0 app/views/admin/sites/_sites.html.erb
  17. +1 −0  app/views/admin/sites/edit.html.erb
  18. +15 −0 app/views/admin/sites/index.html.erb
  19. +1 −0  app/views/admin/sites/new.html.erb
  20. +20 −0 app/views/layouts/application.html.haml
  21. +4 −0 app/views/shared/_footer.html.erb
  22. +15 −0 app/views/shared/_header.html.erb
  23. +22 −0 app/views/sitemap/index.xml.builder
  24. +11 −0 config/locales/de.yml
  25. +13 −0 config/locales/en.yml
  26. +9 −0 config/routes.rb
  27. +14 −0 db/migrate/20110418095543_create_sites.rb
  28. +13 −0 db/migrate/20110418095626_create_hostnames.rb
  29. +12 −0 db/seeds.rb
  30. +8 −0 lib/generators/refinerycms_sites_generator.rb
  31. +22 −0 lib/refinerycms-multisite.rb
  32. +5 −0 lib/refinerycms-multisite/version.rb
  33. 0  lib/tasks/.gitkeep
  34. +25 −0 refinerycms-multisite.gemspec
  35. +5 −0 spec/acceptance/acceptance_helper.rb
  36. +5 −0 spec/acceptance/support/helpers.rb
  37. +9 −0 spec/acceptance/support/paths.rb
  38. +41 −0 spec/controllers/admin/site_controller_spec.rb
  39. +13 −0 spec/factories.rb
  40. +14 −0 spec/models/factory_girl_spec.rb
  41. +17 −0 spec/models/hostname_spec.rb
  42. +9 −0 spec/models/site_spec.rb
  43. +20 −0 spec/views/inherited_resource_helpers.rb
96 .gitignore
@@ -0,0 +1,96 @@
+*.gem
+Gemfile.lock
+pkg/*
+
+db/*.sqlite3
+db/*.sqlite3.db
+log/*.log
+tmp/**/*
+
+*~
+\#*\#
+config/database.yml
+coverage/
+db/*.sqlite3
+db/*.sqlite3-journal
+log/*.log
+*.swp
+nbproject
+public/system/**/*
+private/**/*
+.DS_Store
+webrat*html
+mkmf.log
+public/javascripts/all.js
+public/stylesheets/all.css
+.yardoc
+.idea
+# Rails
+.bundle
+db/*.sqlite3
+db/*.sqlite3-journal
+*.log
+tmp/**/*
+
+# Documentation
+doc/api
+doc/app
+doc/*
+.yardoc
+.yardopts
+
+# Public Uploads
+public/system/*
+public/themes/*
+
+# Public Cache
+public/javascripts/cache
+public/stylesheets/cache
+
+# Vendor Cache
+vendor/cache
+
+# Acts as Indexed
+index/**/*
+
+# Refinery Specific
+*.tmproj
+*.autobackupbyrefinery.*
+.autotest
+
+# Mac
+.DS_Store
+
+# Windows
+Thumbs.db
+
+# NetBeans
+nbproject
+
+# Eclipse
+.project
+
+# Redcar
+.redcar
+
+# Rubinius
+*.rbc
+
+# Vim
+*.swp
+*.swo
+
+# RubyMine
+.idea
+
+# Backup
+*~
+
+# Capybara Bug
+capybara-*html
+
+# sass
+.sass-cache
+.sass-cache/*
+
+
1  .rvmrc
@@ -0,0 +1 @@
+rvm ruby-1.8.7@refinerycms-multisite
4 Gemfile
@@ -0,0 +1,4 @@
+source "http://rubygems.org"
+
+# Specify your gem's dependencies in refinerycms-multisite.gemspec
+gemspec
2  Rakefile
@@ -0,0 +1,2 @@
+require 'bundler'
+Bundler::GemHelper.install_tasks
10 app/controllers/admin/sites_controller.rb
@@ -0,0 +1,10 @@
+class Admin::SitesController < Admin::BaseController
+ helper :refinery_settings
+
+ crudify :site,
+ :title_attribute => :name,
+ :order => "name ASC",
+ #:redirect_to_url => :redirect_to_where?,
+ :xhr_paging => true
+
+end
8 app/controllers/application_controller.rb
@@ -0,0 +1,8 @@
+class ApplicationController < ActionController::Base
+ before_filter :load_site
+ protect_from_forgery
+ protected
+ def load_site
+ @site = Site.find_by_hostname(request.host)
+ end
+end
10 app/helpers/admin/sites_helper.rb
@@ -0,0 +1,10 @@
+module Admin::SitesHelper
+ def link_to_add_fields(name, f, association)
+ new_object = f.object.class.reflect_on_association(association).klass.new
+ fields = f.fields_for(association, new_object, :child_index => "new_#{association}") do |builder|
+ render(association.to_s.singularize + "_fields", :f => builder)
+ end
+ link_to_function(name, "add_fields(this, \"#{association}\",
+\"#{escape_javascript(fields)}\")")
+ end
+end
2  app/helpers/application_helper.rb
@@ -0,0 +1,2 @@
+module ApplicationHelper
+end
4 app/models/hostname.rb
@@ -0,0 +1,4 @@
+class Hostname < ActiveRecord::Base
+ belongs_to :site
+ attr_accessible :hostname, :created_at, :updated_at, :site_id
+end
34 app/models/site.rb
@@ -0,0 +1,34 @@
+class Site < ActiveRecord::Base
+ belongs_to :page
+ attr_accessible :name, :page_id, :stylesheet, :hostnames,
+ :hostnames_attributes
+
+ has_many :hostnames,
+ :dependent => :destroy
+
+ accepts_nested_attributes_for :hostnames, :allow_destroy => true
+
+ def self.find_by_hostname(hostname)
+ Site.joins(:hostnames).where(:hostnames=>{:hostname=>hostname}).first ||
+ Site.joins(:hostnames).where(:hostnames=>{:hostname=>'*'}).first
+ end
+
+ # Monkey-Patch the Page-Controller for loading the right root-Page
+ PagesController.class_eval do
+ def home
+ if (@site)
+ error_404 unless (@page = Page.find(@site.page_id)).present?
+ else
+ error_404 unless (@page = Page.where(:link_url => '/').first).present?
+ end
+ end
+ end
+ # Monkey-Patch the Application-Controller for loading the current site
+ ApplicationController.class_eval do
+ before_filter :load_site
+ protected
+ def load_site
+ @site = Site.find_by_hostname(request.host)
+ end
+ end
+end
15 app/views/admin/sites/_actions.html.erb
@@ -0,0 +1,15 @@
+<ul>
+ <li>
+ <%= render :partial => "/shared/admin/search",
+ :locals => {
+ :url => admin_sites_url
+ } %>
+ </li>
+ <li>
+ <%= link_to t('.new'), new_admin_site_url({
+ :dialog => true,
+ :width => 725,
+ :height => 475
+ }), :class => "add_icon" %>
+ </li>
+</ul>
51 app/views/admin/sites/_form.html.erb
@@ -0,0 +1,51 @@
+<%= form_for [:admin, @site] do |f| %>
+
+ <%= render :partial => "/shared/admin/error_messages",
+ :locals => {
+ :object => @site,
+ :include_object_name => true
+ } %>
+
+ <div class='field'>
+ <%= f.label :name %>
+ <%= f.text_field :name, :class => "larger widest" %>
+ </div>
+ <div class='field'>
+ <%= f.label :page_id %>
+ <%= f.select :page_id, nested_set_options(Page, @page) {|i| "#{'-' * i.level} #{i.title}" },
+ :include_blank => true %>
+ </div>
+ <div class='field'>
+ <%= f.label :stylesheet %>
+ <%= f.text_field :stylesheet%>
+ </div>
+ <%= f.label :hostnames %>
+ <%= f.fields_for :hostnames do |hf| %>
+ <%= render 'hostname_fields', :f=>hf %>
+ <% end %>
+ <p><%= link_to_add_fields refinery_icon_tag('add.png'), f,
+ :hostnames %></p>
+ <%= render :partial => "/shared/admin/form_actions",
+ :locals => {
+ :f => f,
+ :continue_editing => false,
+ :submit_button_title => t('.restart_may_be_in_order_html'),
+ :delete_title => t('delete', :scope => 'admin.sites'),
+ :delete_confirmation => t('message', :scope => 'shared.admin.delete',
+ :title => @site.name.to_s.titleize),
+ :hide_delete => (!@site.persisted? or from_dialog?)
+ } %>
+<% end -%>
+<% content_for :javascripts do %>
+ <script type="text/javascript">
+ function remove_fields(link) {
+ $(link).prev("input[type=hidden]").val("1");
+ $(link).closest(".fields").hide();
+ }
+ function add_fields(link, association, content) {
+ var new_id = new Date().getTime();
+ var regexp = new RegExp("new_" + association, "g");
+ $(link).before(content.replace(regexp, new_id));
+ }
+ </script>
+<% end %>
5 app/views/admin/sites/_hostname_fields.html.erb
@@ -0,0 +1,5 @@
+<p class="fields">
+ <%= f.text_field :hostname %>
+ <%= f.hidden_field :_destroy %>
+ <%= link_to_function refinery_icon_tag('delete.png'), "remove_fields(this)" %>
+</p>
22 app/views/admin/sites/_records.html.erb
@@ -0,0 +1,22 @@
+<% if searching? %>
+ <h2>
+ <%= t('results_for', :scope => 'shared.admin.search', :query => params[:search]) %>
+ </h2>
+<% end %>
+<% if @sites.any? %>
+ <div class='pagination_container'>
+ <%= render :partial => 'sites' %>
+ </div>
+<% else %>
+ <p>
+ <% if searching? %>
+ <%= t('no_results', :scope => 'shared.admin.search') %>
+ <% else %>
+ <strong>
+ <%= t('.empty_set') %>
+ <%= t('.create_first', :link => t('new', :scope => 'admin.sites.actions')
+ ) %>
+ </strong>
+ <% end %>
+ </p>
+<% end %>
20 app/views/admin/sites/_site.html.erb
@@ -0,0 +1,20 @@
+<li class='clearfix record <%= cycle("on", "on-hover") %>'>
+ <span class='title'>
+ <%= site.name.to_s.titleize %>
+ <span class="preview">- <%= site.hostnames.map {|host| host.hostname+" "}
+ %></span>
+ </span>
+ <span class='actions'>
+ <%= link_to refinery_icon_tag('application_edit.png'),
+ edit_admin_site_path(site, :dialog => true,
+ :width => 725, :height => 525),
+ :title => t('edit', :scope => 'admin.sites') %>
+ <%= link_to refinery_icon_tag('delete.png'),
+ admin_site_path(site),
+ :class => 'cancel confirm-delete',
+ :title => t('delete', :scope => 'admin.sites'),
+ :confirm => t('message', :scope => 'shared.admin.delete',
+ :title => site.name),
+ :method => :delete %>
+ </span>
+</li>
5 app/views/admin/sites/_sites.html.erb
@@ -0,0 +1,5 @@
+<%= will_paginate @refinery_settings %>
+<ul class="<%= ['pagination_frame', pagination_css_class].compact.join(' ') %>">
+ <%= render :partial => 'site',
+ :collection => @sites %>
+</ul>
1  app/views/admin/sites/edit.html.erb
@@ -0,0 +1 @@
+<%= render :partial => "form" %>
15 app/views/admin/sites/index.html.erb
@@ -0,0 +1,15 @@
+<div id='records'>
+ <%= render :partial => 'records' %>
+</div>
+<div id='actions'>
+ <%= render :partial => 'actions' %>
+</div>
+<% content_for :javascripts do %>
+ <script>
+ $(document).ready(function() {
+ $('#records ul li .actions a[href*=edit]').each(function(i, li) {
+ $(li).attr('name', $(li).attr('tooltip'));
+ });
+ });
+ </script>
+<% end -%>
1  app/views/admin/sites/new.html.erb
@@ -0,0 +1 @@
+<%= render :partial => "form" %>
20 app/views/layouts/application.html.haml
@@ -0,0 +1,20 @@
+!!!
+- if @site.try(:stylesheet)
+ - content_for :stylesheets do
+ = stylesheet_link_tag @site.stylesheet
+= render :partial => "/shared/html_tag"
+- site_bar = render(:partial => "/shared/site_bar", :locals => {:head => true})
+= render :partial => "/shared/head"
+%body
+ /
+ = request.host
+ -#= site_bar
+ = render :partial => "/shared/ie6check" if request.env['HTTP_USER_AGENT'] =~ /MSIE/
+ #container
+ %header
+ = render :partial => "/shared/header"
+ %section#page
+ = yield
+ %footer
+ = render :partial => "/shared/footer"
+ = render :partial => "/shared/javascripts"
4 app/views/shared/_footer.html.erb
@@ -0,0 +1,4 @@
+<p>
+ <%= t('.copyright', :year => Time.now.year,
+ :site_name => @site.try(:name) || RefinerySetting.find_or_set(:site_name, "Company Name")) %>
+</p>
15 app/views/shared/_header.html.erb
@@ -0,0 +1,15 @@
+<h1 id='logo'>
+ <%= link_to RefinerySetting.find_or_set(:site_name, "Company Name"), root_path %>
+</h1>
+<%=
+ ::Fiber.new {
+ ::Fiber.yield(
+ render(:partial => "/shared/menu", :locals => {
+ :dom_id => 'menu',
+ :css => 'menu',
+ :roots => @menu_pages.where(:parent_id => @site.try(:page_id)),
+ :collection => @menu_pages.where(@menu_pages.arel_table[:parent_id].not_eq(nil)),
+ :selected_item => (@page if defined?(::Page))
+ })
+ )
+ }.resume %>
22 app/views/sitemap/index.xml.builder
@@ -0,0 +1,22 @@
+xml.instruct!
+
+xml.urlset "xmlns" => "http://www.sitemaps.org/schemas/sitemap/0.9" do
+
+ @pages.each do |page|
+ # exclude sites that are external to our own domain.
+ page_url = if page.url.is_a?(Hash)
+ # This is how most pages work without being overriden by link_url
+ page.url.merge({:only_path => false})
+ elsif page.url.to_s !~ /^http/
+ # handle relative link_url addresses.
+ "#{request.protocol}#{request.host_with_port}#{page.url}"
+ end
+
+ # Add XML entry only if there is a valid page_url found above.
+ xml.url do
+ xml.loc url_for(page_url)
+ xml.lastmod page.updated_at.to_date
+ end if page_url.present?
+ end
+
+end
11 config/locales/de.yml
@@ -0,0 +1,11 @@
+de:
+ plugins:
+ sites:
+ title: Sites
+ admin:
+ sites:
+ actions:
+ new: 'Site hinzufügen'
+ edit: 'Site anpassen'
+ delete: 'Site entfernen'
+
13 config/locales/en.yml
@@ -0,0 +1,13 @@
+# Sample localization file for English. Add more files in this directory for other locales.
+# See http://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
+
+en:
+ plugins:
+ sites:
+ title: Sites
+ admin:
+ sites:
+ actions:
+ new: 'new site'
+ edit: 'edit site'
+ delete: 'remove site'
9 config/routes.rb
@@ -0,0 +1,9 @@
+Refinery::Application.routes.draw do
+
+ scope(:path => 'refinery', :as => 'admin', :module => 'admin') do
+ resources :sites do
+ resources :hostnames
+ end
+ end
+
+end
14 db/migrate/20110418095543_create_sites.rb
@@ -0,0 +1,14 @@
+class CreateSites < ActiveRecord::Migration
+ def self.up
+ create_table :sites do |t|
+ t.string :name
+ t.integer :page_id
+ t.string :stylesheet
+ t.timestamps
+ end
+ end
+
+ def self.down
+ drop_table :sites
+ end
+end
13 db/migrate/20110418095626_create_hostnames.rb
@@ -0,0 +1,13 @@
+class CreateHostnames < ActiveRecord::Migration
+ def self.up
+ create_table :hostnames do |t|
+ t.string :hostname
+ t.integer :site_id
+ t.timestamps
+ end
+ end
+
+ def self.down
+ drop_table :hostnames
+ end
+end
12 db/seeds.rb
@@ -0,0 +1,12 @@
+# This file should contain all the record creation needed to seed the database with its default values.
+# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
+#
+# Examples:
+#
+# cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }])
+# Mayor.create(:name => 'Daley', :city => cities.first)
+# Refinery seeds
+Dir[Rails.root.join('db', 'seeds', '*.rb').to_s].each do |file|
+ puts "Loading db/seeds/#{file.split(File::SEPARATOR).last}"
+ load(file)
+end
8 lib/generators/refinerycms_sites_generator.rb
@@ -0,0 +1,8 @@
+require 'refinery/generators'
+
+class RefinerycmsMultisite < ::Refinery::Generators::EngineInstaller
+
+ source_root File.expand_path('../../../', __FILE__)
+ engine_name "refinery_multisite"
+
+end
22 lib/refinerycms-multisite.rb
@@ -0,0 +1,22 @@
+
+require 'refinerycms-base'
+
+module Refinery
+ module Sites
+ class Engine < Rails::Engine
+ initializer "static assets" do |app|
+ app.middleware.insert_after ::ActionDispatch::Static, ::ActionDispatch::Static, "#{root}/public"
+ end
+
+ config.after_initialize do
+ Refinery::Plugin.register do |plugin|
+ plugin.name = "sites"
+ plugin.activity = {
+ :class => Site,
+ :title => 'name'
+ }
+ end
+ end
+ end
+ end
+end
5 lib/refinerycms-multisite/version.rb
@@ -0,0 +1,5 @@
+module Refinerycms
+ module Multisite
+ VERSION = "0.0.1"
+ end
+end
0  lib/tasks/.gitkeep
No changes.
25 refinerycms-multisite.gemspec
@@ -0,0 +1,25 @@
+# -*- encoding: utf-8 -*-
+$:.push File.expand_path("../lib", __FILE__)
+require "refinerycms-multisite/version"
+
+Gem::Specification.new do |s|
+ s.name = "refinerycms-multisite"
+ s.version = Refinerycms::Multisite::VERSION
+ s.platform = Gem::Platform::RUBY
+ s.authors = ["Andreas König"]
+ s.email = ["koa@panter.ch"]
+ s.homepage = ""
+ s.summary = %q{Multisite-Plugin for Refinery-CMS}
+ s.description = %q{Manage multiple Site with Refinery-CMS}
+
+ s.rubyforge_project = "refinerycms-multisite"
+ s.add_dependency "refinerycms-pages"
+ s.add_development_dependency "refinerycms-testing"
+ s.add_development_dependency "shoulda-matchers"
+
+
+ s.files = `git ls-files`.split("\n")
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
+ s.require_paths = ["lib"]
+end
5 spec/acceptance/acceptance_helper.rb
@@ -0,0 +1,5 @@
+require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
+require "steak"
+
+# Put your acceptance spec helpers inside /spec/acceptance/support
+Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
5 spec/acceptance/support/helpers.rb
@@ -0,0 +1,5 @@
+module HelperMethods
+ # Put helper methods you need to be available in all tests here.
+end
+
+RSpec.configuration.include HelperMethods, :type => :acceptance
9 spec/acceptance/support/paths.rb
@@ -0,0 +1,9 @@
+module NavigationHelpers
+ # Put helper methods related to the paths in your application here.
+
+ def homepage
+ "/"
+ end
+end
+
+RSpec.configuration.include NavigationHelpers, :type => :acceptance
41 spec/controllers/admin/site_controller_spec.rb
@@ -0,0 +1,41 @@
+require 'spec_helper'
+
+describe Admin::SitesController do
+ include Devise::TestHelpers
+
+ before(:each) do
+ sign_in Factory.create(:refinery_user)
+ @origin_site=Factory.create(:site)
+ @origin_site.save
+ end
+
+ context 'GET on new' do
+ before(:each) { get :new }
+ it('assigns a new site') { assigns(:site).should be_a_new(Site) }
+ it('responds with success') { response.should be_success }
+ end
+
+ context 'POST on create' do
+ before(:each) { post :create, :post => Factory.attributes_for(:site) }
+ it('responds with a redirect') do
+ response.should redirect_to(:action=>:index)
+ end
+ it('creates a new site') { assigns(:site).should_not be_a_new_record }
+ end
+
+ context 'persisted site' do
+ let(:site) { Factory(:site) }
+ context 'GET on edit' do
+ before(:each) { get :edit, :id => site }
+ it('response with success') { response.should be_success }
+ end
+ context 'GET on index' do
+ before(:each) { get :index }
+ it('response with success') { response.should be_success}
+ it('assigns sites') do
+ assigns(:sites).should include(@origin_site)
+ end
+ end
+ end
+
+end
13 spec/factories.rb
@@ -0,0 +1,13 @@
+Factory.define :post do |f|
+ f.title 'Post title'
+end
+
+Factory.define :hostname do |f|
+ f.hostname 'example.com'
+end
+
+Factory.define :site do |f|
+ f.name 'ex'
+ f.hostnames {|site| [site.association(:hostname),
+ site.association(:hostname, :hostname=>'test.host')]}
+end
14 spec/models/factory_girl_spec.rb
@@ -0,0 +1,14 @@
+require 'spec_helper'
+
+describe "FactoryGirl" do
+
+ describe "a hostname by factory" do
+ let(:hostname){Factory.build(:hostname)}
+ it{hostname.should be_valid}
+ end
+
+ describe "a sites by factory" do
+ let(:site){Factory.build(:site)}
+ it{site.should be_valid}
+ end
+end
17 spec/models/hostname_spec.rb
@@ -0,0 +1,17 @@
+require 'spec_helper'
+
+describe Hostname do
+ it 'initializes' do
+ Hostname.new.should_not be_nil
+ end
+ describe "hostname" do
+ it {should allow_value('localhost').for(:hostname)}
+ end
+ describe "find host example.com" do
+ it 'find saved hostname' do
+ Factory.build(:hostname, :hostname=>'example.com').save
+
+ Hostname.find_by_hostname('example.com').should_not be_nil
+ end
+ end
+end
9 spec/models/site_spec.rb
@@ -0,0 +1,9 @@
+require 'spec_helper'
+require File.expand_path('../../factories', __FILE__)
+
+describe Site do
+ describe 'find sites by hostname' do
+ Factory.build(:site).save
+ puts Site.find_by_hostname('example.com')
+ end
+end
20 spec/views/inherited_resource_helpers.rb
@@ -0,0 +1,20 @@
+module InheritedResourceHelpers
+
+ # mock url and other helper methods contributed by inherited resource
+ def mock_inherited_resource(resource)
+ view.stub(:collection_path).and_return('/collection_path')
+ view.stub(:edit_resource_path).and_return('/edit_resource_path')
+ view.stub(:new_resource_path).and_return('/new_resource_path')
+ view.stub(:parent_path).and_return('/parent_path')
+ view.stub(:resource_path).and_return('/resource_path')
+
+ view.stub(:collection_url).and_return('/collection_url')
+ view.stub(:edit_resource_url).and_return('/edit_resource_url')
+ view.stub(:new_resource_url).and_return('/new_resource_url')
+ view.stub(:parent_url).and_return('/parent_url')
+ view.stub(:resource_url).and_return('/resource_url')
+
+ view.stub(:collection).and_return([resource])
+ view.stub(:resource).and_return(resource)
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.