create a middleware for serving fonts (solve cross domain issue) + add cache for consume liquid tag + fix image picker
This commit is contained in:
parent
86ac74e290
commit
54063d6b46
@ -4,6 +4,7 @@ module Admin
|
|||||||
include ActionView::Helpers::SanitizeHelper
|
include ActionView::Helpers::SanitizeHelper
|
||||||
extend ActionView::Helpers::SanitizeHelper::ClassMethods
|
extend ActionView::Helpers::SanitizeHelper::ClassMethods
|
||||||
include ActionView::Helpers::TextHelper
|
include ActionView::Helpers::TextHelper
|
||||||
|
include ActionView::Helpers::NumberHelper
|
||||||
|
|
||||||
sections 'settings', 'theme_assets'
|
sections 'settings', 'theme_assets'
|
||||||
|
|
||||||
@ -15,6 +16,7 @@ module Admin
|
|||||||
@js_and_css_assets = (@assets[:javascripts] || []) + (@assets[:stylesheets] || [])
|
@js_and_css_assets = (@assets[:javascripts] || []) + (@assets[:stylesheets] || [])
|
||||||
|
|
||||||
if request.xhr?
|
if request.xhr?
|
||||||
|
@images = @assets[:images]
|
||||||
render :action => 'images', :layout => false and return
|
render :action => 'images', :layout => false and return
|
||||||
else
|
else
|
||||||
@snippets = current_site.snippets.order_by([[:name, :asc]]).all.to_a
|
@snippets = current_site.snippets.order_by([[:name, :asc]]).all.to_a
|
||||||
@ -33,9 +35,10 @@ module Admin
|
|||||||
success.json do
|
success.json do
|
||||||
render :json => {
|
render :json => {
|
||||||
:status => 'success',
|
:status => 'success',
|
||||||
:name => truncate(@theme_asset.slug, :length => 22),
|
:url => @theme_asset.source.url,
|
||||||
:slug => @theme_asset.slug,
|
:local_path => @theme_asset.local_path(true),
|
||||||
:url => @theme_asset.source.url
|
:size => number_to_human_size(@theme_asset.size),
|
||||||
|
:date => l(@theme_asset.updated_at, :format => :short)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
failure.json { render :json => { :status => 'error' } }
|
failure.json { render :json => { :status => 'error' } }
|
||||||
|
@ -49,6 +49,14 @@ class ThemeAsset
|
|||||||
self.stylesheet? || self.javascript?
|
self.stylesheet? || self.javascript?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def local_path(short = false)
|
||||||
|
if short
|
||||||
|
self.read_attribute(:local_path).gsub(/^#{self.content_type.pluralize}\//, '')
|
||||||
|
else
|
||||||
|
self.read_attribute(:local_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def plain_text_name
|
def plain_text_name
|
||||||
if not @plain_text_name_changed
|
if not @plain_text_name_changed
|
||||||
@plain_text_name ||= self.safe_source_filename
|
@plain_text_name ||= self.safe_source_filename
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
%li{ :class => "#{'hidden' if asset.hidden?}" }
|
- edit = local_assigns.key?(:edit) ? edit : true
|
||||||
|
|
||||||
|
%li{ :class => "#{asset.new_record? ? 'new-asset' : 'asset'} #{'hidden' if asset.hidden?}" }
|
||||||
%em
|
%em
|
||||||
%strong= link_to asset.local_path, edit_admin_theme_asset_path(asset)
|
%strong= link_to asset.local_path(!edit), edit ? edit_admin_theme_asset_path(asset) : asset.source.url, :'data-local-path' => asset.local_path
|
||||||
.more
|
.more
|
||||||
%span.size= number_to_human_size(asset.size)
|
%span.size= number_to_human_size(asset.size)
|
||||||
—
|
—
|
||||||
%span!= t('.updated_at')
|
%span!= t('.updated_at')
|
||||||
= l asset.updated_at, :format => :short
|
%span.date= l asset.updated_at, :format => :short
|
||||||
|
|
||||||
= link_to image_tag('admin/list/icons/trash.png'), admin_theme_asset_path(asset), :class => 'remove', :confirm => t('admin.messages.confirm'), :method => :delete
|
- if edit
|
||||||
|
= link_to image_tag('admin/list/icons/trash.png'), admin_theme_asset_path(asset), :class => 'remove', :confirm => t('admin.messages.confirm'), :method => :delete
|
||||||
|
@ -4,12 +4,12 @@
|
|||||||
.actions
|
.actions
|
||||||
= admin_button_tag t('admin.theme_assets.index.new'), admin_theme_assets_url(:json), :class => 'button small add', :id => 'upload-link'
|
= admin_button_tag t('admin.theme_assets.index.new'), admin_theme_assets_url(:json), :class => 'button small add', :id => 'upload-link'
|
||||||
|
|
||||||
- if @image_assets.empty?
|
- if @images.empty?
|
||||||
%p.no-items!= t('.no_items')
|
%p.no-items!= t('.no_items')
|
||||||
|
|
||||||
%ul.assets
|
%ul.list.theme-assets
|
||||||
= render 'asset', :asset => current_site.theme_assets.build, :edit => false
|
= render 'asset', :asset => current_site.theme_assets.build(:updated_at => Time.now, :local_path => 'images/new.jpg', :content_type => 'image'), :edit => false
|
||||||
|
|
||||||
= render :partial => 'asset', :collection => @image_assets, :locals => { :per_row => 3, :edit => false }
|
= render :partial => 'asset', :collection => @images, :locals => { :edit => false }
|
||||||
|
|
||||||
%li.clear
|
%li.clear
|
@ -46,5 +46,8 @@ module Locomotive
|
|||||||
|
|
||||||
# Configure sensitive parameters which will be filtered from the log file.
|
# Configure sensitive parameters which will be filtered from the log file.
|
||||||
config.filter_parameters << :password
|
config.filter_parameters << :password
|
||||||
|
|
||||||
|
config.middleware.insert_after ::ActionDispatch::Static, '::Locomotive::Middlewares::Fonts', :path => %r{^/fonts}
|
||||||
|
# config.middleware.insert_after 'Rack::Lock', 'Dragonfly::Middleware'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -4,14 +4,14 @@ Locomotive::Application.configure do
|
|||||||
# In the development environment your application's code is reloaded on
|
# In the development environment your application's code is reloaded on
|
||||||
# every request. This slows down response time but is perfect for development
|
# every request. This slows down response time but is perfect for development
|
||||||
# since you don't have to restart the webserver when you make code changes.
|
# since you don't have to restart the webserver when you make code changes.
|
||||||
config.cache_classes = false
|
config.cache_classes = true # false
|
||||||
|
|
||||||
# Log error messages when you accidentally call methods on nil.
|
# Log error messages when you accidentally call methods on nil.
|
||||||
config.whiny_nils = true
|
config.whiny_nils = true
|
||||||
|
|
||||||
# Show full error reports and disable caching
|
# Show full error reports and disable caching
|
||||||
config.consider_all_requests_local = true
|
config.consider_all_requests_local = true
|
||||||
config.action_controller.perform_caching = false
|
config.action_controller.perform_caching = true #false
|
||||||
|
|
||||||
# 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
|
||||||
|
3
doc/TODO
3
doc/TODO
@ -38,7 +38,8 @@ x snippet dependencies => do not work correctly
|
|||||||
x mask internal asset_collections
|
x mask internal asset_collections
|
||||||
x refactor ui for the theme assets page
|
x refactor ui for the theme assets page
|
||||||
x fix assets liquid tags / filters
|
x fix assets liquid tags / filters
|
||||||
- proxy for fonts
|
x upload and insert new images in a css or js from the ui is broken
|
||||||
|
x proxy for fonts (http://markevans.github.com/dragonfly/file.Rails3.html)
|
||||||
- fix tests
|
- fix tests
|
||||||
- order yaml file (http://www.ruby-forum.com/topic/120295)
|
- order yaml file (http://www.ruby-forum.com/topic/120295)
|
||||||
- global regions: keyword in editable element (http://www.mongodb.org/display/DOCS/Updating)
|
- global regions: keyword in editable element (http://www.mongodb.org/display/DOCS/Updating)
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
# require 'locomotive/patches'
|
|
||||||
require 'locomotive/configuration'
|
require 'locomotive/configuration'
|
||||||
require 'locomotive/logger'
|
require 'locomotive/logger'
|
||||||
require 'locomotive/liquid'
|
require 'locomotive/liquid'
|
||||||
@ -14,6 +13,7 @@ require 'locomotive/regexps'
|
|||||||
require 'locomotive/render'
|
require 'locomotive/render'
|
||||||
require 'locomotive/import'
|
require 'locomotive/import'
|
||||||
require 'locomotive/delayed_job'
|
require 'locomotive/delayed_job'
|
||||||
|
require 'locomotive/middlewares'
|
||||||
|
|
||||||
require 'mongo_session_store/mongoid'
|
require 'mongo_session_store/mongoid'
|
||||||
|
|
||||||
|
@ -9,13 +9,17 @@ module Locomotive
|
|||||||
return '' if input.nil?
|
return '' if input.nil?
|
||||||
|
|
||||||
unless input =~ /^(\/|http:)/
|
unless input =~ /^(\/|http:)/
|
||||||
stylesheet = ThemeAsset.new(:site => @context.registers[:site], :folder => 'stylesheets')
|
segments = "stylesheets/#{input}".split('/')
|
||||||
input = '/' + ThemeAssetUploader.new(stylesheet).store_path(input)
|
|
||||||
|
filename, folder = segments.pop, segments.join('/')
|
||||||
|
|
||||||
|
stylesheet = ThemeAsset.new(:site => @context.registers[:site], :folder => folder)
|
||||||
|
|
||||||
|
input = '/' + ThemeAssetUploader.new(stylesheet).store_path(filename)
|
||||||
end
|
end
|
||||||
|
|
||||||
# puts "stylesheet_tag context ? #{@context}"
|
|
||||||
|
|
||||||
input = "#{input}.css" unless input.ends_with?('.css')
|
input = "#{input}.css" unless input.ends_with?('.css')
|
||||||
|
|
||||||
%{<link href="#{input}" media="screen" rel="stylesheet" type="text/css" />}
|
%{<link href="#{input}" media="screen" rel="stylesheet" type="text/css" />}
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -25,20 +29,39 @@ module Locomotive
|
|||||||
return '' if input.nil?
|
return '' if input.nil?
|
||||||
|
|
||||||
unless input =~ /^(\/|http:)/
|
unless input =~ /^(\/|http:)/
|
||||||
javascript = ThemeAsset.new(:site => @context.registers[:site], :folder => 'javascripts')
|
segments = "javascripts/#{input}".split('/')
|
||||||
input = '/' + ThemeAssetUploader.new(javascript).store_path(input)
|
|
||||||
|
filename, folder = segments.pop, segments.join('/')
|
||||||
|
|
||||||
|
javascript = ThemeAsset.new(:site => @context.registers[:site], :folder => folder)
|
||||||
|
|
||||||
|
input = '/' + ThemeAssetUploader.new(javascript).store_path(filename)
|
||||||
end
|
end
|
||||||
|
|
||||||
input = "#{input}.js" unless input.ends_with?('.js')
|
input = "#{input}.js" unless input.ends_with?('.js')
|
||||||
|
|
||||||
%{<script src="#{input}" type="text/javascript"></script>}
|
%{<script src="#{input}" type="text/javascript"></script>}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def theme_image_url(input)
|
||||||
|
return '' if input.nil?
|
||||||
|
|
||||||
|
input = "images/#{input}" unless input.starts_with?('/')
|
||||||
|
|
||||||
|
segments = input.split('/')
|
||||||
|
|
||||||
|
filename, folder = segments.pop, segments.join('/')
|
||||||
|
|
||||||
|
image = ThemeAsset.new(:site => @context.registers[:site], :folder => folder)
|
||||||
|
|
||||||
|
'/' + ThemeAssetUploader.new(image).store_path(filename)
|
||||||
|
end
|
||||||
|
|
||||||
# Write an image tag
|
# Write an image tag
|
||||||
# input: url of the image OR asset drop
|
# input: url of the image OR asset drop
|
||||||
def image_tag(input, *args)
|
def image_tag(input, *args)
|
||||||
image_options = inline_options(args_to_options(args))
|
image_options = inline_options(args_to_options(args))
|
||||||
"<img src=\"#{File.join('/', get_path_from_asset(input))}\" #{image_options}/>"
|
"<img src=\"#{File.join('/', get_url_from_asset(input))}\" #{image_options}/>"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Embed a flash movie into a page
|
# Embed a flash movie into a page
|
||||||
@ -46,7 +69,7 @@ module Locomotive
|
|||||||
# width: width (in pixel or in %) of the embedded movie
|
# width: width (in pixel or in %) of the embedded movie
|
||||||
# height: height (in pixel or in %) of the embedded movie
|
# height: height (in pixel or in %) of the embedded movie
|
||||||
def flash_tag(input, *args)
|
def flash_tag(input, *args)
|
||||||
path = get_path_from_asset(input)
|
path = get_url_from_asset(input)
|
||||||
embed_options = inline_options(args_to_options(args))
|
embed_options = inline_options(args_to_options(args))
|
||||||
%{
|
%{
|
||||||
<object #{embed_options}>
|
<object #{embed_options}>
|
||||||
@ -114,9 +137,9 @@ module Locomotive
|
|||||||
(options.stringify_keys.to_a.collect { |a, b| "#{a}=\"#{b}\"" }).join(' ') << ' '
|
(options.stringify_keys.to_a.collect { |a, b| "#{a}=\"#{b}\"" }).join(' ') << ' '
|
||||||
end
|
end
|
||||||
|
|
||||||
# Get the path to be used in html tags such as image_tag, flash_tag, ...etc
|
# Get the url to be used in html tags such as image_tag, flash_tag, ...etc
|
||||||
# input: url (String) OR asset drop
|
# input: url (String) OR asset drop
|
||||||
def get_path_from_asset(input)
|
def get_url_from_asset(input)
|
||||||
input.respond_to?(:url) ? input.url : input
|
input.respond_to?(:url) ? input.url : input
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -5,7 +5,7 @@ module Locomotive
|
|||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
#
|
#
|
||||||
# {% consume blog from 'http://nocoffee.tumblr.com/api/read.json?num=3' username: 'john', password: 'easy', format: 'json' %}
|
# {% consume blog from 'http://nocoffee.tumblr.com/api/read.json?num=3' username: 'john', password: 'easy', format: 'json', expires_in: 3000 %}
|
||||||
# {% for post in blog.posts %}
|
# {% for post in blog.posts %}
|
||||||
# {{ post.title }}
|
# {{ post.title }}
|
||||||
# {% endfor %}
|
# {% endfor %}
|
||||||
@ -23,6 +23,8 @@ module Locomotive
|
|||||||
markup.scan(::Liquid::TagAttributes) do |key, value|
|
markup.scan(::Liquid::TagAttributes) do |key, value|
|
||||||
@options[key] = value if key != 'http'
|
@options[key] = value if key != 'http'
|
||||||
end
|
end
|
||||||
|
@expires_in = (@options.delete('expires_in') || 0).to_i
|
||||||
|
@cache_key = Digest::SHA1.hexdigest(@target)
|
||||||
else
|
else
|
||||||
raise ::Liquid::SyntaxError.new("Syntax Error in 'consume' - Valid syntax: consume <var> from \"<url>\" [username: value, password: value]")
|
raise ::Liquid::SyntaxError.new("Syntax Error in 'consume' - Valid syntax: consume <var> from \"<url>\" [username: value, password: value]")
|
||||||
end
|
end
|
||||||
@ -31,10 +33,18 @@ module Locomotive
|
|||||||
end
|
end
|
||||||
|
|
||||||
def render(context)
|
def render(context)
|
||||||
context.stack do
|
render_all_and_cache_it(context)
|
||||||
context.scopes.last[@target.to_s] = Locomotive::Httparty::Webservice.consume(@url, @options.symbolize_keys)
|
end
|
||||||
|
|
||||||
render_all(@nodelist, context)
|
protected
|
||||||
|
|
||||||
|
def render_all_and_cache_it(context)
|
||||||
|
Rails.cache.fetch(@cache_key, :expires_in => @expires_in) do
|
||||||
|
context.stack do
|
||||||
|
context.scopes.last[@target.to_s] = Locomotive::Httparty::Webservice.consume(@url, @options.symbolize_keys)
|
||||||
|
|
||||||
|
render_all(@nodelist, context)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
1
lib/locomotive/middlewares.rb
Normal file
1
lib/locomotive/middlewares.rb
Normal file
@ -0,0 +1 @@
|
|||||||
|
require 'locomotive/middlewares/fonts'
|
44
lib/locomotive/middlewares/fonts.rb
Normal file
44
lib/locomotive/middlewares/fonts.rb
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
require 'rack/utils'
|
||||||
|
|
||||||
|
module Locomotive
|
||||||
|
module Middlewares
|
||||||
|
class Fonts
|
||||||
|
include Rack::Utils
|
||||||
|
|
||||||
|
def initialize(app, opts = {})
|
||||||
|
@app = app
|
||||||
|
@path_regexp = opts[:path] || %r{^/fonts/}
|
||||||
|
@file_server = ::Rack::File.new(opts[:root] || "#{Rails.root}/public")
|
||||||
|
@expires_in = opts[:expires_in] || 24.hour
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(env)
|
||||||
|
if env["PATH_INFO"] =~ @path_regexp
|
||||||
|
site = fetch_site(env['SERVER_NAME'])
|
||||||
|
|
||||||
|
if site.nil?
|
||||||
|
@app.call(env)
|
||||||
|
else
|
||||||
|
env["PATH_INFO"] = ::File.join('/', 'sites', site.id.to_s, 'theme', env["PATH_INFO"])
|
||||||
|
|
||||||
|
response = @file_server.call(env)
|
||||||
|
|
||||||
|
response[1]['Cache-Control'] = "public; max-age=#{@expires_in}" # varnish
|
||||||
|
|
||||||
|
response
|
||||||
|
end
|
||||||
|
else
|
||||||
|
@app.call(env)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def fetch_site(domain_name)
|
||||||
|
Rails.cache.fetch(domain_name, :expires_in => @expires_in) do
|
||||||
|
Site.match_domain(domain_name).first
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -56,7 +56,7 @@ module Locomotive
|
|||||||
'asset_collections' => Locomotive::Liquid::Drops::AssetCollections.new,
|
'asset_collections' => Locomotive::Liquid::Drops::AssetCollections.new,
|
||||||
'stylesheets' => Locomotive::Liquid::Drops::ThemeAssets::Stylesheets.new,
|
'stylesheets' => Locomotive::Liquid::Drops::ThemeAssets::Stylesheets.new,
|
||||||
'javascripts' => Locomotive::Liquid::Drops::ThemeAssets::Javascripts.new,
|
'javascripts' => Locomotive::Liquid::Drops::ThemeAssets::Javascripts.new,
|
||||||
'images' => Locomotive::Liquid::Drops::ThemeAssets::Images.new,
|
'theme_images' => Locomotive::Liquid::Drops::ThemeAssets::Images.new,
|
||||||
'contents' => Locomotive::Liquid::Drops::Contents.new,
|
'contents' => Locomotive::Liquid::Drops::Contents.new,
|
||||||
'current_page' => self.params[:page]
|
'current_page' => self.params[:page]
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ $(document).ready(function() {
|
|||||||
if (typeof $.fn.imagepicker != 'undefined')
|
if (typeof $.fn.imagepicker != 'undefined')
|
||||||
$('a#image-picker-link').imagepicker({
|
$('a#image-picker-link').imagepicker({
|
||||||
insertFn: function(link) {
|
insertFn: function(link) {
|
||||||
return "{{ theme_images." + link.attr('data-slug') + " }}";
|
return "{{ '" + link.attr('data-local-path') + "' | theme_image_url }}";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -47,13 +47,13 @@ $.fn.imagepicker = function(options) {
|
|||||||
.insertBefore($('.asset-picker ul li.clear'))
|
.insertBefore($('.asset-picker ul li.clear'))
|
||||||
.addClass('asset');
|
.addClass('asset');
|
||||||
|
|
||||||
asset.find('h4 a').attr('href', json.url)
|
asset.find('strong a').attr('href', json.url)
|
||||||
.attr('data-slug', json.slug)
|
.attr('data-local-path', json.local_path)
|
||||||
.attr('data-shortcut-url', json.shortcut_url)
|
.html(json.local_path).bind('click', function(e) {
|
||||||
.html(json.name).bind('click', function(e) {
|
|
||||||
copyLinkToEditor($(this), e);
|
copyLinkToEditor($(this), e);
|
||||||
});
|
});
|
||||||
asset.find('.image .inside img').attr('src', json.vignette_url);
|
asset.find('.more .size').html(json.size);
|
||||||
|
asset.find('.more .date').html(json.date);
|
||||||
|
|
||||||
if ($('.asset-picker ul li.asset').length % 3 == 0)
|
if ($('.asset-picker ul li.asset').length % 3 == 0)
|
||||||
asset.addClass('last');
|
asset.addClass('last');
|
||||||
@ -74,7 +74,7 @@ $.fn.imagepicker = function(options) {
|
|||||||
'onComplete': function() {
|
'onComplete': function() {
|
||||||
setupUploader();
|
setupUploader();
|
||||||
|
|
||||||
$('ul.assets h4 a').bind('click', function(e) { copyLinkToEditor($(this), e); });
|
$('ul.theme-assets strong a').bind('click', function(e) { copyLinkToEditor($(this), e); });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -37,7 +37,7 @@ $(document).ready(function() {
|
|||||||
|
|
||||||
$('a#image-picker-link').imagepicker({
|
$('a#image-picker-link').imagepicker({
|
||||||
insertFn: function(link) {
|
insertFn: function(link) {
|
||||||
return link.attr('data-url');
|
return link.attr('href');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -49,7 +49,7 @@
|
|||||||
|
|
||||||
/* ___ asset picker ___ */
|
/* ___ asset picker ___ */
|
||||||
|
|
||||||
div.asset-picker { width: 470px; position: relative; }
|
div.asset-picker { width: 720px; position: relative; }
|
||||||
div.asset-picker .actions { position: absolute; right: 4px; top: 0px; }
|
div.asset-picker .actions { position: absolute; right: 4px; top: 0px; }
|
||||||
|
|
||||||
div.asset-picker p.no-items { background-image: url("/images/admin/list/none-small.png"); }
|
div.asset-picker p.no-items { background-image: url("/images/admin/list/none-small.png"); }
|
||||||
@ -57,6 +57,9 @@ div.asset-picker p.no-items { background-image: url("/images/admin/list/none-sma
|
|||||||
div.asset-picker ul { overflow: auto; height: 471px; }
|
div.asset-picker ul { overflow: auto; height: 471px; }
|
||||||
div.asset-picker ul li.new-asset { display: none; }
|
div.asset-picker ul li.new-asset { display: none; }
|
||||||
|
|
||||||
|
div.asset-picker ul { margin: 0px; }
|
||||||
|
div.asset-picker ul li .more { top: 8px; }
|
||||||
|
|
||||||
/* ___ custom fields ___ */
|
/* ___ custom fields ___ */
|
||||||
|
|
||||||
#edit-custom-field {
|
#edit-custom-field {
|
||||||
|
Loading…
Reference in New Issue
Block a user