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