allow users to use shortcut for theme images when editing snippets / layouts / stylesheets + fix minor ui bugs
This commit is contained in:
parent
a03b631a71
commit
2608430cae
@ -19,7 +19,7 @@ h2. Gems
|
||||
|
||||
Here is a short list of main gems used in the application.
|
||||
|
||||
* Rails 3 (beta 3)
|
||||
* Rails 3 (beta 4)
|
||||
* Mongoid
|
||||
* Liquid
|
||||
* Devise
|
||||
@ -30,12 +30,17 @@ h2. Installation
|
||||
|
||||
See the "official website":http://www.locomotiveapp.org
|
||||
|
||||
h2. Team
|
||||
|
||||
* Developers: "Didier Lafforgue":http://www.nocoffee.fr, "Jacques Crocker":http://www.railsjedi.com
|
||||
* UI Designer: "Sacha Greif":http://www.sachagreif.com
|
||||
|
||||
h2. Credits
|
||||
|
||||
Many thanks to "Sacha Greif":http://www.sachagreif.com for his great work on the user interface and the LocomotiveApp website front page.
|
||||
|
||||
"Rodrigo Alvarez":http://blog.codecaster.es/ for his plugin named Congo which gave us a good starting point and for his availability for (very late) tech discussions.
|
||||
|
||||
"Emmanuel Grard":http://www.grardesign.com designed the awesome locomotive illustration in the LocomotiveApp.org landing page.
|
||||
|
||||
h2. Contact
|
||||
|
||||
Feel free to contact me at didier at nocoffee dot fr.
|
||||
|
@ -1,39 +1,43 @@
|
||||
module Admin
|
||||
class ThemeAssetsController < BaseController
|
||||
|
||||
include ActionView::Helpers::SanitizeHelper
|
||||
extend ActionView::Helpers::SanitizeHelper::ClassMethods
|
||||
include ActionView::Helpers::TextHelper
|
||||
|
||||
sections 'settings', 'theme_assets'
|
||||
|
||||
|
||||
respond_to :json, :only => [:create, :update]
|
||||
|
||||
|
||||
def index
|
||||
assets = current_site.theme_assets.all
|
||||
@non_image_assets = assets.find_all { |a| a.stylesheet? || a.javascript? }
|
||||
@image_assets = assets.find_all { |a| a.image? }
|
||||
@flash_assets = assets.find_all { |a| a.movie? }
|
||||
|
||||
|
||||
if request.xhr?
|
||||
render :action => 'images', :layout => false and return
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def create
|
||||
params[:theme_asset] = { :source => params[:file] } if params[:file]
|
||||
|
||||
|
||||
create! do |success, failure|
|
||||
success.json do
|
||||
render :json => {
|
||||
:status => 'success',
|
||||
:name => truncate(@theme_asset.slug, :length => 22),
|
||||
:url => @theme_asset.source.url,
|
||||
:vignette_url => @theme_asset.vignette_url
|
||||
render :json => {
|
||||
:status => 'success',
|
||||
:name => truncate(@theme_asset.slug, :length => 22),
|
||||
:slug => @theme_asset.slug,
|
||||
:url => @theme_asset.source.url,
|
||||
:vignette_url => @theme_asset.vignette_url,
|
||||
:shortcut_url => @theme_asset.shortcut_url
|
||||
}
|
||||
end
|
||||
failure.json { render :json => { :status => 'error' } }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
@ -1,96 +1,118 @@
|
||||
class ThemeAsset
|
||||
|
||||
|
||||
include Locomotive::Mongoid::Document
|
||||
|
||||
## Extensions ##
|
||||
|
||||
## Extensions ##
|
||||
include Models::Extensions::Asset::Vignette
|
||||
|
||||
|
||||
## fields ##
|
||||
field :slug
|
||||
field :content_type
|
||||
field :width, :type => Integer
|
||||
field :height, :type => Integer
|
||||
field :size, :type => Integer
|
||||
field :plain_text
|
||||
mount_uploader :source, ThemeAssetUploader
|
||||
|
||||
|
||||
## associations ##
|
||||
belongs_to_related :site
|
||||
|
||||
|
||||
## callbacks ##
|
||||
before_validation :sanitize_slug
|
||||
before_validation :store_plain_text
|
||||
before_save :set_slug
|
||||
|
||||
|
||||
## validations ##
|
||||
validate :extname_can_not_be_changed
|
||||
validates_presence_of :site, :source
|
||||
validates_presence_of :slug, :if => Proc.new { |a| a.new_record? && a.performing_plain_text? }
|
||||
validates_uniqueness_of :slug, :scope => [:site_id, :content_type]
|
||||
validates_integrity_of :source
|
||||
|
||||
|
||||
## accessors ##
|
||||
attr_accessor :performing_plain_text
|
||||
|
||||
|
||||
## methods ##
|
||||
|
||||
|
||||
%w{movie image stylesheet javascript font}.each do |type|
|
||||
define_method("#{type}?") do
|
||||
self.content_type == type
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def stylesheet_or_javascript?
|
||||
self.stylesheet? || self.javascript?
|
||||
end
|
||||
|
||||
|
||||
def plain_text
|
||||
@plain_text ||= (if self.stylesheet_or_javascript?
|
||||
self.source.read
|
||||
if self.stylesheet_or_javascript?
|
||||
self.plain_text = self.source.read if read_attribute(:plain_text).blank?
|
||||
read_attribute(:plain_text)
|
||||
else
|
||||
nil
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def plain_text=(source)
|
||||
self.performing_plain_text = true if self.performing_plain_text.nil?
|
||||
@plain_text = source
|
||||
write_attribute(:plain_text, source)
|
||||
end
|
||||
|
||||
|
||||
def performing_plain_text?
|
||||
return true if !self.new_record? && self.stylesheet_or_javascript? && self.errors.empty?
|
||||
|
||||
|
||||
!(self.performing_plain_text.blank? || self.performing_plain_text == 'false' || self.performing_plain_text == false)
|
||||
end
|
||||
|
||||
|
||||
def store_plain_text
|
||||
return if self.plain_text.blank?
|
||||
|
||||
self.source = CarrierWave::SanitizedFile.new({
|
||||
:tempfile => StringIO.new(self.plain_text),
|
||||
|
||||
# replace /theme/<content_type>/<slug> occurences by the real amazon S3 url or local files
|
||||
sanitized_source = self.plain_text.gsub(/(\/theme\/([a-z]+)\/([a-z_\-0-9]+)\.[a-z]{2,3})/) do |url|
|
||||
content_type, slug = url.split('/')[2..-1]
|
||||
|
||||
content_type = content_type.singularize
|
||||
slug = slug.split('.').first
|
||||
|
||||
if asset = self.site.theme_assets.where(:content_type => content_type, :slug => slug).first
|
||||
asset.source.url
|
||||
else
|
||||
url
|
||||
end
|
||||
end
|
||||
|
||||
self.source = CarrierWave::SanitizedFile.new({
|
||||
:tempfile => StringIO.new(sanitized_source),
|
||||
:filename => "#{self.slug}.#{self.stylesheet? ? 'css' : 'js'}"
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
def shortcut_url # ex: /theme/stylesheets/application.css is a shortcut for a theme asset (content_type => stylesheet, slug => 'application')
|
||||
File.join('/theme', self.content_type.pluralize, "#{self.slug}#{File.extname(self.source_filename)}")
|
||||
rescue
|
||||
''
|
||||
end
|
||||
|
||||
def to_liquid
|
||||
{ :url => self.source.url }.merge(self.attributes)
|
||||
end
|
||||
|
||||
|
||||
protected
|
||||
|
||||
|
||||
def sanitize_slug
|
||||
self.slug.slugify!(:underscore => true) if self.slug.present?
|
||||
end
|
||||
|
||||
|
||||
def set_slug
|
||||
if self.slug.blank?
|
||||
self.slug = File.basename(self.source_filename, File.extname(self.source_filename))
|
||||
self.sanitize_slug
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def extname_can_not_be_changed
|
||||
return if self.new_record?
|
||||
|
||||
|
||||
if File.extname(self.source.file.original_filename) != File.extname(self.source_filename)
|
||||
self.errors.add(:source, :extname_changed)
|
||||
end
|
||||
|
@ -1,10 +1,10 @@
|
||||
- content_for :head do
|
||||
= javascript_include_tag 'admin/plugins/codemirror/codemirror'
|
||||
= javascript_include_tag 'admin/plugins/codemirror/codemirror', 'admin/layouts.js'
|
||||
= image_picker_include_tags
|
||||
|
||||
= f.inputs :name => :information do
|
||||
= f.input :name
|
||||
|
||||
|
||||
= f.inputs :name => :code do
|
||||
= f.custom_input :value, :css => 'code full', :with_label => false do
|
||||
%code{ :class => 'html' }
|
||||
|
@ -3,7 +3,7 @@
|
||||
- edit = local_assigns.key?(:edit) ? edit : true
|
||||
|
||||
%li{ :class => "#{asset.new_record? ? 'new-asset' : 'asset'} #{'last' if (asset_counter + 1) % per_row == 0}"}
|
||||
%h4= link_to truncate(asset.slug, :length => 18), edit ? edit_admin_theme_asset_path(asset) : asset.source.url
|
||||
%h4= link_to truncate(asset.slug, :length => 18), edit ? edit_admin_theme_asset_path(asset) : asset.source.url, :"data-slug" => asset.slug, :"data-shortcut-url" => asset.shortcut_url
|
||||
.image
|
||||
.inside
|
||||
= vignette_tag(asset)
|
||||
|
@ -7,6 +7,7 @@
|
||||
#file-selector{ :class => "selector #{'hidden' if @theme_asset.performing_plain_text?}" }
|
||||
= f.inputs :name => :information do
|
||||
= f.input :source
|
||||
= f.input :slug
|
||||
|
||||
- if @theme_asset.new_record? || @theme_asset.stylesheet_or_javascript?
|
||||
%span.alt
|
||||
|
@ -6,7 +6,7 @@
|
||||
- content_for :buttons do
|
||||
= admin_button_tag t('admin.theme_assets.index.new'), new_admin_theme_asset_url, :class => 'new'
|
||||
|
||||
%p!= t('.help', :url => @theme_asset.source.url)
|
||||
%p!= t('.help', :url => @theme_asset.source.url, :shortcut_url => @theme_asset.shortcut_url)
|
||||
|
||||
= semantic_form_for @theme_asset, :url => admin_theme_asset_url(@theme_asset), :html => { :multipart => true, :class => 'save-with-shortcut' } do |form|
|
||||
|
||||
|
@ -16,9 +16,9 @@ Locomotive::Application.configure do
|
||||
|
||||
# Don't care if the mailer can't send
|
||||
config.action_mailer.raise_delivery_errors = false
|
||||
|
||||
|
||||
# config.action_mailer.default_url_options = { :host => 'localhost:3000' }
|
||||
|
||||
|
||||
# MockSmtp settings
|
||||
config.action_mailer.delivery_method = :smtp
|
||||
config.action_mailer.smtp_settings = {
|
||||
|
@ -172,7 +172,7 @@ en:
|
||||
help: "You have the choice to either upload any file or to copy/paste a stylesheet or a javascript in plain text."
|
||||
edit:
|
||||
title: "Editing %{file}"
|
||||
help: "You can use it by copying/pasting the following url: %{url}"
|
||||
help: "You can insert the following shortcut url in your stylesheets: <strong>%{shortcut_url}</strong> OR use the direct url <strong>%{url}</strong>"
|
||||
form:
|
||||
picker_link: Insert a file into the code
|
||||
choose_file: Choose file
|
||||
|
@ -14,14 +14,14 @@ fr:
|
||||
edit:
|
||||
title: Changer mon mot de passe
|
||||
link: "→ Retour page de connexion"
|
||||
|
||||
|
||||
buttons:
|
||||
login: Se connecter
|
||||
send_password: Envoyer
|
||||
change_password: Changer
|
||||
|
||||
fr:
|
||||
admin:
|
||||
admin:
|
||||
buttons:
|
||||
login: Se connecter
|
||||
send_password: Envoyer
|
||||
@ -134,7 +134,7 @@ fr:
|
||||
new: nouveau gabarit
|
||||
layout:
|
||||
updated_at: Mis à jour le
|
||||
|
||||
|
||||
snippets:
|
||||
index:
|
||||
title: Liste des snippets
|
||||
@ -149,28 +149,28 @@ fr:
|
||||
help: "Remplissez le formulaire ci-dessous pour mettre à jour votre snippet."
|
||||
snippet:
|
||||
updated_at: Mis à jour le
|
||||
|
||||
|
||||
sites:
|
||||
new:
|
||||
title: "Nouveau site"
|
||||
help: "Remplissez le formulaire ci-dessous pour créer votre nouveau site."
|
||||
|
||||
|
||||
current_sites:
|
||||
edit:
|
||||
new_membership: ajouter compte
|
||||
help: "Le nom du site est modifiable en cliquant dessus."
|
||||
ask_for_name: "Veuillez entrer le nouveau nom"
|
||||
|
||||
|
||||
memberships:
|
||||
new:
|
||||
title: "Ajout d'un compte"
|
||||
help: "Donnez l'adresse email du compte à ajouter. S'il n'existe pas, vous serez redirigé(e) vers le formulaire de création d'un compte."
|
||||
|
||||
|
||||
accounts:
|
||||
new:
|
||||
title: Nouveau compte
|
||||
help: "Remplissez le formulaire ci-dessous pour ajouter un nouveau compte."
|
||||
|
||||
|
||||
my_accounts:
|
||||
edit:
|
||||
help: "Votre nom est modifiable en cliquant dessus."
|
||||
@ -178,7 +178,7 @@ fr:
|
||||
en: en Anglais
|
||||
fr: en Français
|
||||
ask_for_name: "Veuillez entrer le nouveau nom"
|
||||
|
||||
|
||||
theme_assets:
|
||||
index:
|
||||
title: Liste des fichiers du thème
|
||||
@ -194,14 +194,14 @@ fr:
|
||||
help: "Vous avez le choix de soit uploader n'importe quel fichier ou bien soit de copier/coller du code css ou javascript."
|
||||
edit:
|
||||
title: "Edition %{file}"
|
||||
help: "Vous pouvez utiliser ce fichier grâce a l'url suivante: %{url}"
|
||||
help: "Vous pouvez insérer le raccourci suivant dans vos feuilles de style: <strong>%{shortcut_url}</strong> OU utiliser directement l'url : %{url}"
|
||||
form:
|
||||
choose_file: Choisir fichier
|
||||
choose_plain_text: Passer en mode texte
|
||||
images:
|
||||
title: Liste des images
|
||||
no_items: "Il n'y a pas d'images."
|
||||
|
||||
|
||||
asset_collections:
|
||||
index:
|
||||
title: Collections
|
||||
@ -217,7 +217,7 @@ fr:
|
||||
destroy: supprimer collection
|
||||
no_items: "Il n'existe pas de médias. Vous pouvez commencer par créer un <a href='%{url}'>ici</a>."
|
||||
ask_for_name: "Veuillez entrer le nouveau nom"
|
||||
|
||||
|
||||
assets:
|
||||
new:
|
||||
title: "Nouveau média"
|
||||
@ -225,7 +225,7 @@ fr:
|
||||
edit:
|
||||
title: "Edition média"
|
||||
help: "Remplissez le formulaire ci-dessous pour mettre à jour votre média."
|
||||
|
||||
|
||||
content_types:
|
||||
index:
|
||||
new: nouveau modèle
|
||||
@ -241,27 +241,27 @@ fr:
|
||||
order_by:
|
||||
updated_at: 'Par date de mise à jour'
|
||||
position_in_list: Manuellement
|
||||
|
||||
|
||||
contents:
|
||||
index:
|
||||
title: 'Liste des "%{type}"'
|
||||
edit: éditer modèle
|
||||
destroy: supprimer modèle
|
||||
download: télécharger éléments
|
||||
new: nouvel élément
|
||||
new: nouvel élément
|
||||
category_noname: "Pas de nom"
|
||||
lastest_items: "Eléments récents"
|
||||
updated_at: "Mis à jour le"
|
||||
list:
|
||||
no_items: "Il n'existe pas d'éléments. Vous pouvez commencer par créer un <a href='%{url}'>ici</a>"
|
||||
new:
|
||||
title: '%{type} — nouvel élément'
|
||||
title: '%{type} — nouvel élément'
|
||||
edit:
|
||||
title: '%{type} — édition élément'
|
||||
|
||||
|
||||
image_picker:
|
||||
link: Insérer une image dans le code
|
||||
|
||||
|
||||
formtastic:
|
||||
titles:
|
||||
information: Informations générales
|
||||
@ -289,7 +289,7 @@ fr:
|
||||
custom_fields:
|
||||
field:
|
||||
_alias: Alias
|
||||
|
||||
|
||||
hints:
|
||||
page:
|
||||
published: "Seuls les administrateurs authentifiés peuvent voir une page non publiée."
|
||||
|
@ -1,57 +1,57 @@
|
||||
# Locomotive::Application.routes.draw do |map|
|
||||
Rails.application.routes.draw do |map|
|
||||
|
||||
|
||||
constraints(Locomotive::Routing::DefaultConstraint) do
|
||||
root :to => 'home#show'
|
||||
end
|
||||
|
||||
|
||||
# admin authentication
|
||||
devise_for :admin, :class_name => 'Account', :controllers => { :sessions => 'admin/sessions', :passwords => 'admin/passwords' }
|
||||
|
||||
|
||||
# admin interface for each website
|
||||
namespace 'admin' do
|
||||
root :to => 'pages#index'
|
||||
|
||||
resources :pages do
|
||||
|
||||
resources :pages do
|
||||
put :sort, :on => :member
|
||||
get :get_path, :on => :collection
|
||||
end
|
||||
|
||||
get :get_path, :on => :collection
|
||||
end
|
||||
|
||||
resources :layouts do
|
||||
resources :page_parts, :only => :index
|
||||
end
|
||||
resources :snippets
|
||||
|
||||
|
||||
resources :site
|
||||
|
||||
|
||||
resource :current_site
|
||||
|
||||
|
||||
resources :accounts
|
||||
|
||||
|
||||
resource :my_account
|
||||
|
||||
|
||||
resources :memberships
|
||||
|
||||
|
||||
resources :theme_assets
|
||||
|
||||
|
||||
resources :asset_collections
|
||||
|
||||
resources :assets, :path => 'asset_collections/:collection_id/assets'
|
||||
|
||||
resources :assets, :path => 'asset_collections/:collection_id/assets'
|
||||
|
||||
resources :content_types
|
||||
|
||||
|
||||
resources :contents, :path => 'content_types/:slug/contents' do
|
||||
put :sort, :on => :collection
|
||||
end
|
||||
|
||||
|
||||
resources :api_contents, :path => 'api/:slug/contents', :controller => 'api_contents', :only => [:create]
|
||||
|
||||
|
||||
resources :custom_fields, :path => 'custom/:parent/:slug/fields'
|
||||
end
|
||||
|
||||
|
||||
# sitemap
|
||||
match '/sitemap.xml' => 'admin/sitemaps#show', :format => 'xml'
|
||||
|
||||
|
||||
# magic urls
|
||||
match '/' => 'admin/rendering#show'
|
||||
match '*path' => 'admin/rendering#show'
|
||||
|
8
doc/TODO
8
doc/TODO
@ -1,13 +1,10 @@
|
||||
BOARD:
|
||||
|
||||
- refactor slugify method (use parameterize + create a module)
|
||||
- send email when new content added thru api
|
||||
|
||||
BACKLOG:
|
||||
|
||||
- rack app to map pretty asset url to S3
|
||||
- notify accounts when new instance of models (opt): none, one or many accounts. Used for contact form.
|
||||
- theme assets: disable version if not image
|
||||
- new custom field types:
|
||||
- belongs_to => association
|
||||
- cucumber features for admin pages
|
||||
@ -28,6 +25,7 @@ NICE TO HAVE:
|
||||
- page with regexp url ?
|
||||
- page redirection (option)
|
||||
- automatic update !
|
||||
- import / export site
|
||||
|
||||
DONE:
|
||||
|
||||
@ -64,4 +62,6 @@ x change action icons according to the right action [Sacha]
|
||||
x publish event when saving form in ajax (for instance, in order to update account name or site name)
|
||||
x page templatized (bound to a model)
|
||||
x theme asset picker when editing layout / snippet
|
||||
x templatized: do not display content with visible / active set to false
|
||||
x templatized: do not display content with visible / active set to false
|
||||
x theme assets: disable version if not image (handled by the new version of Carrierwave)
|
||||
x rack app to map pretty asset url to S3 => shortcut urls instead
|
19
lib/locomotive/liquid/drops/theme_images.rb
Normal file
19
lib/locomotive/liquid/drops/theme_images.rb
Normal file
@ -0,0 +1,19 @@
|
||||
module Locomotive
|
||||
module Liquid
|
||||
module Drops
|
||||
class ThemeImages < ::Liquid::Drop
|
||||
|
||||
def initialize(site)
|
||||
@site = site
|
||||
end
|
||||
|
||||
def before_method(meth)
|
||||
asset = @site.theme_assets.where(:content_type => 'image', :slug => meth.to_s).first
|
||||
!asset.nil? ? asset.source.url : nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
@ -1,40 +1,40 @@
|
||||
module Locomotive
|
||||
module Render
|
||||
|
||||
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
|
||||
module InstanceMethods
|
||||
|
||||
|
||||
protected
|
||||
|
||||
|
||||
def render_locomotive_page
|
||||
@page = locomotive_page
|
||||
|
||||
|
||||
redirect_to application_root_url and return if @page.nil?
|
||||
|
||||
|
||||
output = @page.render(locomotive_context)
|
||||
|
||||
|
||||
prepare_and_set_response(output)
|
||||
end
|
||||
|
||||
|
||||
def locomotive_page
|
||||
path = request.fullpath.clone
|
||||
path.gsub!(/\.[a-zA-Z][a-zA-Z0-9]{2,}$/, '')
|
||||
path.gsub!(/^\//, '')
|
||||
path = 'index' if path.blank?
|
||||
|
||||
|
||||
if path != 'index'
|
||||
dirname = File.dirname(path).gsub(/^\.$/, '') # also look for templatized page path
|
||||
path = [path, File.join(dirname, 'content_type_template').gsub(/^\//, '')]
|
||||
end
|
||||
|
||||
|
||||
if page = current_site.pages.any_in(:fullpath => [*path]).first
|
||||
if not page.published? and current_admin.nil?
|
||||
page = nil
|
||||
else
|
||||
if page.templatized?
|
||||
@content_instance = page.content_type.contents.where(:_slug => File.basename(path.first)).first
|
||||
|
||||
|
||||
if @content_instance.nil? || (!@content_instance.visible? && current_admin.nil?) # content instance not found or not visible
|
||||
page = nil
|
||||
end
|
||||
@ -44,7 +44,7 @@ module Locomotive
|
||||
|
||||
page || current_site.pages.not_found.published.first
|
||||
end
|
||||
|
||||
|
||||
def locomotive_context
|
||||
assigns = {
|
||||
'site' => current_site,
|
||||
@ -52,35 +52,36 @@ module Locomotive
|
||||
'asset_collections' => Locomotive::Liquid::Drops::AssetCollections.new(current_site),
|
||||
'stylesheets' => Locomotive::Liquid::Drops::Stylesheets.new(current_site),
|
||||
'javascripts' => Locomotive::Liquid::Drops::Javascripts.new(current_site),
|
||||
'theme_images' => Locomotive::Liquid::Drops::ThemeImages.new(current_site),
|
||||
'contents' => Locomotive::Liquid::Drops::Contents.new(current_site),
|
||||
'current_page' => self.params[:page]
|
||||
}
|
||||
|
||||
|
||||
if @page.templatized? # add instance from content type
|
||||
assigns['content_instance'] = @content_instance
|
||||
assigns[@page.content_type.slug.singularize] = @content_instance # just here to help to write readable liquid code
|
||||
end
|
||||
|
||||
|
||||
registers = { :controller => self, :site => current_site, :page => @page }
|
||||
|
||||
|
||||
::Liquid::Context.new(assigns, registers)
|
||||
end
|
||||
|
||||
|
||||
def prepare_and_set_response(output)
|
||||
response.headers['Content-Type'] = 'text/html; charset=utf-8'
|
||||
|
||||
if @page.with_cache?
|
||||
fresh_when :etag => @page, :last_modified => @page.updated_at.utc, :public => true
|
||||
|
||||
|
||||
if @page.cache_strategy != 'simple' # varnish
|
||||
response.cache_control[:max_age] = @page.cache_strategy
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
render :text => output, :layout => false, :status => :ok
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
@ -1,43 +1,45 @@
|
||||
module Locomotive
|
||||
module Routing
|
||||
module Routing
|
||||
module SiteDispatcher
|
||||
|
||||
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
|
||||
included do
|
||||
before_filter :fetch_site
|
||||
|
||||
helper_method :current_site
|
||||
if self.respond_to?(:before_filter)
|
||||
before_filter :fetch_site
|
||||
|
||||
helper_method :current_site
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
|
||||
|
||||
protected
|
||||
|
||||
|
||||
def fetch_site
|
||||
Locomotive.logger "[fetch site] host = #{request.host} / #{request.env['HTTP_HOST']}"
|
||||
@current_site ||= Site.match_domain(request.host).first
|
||||
end
|
||||
|
||||
|
||||
def current_site
|
||||
@current_site || fetch_site
|
||||
end
|
||||
|
||||
|
||||
def require_site
|
||||
redirect_to application_root_url and return false if current_site.nil?
|
||||
end
|
||||
|
||||
|
||||
def validate_site_membership
|
||||
return if current_site && current_site.accounts.include?(current_admin)
|
||||
redirect_to application_root_url
|
||||
end
|
||||
|
||||
|
||||
def application_root_url
|
||||
root_url(:host => Locomotive.config.default_domain, :port => request.port)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
7
public/javascripts/admin/layouts.js
Normal file
7
public/javascripts/admin/layouts.js
Normal file
@ -0,0 +1,7 @@
|
||||
$(document).ready(function() {
|
||||
$('a#image-picker-link').imagepicker({
|
||||
insertFn: function(link) {
|
||||
return "{{ theme_images." + link.attr('data-slug') + " }}";
|
||||
}
|
||||
});
|
||||
});
|
@ -1,69 +1,81 @@
|
||||
$(document).ready(function() {
|
||||
|
||||
var copyLinkToEditor = function(link, event) {
|
||||
var editor = CodeMirrorEditors[0].editor;
|
||||
var handle = editor.cursorLine(), position = editor.cursorPosition(handle).character;
|
||||
$.fn.imagepicker = function(options) {
|
||||
|
||||
editor.insertIntoLine(handle, position, link.attr('href'));
|
||||
var defaults = {
|
||||
insertFn: null
|
||||
};
|
||||
var options = $.extend(defaults, options);
|
||||
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
var copyLinkToEditor = function(link, event) {
|
||||
var editor = CodeMirrorEditors[0].editor;
|
||||
var handle = editor.cursorLine(), position = editor.cursorPosition(handle).character;
|
||||
|
||||
$.fancybox.close();
|
||||
}
|
||||
var value = options.insertFn != null ? options.insertFn(link) : link.attr('href');
|
||||
|
||||
var setupUploader = function() {
|
||||
var multipartParams = {};
|
||||
multipartParams[$('meta[name=csrf-param]').attr('content')] = $('meta[name=csrf-token]').attr('content');
|
||||
editor.insertIntoLine(handle, position, value);
|
||||
|
||||
var uploader = new plupload.Uploader({
|
||||
runtimes : (jQuery.browser.webkit == true ? 'flash' : 'html5,flash'),
|
||||
container: 'theme-images',
|
||||
browse_button : 'upload-link',
|
||||
max_file_size : '5mb',
|
||||
url : $('a#upload-link').attr('href'),
|
||||
flash_swf_url : '/javascripts/admin/plugins/plupload/plupload.flash.swf',
|
||||
multipart: true,
|
||||
multipart_params: multipartParams
|
||||
});
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
uploader.bind('QueueChanged', function() {
|
||||
uploader.start();
|
||||
});
|
||||
$.fancybox.close();
|
||||
}
|
||||
|
||||
uploader.bind('FileUploaded', function(up, file, response) {
|
||||
var json = JSON.parse(response.response);
|
||||
var setupUploader = function() {
|
||||
var multipartParams = {};
|
||||
multipartParams[$('meta[name=csrf-param]').attr('content')] = $('meta[name=csrf-token]').attr('content');
|
||||
|
||||
if (json.status == 'success') {
|
||||
var asset = $('.asset-picker ul li.new-asset')
|
||||
.clone()
|
||||
.insertBefore($('.asset-picker ul li.clear'))
|
||||
.addClass('asset');
|
||||
var uploader = new plupload.Uploader({
|
||||
runtimes : (jQuery.browser.webkit == true ? 'flash' : 'html5,flash'),
|
||||
container: 'theme-images',
|
||||
browse_button : 'upload-link',
|
||||
max_file_size : '5mb',
|
||||
url : $('a#upload-link').attr('href'),
|
||||
flash_swf_url : '/javascripts/admin/plugins/plupload/plupload.flash.swf',
|
||||
multipart: true,
|
||||
multipart_params: multipartParams
|
||||
});
|
||||
|
||||
asset.find('h4 a').attr('href', json.url).html(json.name).bind('click', function(e) {
|
||||
copyLinkToEditor($(this), e);
|
||||
});
|
||||
asset.find('.image .inside img').attr('src', json.vignette_url);
|
||||
uploader.bind('QueueChanged', function() {
|
||||
uploader.start();
|
||||
});
|
||||
|
||||
if ($('.asset-picker ul li.asset').length % 3 == 0)
|
||||
asset.addClass('last');
|
||||
uploader.bind('FileUploaded', function(up, file, response) {
|
||||
var json = JSON.parse(response.response);
|
||||
|
||||
asset.removeClass('new-asset');
|
||||
if (json.status == 'success') {
|
||||
var asset = $('.asset-picker ul li.new-asset')
|
||||
.clone()
|
||||
.insertBefore($('.asset-picker ul li.clear'))
|
||||
.addClass('asset');
|
||||
|
||||
$('.asset-picker p.no-items').hide();
|
||||
asset.find('h4 a').attr('href', json.url)
|
||||
.attr('data-slug', json.slug)
|
||||
.attr('data-shortcut-url', json.shortcut_url)
|
||||
.html(json.name).bind('click', function(e) {
|
||||
copyLinkToEditor($(this), e);
|
||||
});
|
||||
asset.find('.image .inside img').attr('src', json.vignette_url);
|
||||
|
||||
$('.asset-picker ul').scrollTo($('li.asset:last'), 400);
|
||||
}
|
||||
});
|
||||
if ($('.asset-picker ul li.asset').length % 3 == 0)
|
||||
asset.addClass('last');
|
||||
|
||||
uploader.init();
|
||||
}
|
||||
asset.removeClass('new-asset');
|
||||
|
||||
$('a#image-picker-link').fancybox({
|
||||
'onComplete': function() {
|
||||
setupUploader();
|
||||
|
||||
$('ul.assets h4 a').bind('click', function(e) { copyLinkToEditor($(this), e); });
|
||||
}
|
||||
});
|
||||
});
|
||||
$('.asset-picker p.no-items').hide();
|
||||
|
||||
$('.asset-picker ul').scrollTo($('li.asset:last'), 400);
|
||||
}
|
||||
});
|
||||
|
||||
uploader.init();
|
||||
}
|
||||
|
||||
return this.each(function() {
|
||||
$(this).fancybox({
|
||||
'onComplete': function() {
|
||||
setupUploader();
|
||||
|
||||
$('ul.assets h4 a').bind('click', function(e) { copyLinkToEditor($(this), e); });
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
@ -1,16 +1,22 @@
|
||||
$(document).ready(function() {
|
||||
|
||||
// automatic slug from snippet name
|
||||
$('#snippet_name').keypress(function() {
|
||||
var input = $(this);
|
||||
var slug = $('#snippet_slug');
|
||||
|
||||
if (!slug.hasClass('filled')) {
|
||||
setTimeout(function() {
|
||||
slug.val(makeSlug(input.val()));
|
||||
}, 50);
|
||||
}
|
||||
});
|
||||
|
||||
$('#snippet_slug').keypress(function() { $(this).addClass('filled'); });
|
||||
|
||||
// automatic slug from snippet name
|
||||
$('#snippet_name').keypress(function() {
|
||||
var input = $(this);
|
||||
var slug = $('#snippet_slug');
|
||||
|
||||
if (!slug.hasClass('filled')) {
|
||||
setTimeout(function() {
|
||||
slug.val(makeSlug(input.val()));
|
||||
}, 50);
|
||||
}
|
||||
});
|
||||
|
||||
$('#snippet_slug').keypress(function() { $(this).addClass('filled'); });
|
||||
|
||||
$('a#image-picker-link').imagepicker({
|
||||
insertFn: function(link) {
|
||||
return "{{ theme_images." + link.attr('data-slug') + " }}";
|
||||
}
|
||||
});
|
||||
});
|
@ -1,38 +1,43 @@
|
||||
/* ___ file or text ___ */
|
||||
|
||||
var enableFileOrTextToggling = function() {
|
||||
$('div.hidden').hide();
|
||||
|
||||
$('span.alt').click(function(event) {
|
||||
event.preventDefault();
|
||||
$('div.hidden').hide();
|
||||
|
||||
if ($("div#file-selector").is(":hidden")) {
|
||||
$("div#text-selector").slideUp("normal", function() {
|
||||
$("div#file-selector").slideDown();
|
||||
$("input#theme_asset_performing_plain_text").val(false);
|
||||
});
|
||||
$('span.alt').click(function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
if ($("div#file-selector").is(":hidden")) {
|
||||
$("div#text-selector").slideUp("normal", function() {
|
||||
$("div#file-selector").slideDown();
|
||||
$("input#theme_asset_performing_plain_text").val(false);
|
||||
});
|
||||
} else {
|
||||
$("div#file-selector").slideUp("normal", function() {
|
||||
$("div#text-selector").slideDown();
|
||||
$("input#theme_asset_performing_plain_text").val(true);
|
||||
});
|
||||
$("div#file-selector").slideUp("normal", function() {
|
||||
$("div#text-selector").slideDown();
|
||||
$("input#theme_asset_performing_plain_text").val(true);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
enableFileOrTextToggling();
|
||||
|
||||
$('code.stylesheet textarea').each(function() {
|
||||
addCodeMirrorEditor(null, $(this), ["tokenizejavascript.js", "parsejavascript.js", "parsecss.js"]);
|
||||
});
|
||||
$('code.javascript textarea').each(function() {
|
||||
addCodeMirrorEditor(null, $(this), ["parsecss.js", "tokenizejavascript.js", "parsejavascript.js"]);
|
||||
});
|
||||
|
||||
$('select#theme_asset_content_type').bind('change', function() {
|
||||
var editor = CodeMirrorEditors[0].editor;
|
||||
editor.setParser($(this).val() == 'stylesheet' ? 'CSSParser' : 'JSParser');
|
||||
});
|
||||
|
||||
enableFileOrTextToggling();
|
||||
|
||||
$('code.stylesheet textarea').each(function() {
|
||||
addCodeMirrorEditor(null, $(this), ["tokenizejavascript.js", "parsejavascript.js", "parsecss.js"]);
|
||||
});
|
||||
$('code.javascript textarea').each(function() {
|
||||
addCodeMirrorEditor(null, $(this), ["parsecss.js", "tokenizejavascript.js", "parsejavascript.js"]);
|
||||
});
|
||||
|
||||
$('select#theme_asset_content_type').bind('change', function() {
|
||||
var editor = CodeMirrorEditors[0].editor;
|
||||
editor.setParser($(this).val() == 'stylesheet' ? 'CSSParser' : 'JSParser');
|
||||
});
|
||||
|
||||
$('a#image-picker-link').imagepicker({
|
||||
insertFn: function(link) {
|
||||
return link.attr('data-shortcut-url');
|
||||
}
|
||||
});
|
||||
});
|
@ -1,74 +1,74 @@
|
||||
/* ___ application messages ___ */
|
||||
|
||||
div.notice {
|
||||
background: transparent url(/images/admin/form/growl-notice.png) repeat-x 0 0;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 90px;
|
||||
background: transparent url(/images/admin/form/growl-notice.png) repeat-x 0 0;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 90px;
|
||||
}
|
||||
|
||||
div.notice.error, div.notice.alert {
|
||||
background-image: url(/images/admin/form/growl-error.png);
|
||||
background-image: url(/images/admin/form/growl-error.png);
|
||||
}
|
||||
|
||||
div.notice p {
|
||||
position: relative;
|
||||
top: 35px;
|
||||
margin: 0px;
|
||||
text-align: center;
|
||||
font-size: 1.5em;
|
||||
text-shadow: 1px 1px 1px #333;
|
||||
color: #fff;
|
||||
position: relative;
|
||||
top: 35px;
|
||||
margin: 0px;
|
||||
text-align: center;
|
||||
font-size: 1.5em;
|
||||
text-shadow: 1px 1px 1px #333;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* ___ list ___ */
|
||||
|
||||
p.no-items {
|
||||
padding: 15px 0px;
|
||||
background: transparent url(/images/admin/list/none.png) no-repeat 0 0;
|
||||
text-align: center;
|
||||
color: #9d8963 !important;
|
||||
font-size: 1.1em !important;
|
||||
padding: 15px 0px;
|
||||
background: transparent url(/images/admin/list/none.png) no-repeat 0 0;
|
||||
text-align: center;
|
||||
color: #9d8963 !important;
|
||||
font-size: 1.1em !important;
|
||||
}
|
||||
|
||||
p.no-items a {
|
||||
color: #ff2900;
|
||||
text-decoration: none;
|
||||
p.no-items a {
|
||||
color: #ff2900;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
p.no-items a:hover {
|
||||
text-decoration: underline;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
ul.list {
|
||||
list-style: none;
|
||||
margin: 0px 0 20px 0;
|
||||
background: white;
|
||||
list-style: none;
|
||||
margin: 0px 0 20px 0;
|
||||
background: white;
|
||||
}
|
||||
|
||||
ul.list li {
|
||||
height: 31px;
|
||||
margin-bottom: 10px;
|
||||
position: relative;
|
||||
clear: both;
|
||||
background: transparent url(/images/admin/list/item.png) no-repeat 0 0;
|
||||
height: 31px;
|
||||
margin-bottom: 10px;
|
||||
position: relative;
|
||||
clear: both;
|
||||
background: transparent url(/images/admin/list/item.png) no-repeat 0 0;
|
||||
}
|
||||
|
||||
ul.list li em {
|
||||
display: block;
|
||||
float: left;
|
||||
background: transparent url(/images/admin/list/item-left.png) no-repeat left 0;
|
||||
height: 31px;
|
||||
width: 18px;
|
||||
display: block;
|
||||
float: left;
|
||||
background: transparent url(/images/admin/list/item-left.png) no-repeat left 0;
|
||||
height: 31px;
|
||||
width: 18px;
|
||||
}
|
||||
|
||||
ul.list li strong a {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
left: 15px;
|
||||
text-decoration: none;
|
||||
color: #1f82bc;
|
||||
font-size: 0.9em;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
left: 15px;
|
||||
text-decoration: none;
|
||||
color: #1f82bc;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
ul.list.sortable li strong a { left: 10px; }
|
||||
@ -76,81 +76,81 @@ ul.list.sortable li strong a { left: 10px; }
|
||||
ul.list li strong a:hover { text-decoration: underline; }
|
||||
|
||||
ul.list li div.more {
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
right: 15px;
|
||||
font-size: 0.7em;
|
||||
color: #8b8d9a;
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
right: 15px;
|
||||
font-size: 0.7em;
|
||||
color: #8b8d9a;
|
||||
}
|
||||
|
||||
ul.list li div.more a {
|
||||
margin-left: 10px;
|
||||
position: relative;
|
||||
top: 4px;
|
||||
margin-left: 10px;
|
||||
position: relative;
|
||||
top: 4px;
|
||||
}
|
||||
|
||||
ul.list li span.handle {
|
||||
position: relative;
|
||||
top: 5px;
|
||||
margin: 0 0 0 15px;
|
||||
cursor: move;
|
||||
position: relative;
|
||||
top: 5px;
|
||||
margin: 0 0 0 15px;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
/* ___ assets ___ */
|
||||
|
||||
ul.assets {
|
||||
list-style: none;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
list-style: none;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
ul.assets li.asset {
|
||||
position: relative;
|
||||
float: left;
|
||||
width: 139px;
|
||||
height: 140px;
|
||||
background: transparent url(/images/admin/list/thumb.png) no-repeat 0 0;
|
||||
margin: 0 17px 17px 0;
|
||||
position: relative;
|
||||
float: left;
|
||||
width: 139px;
|
||||
height: 140px;
|
||||
background: transparent url(/images/admin/list/thumb.png) no-repeat 0 0;
|
||||
margin: 0 17px 17px 0;
|
||||
}
|
||||
|
||||
ul.assets li.asset.last {
|
||||
margin-right: 0px;
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
ul.assets li.asset h4 { margin: 0px; height: 30px; border-bottom: 1px solid #ccced7; }
|
||||
ul.assets li.asset h4 { margin: 0px; height: 30px; border-bottom: 1px solid #ccced7; position: relative; }
|
||||
|
||||
ul.assets li.asset h4 a {
|
||||
position: relative;
|
||||
top: 6px;
|
||||
left: 12px;
|
||||
font-weight: bold;
|
||||
font-size: 0.6em;
|
||||
color: #1f82bc;
|
||||
text-decoration: none;
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
left: 12px;
|
||||
font-weight: bold;
|
||||
font-size: 0.6em;
|
||||
color: #1f82bc;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
ul.assets li.asset h4 a:hover { text-decoration: underline; }
|
||||
|
||||
ul.assets li.asset div.image {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border: 4px solid #fff;
|
||||
margin: 10px 0 0 24px;
|
||||
background: transparent url(/images/admin/list/empty.png) repeat 0 0;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border: 4px solid #fff;
|
||||
margin: 10px 0 0 24px;
|
||||
background: transparent url(/images/admin/list/empty.png) repeat 0 0;
|
||||
}
|
||||
|
||||
ul.assets li.asset div.image div.inside {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
ul.assets li.asset div.actions {
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
right: 12px;
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 12px;
|
||||
}
|
||||
|
||||
/* ___ asset collections ___ */
|
||||
@ -164,73 +164,73 @@ div#uploadAssetsInputQueue { display: none; }
|
||||
#contents-list li { background: none; }
|
||||
|
||||
#contents-list.sortable li em {
|
||||
background-position: left -31px;
|
||||
cursor: move;
|
||||
background-position: left -31px;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
#contents-list li strong {
|
||||
margin-left: 18px;
|
||||
display: block;
|
||||
height: 31px;
|
||||
background: transparent url(/images/admin/list/item-right.png) no-repeat right 0;
|
||||
margin-left: 18px;
|
||||
display: block;
|
||||
height: 31px;
|
||||
background: transparent url(/images/admin/list/item-right.png) no-repeat right 0;
|
||||
}
|
||||
|
||||
|
||||
/* ___ pages ___ */
|
||||
|
||||
#pages-list {
|
||||
list-style: none;
|
||||
margin: 0px 0 20px 0;
|
||||
background: white;
|
||||
list-style: none;
|
||||
margin: 0px 0 20px 0;
|
||||
background: white;
|
||||
}
|
||||
|
||||
#pages-list ul { list-style: none; margin: 10px 0 10px 40px; padding: 0; }
|
||||
|
||||
#pages-list li {
|
||||
margin-bottom: 10px;
|
||||
position: relative;
|
||||
clear: both;
|
||||
margin-bottom: 10px;
|
||||
position: relative;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
#pages-list li em {
|
||||
display: block;
|
||||
float: left;
|
||||
background: transparent url(/images/admin/list/item-left.png) no-repeat left 0;
|
||||
height: 31px;
|
||||
width: 18px;
|
||||
display: block;
|
||||
float: left;
|
||||
background: transparent url(/images/admin/list/item-left.png) no-repeat left 0;
|
||||
height: 31px;
|
||||
width: 18px;
|
||||
}
|
||||
|
||||
#pages-list ul.folder li em {
|
||||
background-position: left -31px;
|
||||
cursor: move;
|
||||
background-position: left -31px;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
#pages-list ul.folder li.templatized em {
|
||||
background-position: left -62px;
|
||||
cursor: pointer;
|
||||
background-position: left -62px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#pages-list li .toggler {
|
||||
position: absolute;
|
||||
top: 9px;
|
||||
left: -15px;
|
||||
cursor: pointer;
|
||||
#pages-list li .toggler {
|
||||
position: absolute;
|
||||
top: 9px;
|
||||
left: -15px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#pages-list li strong {
|
||||
margin-left: 18px;
|
||||
display: block;
|
||||
height: 31px;
|
||||
background: transparent url(/images/admin/list/item-right.png) no-repeat right 0;
|
||||
margin-left: 18px;
|
||||
display: block;
|
||||
height: 31px;
|
||||
background: transparent url(/images/admin/list/item-right.png) no-repeat right 0;
|
||||
}
|
||||
|
||||
#pages-list li strong a {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
text-decoration: none;
|
||||
color: #1f82bc;
|
||||
font-size: 0.9em;
|
||||
padding-left: 6px;
|
||||
position: relative;
|
||||
top: 3px;
|
||||
text-decoration: none;
|
||||
color: #1f82bc;
|
||||
font-size: 0.9em;
|
||||
padding-left: 6px;
|
||||
}
|
||||
|
||||
#pages-list li strong a:hover { text-decoration: underline; }
|
||||
@ -238,18 +238,18 @@ div#uploadAssetsInputQueue { display: none; }
|
||||
#pages-list li.hidden strong a { font-style: italic; font-weight: normal; }
|
||||
|
||||
#pages-list li .more {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: 20px;
|
||||
font-size: 0.7em;
|
||||
color: #8b8d9a;
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: 20px;
|
||||
font-size: 0.7em;
|
||||
color: #8b8d9a;
|
||||
}
|
||||
|
||||
#pages-list li .more a {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
margin-left: 10px;
|
||||
outline: none;
|
||||
position: relative;
|
||||
top: 3px;
|
||||
margin-left: 10px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#pages-list li.not-found { border-top: 1px dotted #bbbbbd; padding-top: 10px; margin-left: 0px; }
|
||||
|
@ -64,7 +64,7 @@ describe ThemeAsset do
|
||||
|
||||
before(:each) do
|
||||
ThemeAsset.any_instance.stubs(:site_id).returns('test')
|
||||
@asset = Factory.build(:theme_asset)
|
||||
@asset = Factory.build(:theme_asset, :site => Factory.build(:site))
|
||||
@asset.performing_plain_text = true
|
||||
@asset.slug = 'a file'
|
||||
@asset.plain_text = "Lorem ipsum"
|
||||
@ -84,6 +84,24 @@ describe ThemeAsset do
|
||||
@asset.source.should_not be_nil
|
||||
end
|
||||
|
||||
context 'shortcut urls' do
|
||||
|
||||
before(:each) do
|
||||
@image = Factory.build(:theme_asset, :source => FixturedAsset.open('5k.png'))
|
||||
@image.source.stubs(:url).returns('5k.png')
|
||||
@asset.stubs(:stylesheet?).returns(true)
|
||||
@asset.site.theme_assets.stubs(:where).returns([@image])
|
||||
@asset.plain_text = 'body { background-image: url("/theme/images/5k.png"); } h1 { background-image: url("/images/5k.png"); }'
|
||||
@asset.store_plain_text
|
||||
end
|
||||
|
||||
it 'replaces shortcut url if present' do
|
||||
@asset.plain_text.should == 'body { background-image: url("/theme/images/5k.png"); } h1 { background-image: url("/images/5k.png"); }'
|
||||
@asset.source.read.should == 'body { background-image: url("5k.png"); } h1 { background-image: url("/images/5k.png"); }'
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue
Block a user