theme assets

This commit is contained in:
dinedine 2010-05-11 23:38:52 +02:00
parent 9901f53e12
commit e5c10cfa3e
265 changed files with 1710 additions and 20 deletions

2
.gitignore vendored
View File

@ -4,3 +4,5 @@ log/*.log
tmp/**/*
.DS_Store
rerun.txt
uploads
spec/tmp

View File

@ -11,9 +11,11 @@ gem "mongoid", ">= 2.0.0.beta4"
gem "mongoid_acts_as_tree", :git => 'git://github.com/evansagge/mongoid_acts_as_tree.git'
gem "warden"
gem "devise", ">= 1.1.rc0"
gem "haml", '>= 3.0.0.beta.2', :git => 'git://github.com/nex3/haml.git'
gem "haml", '>= 3.0.1' #, :git => 'git://github.com/nex3/haml.git'
gem "formtastic", :git => 'git://github.com/justinfrench/formtastic.git', :branch => 'rails3'
gem "mongoid_acts_as_tree", :git => 'git://github.com/evansagge/mongoid_acts_as_tree.git'
gem "carrierwave", :git => "http://github.com/jnicklas/carrierwave.git"
gem "rmagick"
# Development environment

View File

@ -0,0 +1,54 @@
module Admin
class ThemeAssetsController < BaseController
sections 'settings', 'theme_assets'
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? }
end
def new
@asset = current_site.theme_assets.build
end
def edit
@asset = current_site.theme_assets.find(params[:id])
end
def create
@asset = current_site.theme_assets.build(params[:theme_asset])
if @asset.save
flash_success!
redirect_to edit_admin_theme_asset_url(@asset)
else
flash_error!
render :action => 'new'
end
end
def update
@asset = current_site.theme_assets.find(params[:id])
if @asset.update_attributes(params[:theme_asset])
flash_success!
redirect_to edit_admin_theme_asset_url(@asset)
else
flash_error!
render :action => 'edit'
end
end
def destroy
@asset = current_site.theme_assets.find(params[:id])
@asset.destroy
flash_success!
redirect_to admin_theme_assets_url
end
end
end

View File

@ -4,6 +4,9 @@ class PagesController < ActionController::Base
before_filter :require_site
def show; end
def show
logger.debug "fullpath = #{request.fullpath}"
# @page = current_site.pages.find
end
end

View File

@ -0,0 +1,40 @@
module Admin::AssetsHelper
def vignette_tag(asset)
if asset.image?
if asset.width < 80 && asset.height < 80
image_tag(asset.source.url)
else
image_tag(asset.source.url(:medium))
end
# elsif asset.pdf?
# image_tag(asset.source.url(:medium))
else
mime_type_to_image(asset, :medium)
end
end
def mime_type_to_image(asset, size = :thumb)
mime_type = File.mime_type?(asset.source_filename)
filename = "unknown"
if !(mime_type =~ /pdf/).nil?
filename = "PDF"
elsif !(mime_type =~ /css/).nil?
filename = "CSS"
elsif !(mime_type =~ /javascript/).nil?
filename = "JAVA"
end
image_tag(File.join("admin", "icons", "filetype", size.to_s, filename + ".png"))
end
def image_dimensions_and_size(asset)
content_tag(:small, "#{asset.width}px x #{@asset.height}px | #{number_to_human_size(asset.size)}")
end
def allow_plain_text_editing?(asset)
asset.new_record? || asset.stylesheet? || asset.javascript?
end
end

View File

@ -21,7 +21,7 @@ class Page
before_validate :normalize_slug
before_save { |p| p.parent_id = nil if p.parent_id.blank? }
before_save :change_parent
before_create { |p| p.parts << PagePart.build_body_part }
before_create { |p| p.parts << PagePart.build_body_part if p.parts.empty? }
before_create { |p| p.fix_position(false) }
before_create :add_to_list_bottom
before_destroy :do_not_remove_index_and_404_pages

View File

@ -11,6 +11,7 @@ class Site
has_many_related :pages
has_many_related :layouts
has_many_related :snippets
has_many_related :theme_assets
embeds_many :memberships
## validations ##
@ -81,7 +82,7 @@ class Site
end
def destroy_in_cascade!
%w{pages layouts snippets}.each do |association|
%w{pages layouts snippets theme_assets}.each do |association|
self.send(association).destroy_all
end
end

94
app/models/theme_asset.rb Normal file
View File

@ -0,0 +1,94 @@
class ThemeAsset
include Mongoid::Document
include Mongoid::Timestamps
## fields ##
field :slug, :type => String
field :content_type, :type => String
field :width, :type => Integer
field :height, :type => Integer
field :size, :type => Integer
mount_uploader :source, ThemeAssetUploader
## associations ##
belongs_to_related :site
## callbacks ##
before_validate :sanitize_slug
before_validate :store_plain_text
before_save :set_slug
## validations ##
validate :extname_can_not_be_changed
validates_presence_of :site
validates_presence_of :slug, :if => Proc.new { |a| a.new_record? && a.performing_plain_text? }
validates_integrity_of :source
## accessors ##
attr_accessor :performing_plain_text
## methods ##
%w{image stylesheet javascript}.each do |type|
define_method("#{type}?") do
self.content_type == type
end
end
def plain_text
@plain_text ||= (if self.stylesheet? || self.javascript?
File.read(self.source.path)
else
nil
end)
end
def plain_text=(source)
self.performing_plain_text = true if self.performing_plain_text.nil?
@plain_text = source
end
def performing_plain_text?
!(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),
:filename => self.filename
})
end
def filename
if not self.image?
"#{self.slug}.#{self.stylesheet? ? 'css' : 'js'}"
else
"#{self.slug}#{File.extname(self.source.file.original_filename)}"
end
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?
Rails.logger.debug "previous = #{self.source.file.original_filename.inspect} / #{self.source_filename.inspect}"
if File.extname(self.source.file.original_filename) != File.extname(self.source_filename)
self.errors.add(:source, :extname_changed)
end
end
end

View File

@ -0,0 +1,87 @@
# encoding: utf-8
class ThemeAssetUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
# Choose what kind of storage to use for this uploader
# storage :file
# storage :s3
# Override the directory where uploaded files will be stored
# This is a sensible default for uploaders that are meant to be mounted:
def store_dir
"sites/#{model.site_id}/themes/#{model.id}"
end
version :thumb do
process :resize_to_fill => [50, 50]
process :convert => 'png'
end
version :medium do
process :resize_to_fill => [80, 80]
process :convert => 'png'
end
version :preview do
process :resize_to_fit => [880, 1100]
process :convert => 'png'
end
process :set_content_type
process :set_size
process :set_width_and_height
def set_content_type
value = :other
self.class.content_types.each_pair do |type, rules|
rules.each do |rule|
case rule
when String then value = type if file.content_type == rule
when Regexp then value = type if (file.content_type =~ rule) == 0
end
end
end
model.content_type = value
end
def set_size
model.size = file.size
end
def set_width_and_height
if model.image?
model.width, model.height = `identify -format "%wx%h" #{file.path}`.split(/x/).collect(&:to_i)
end
end
def self.content_types
{
:image => ['image/jpeg', 'image/pjpeg', 'image/gif', 'image/png', 'image/x-png', 'image/jpg'],
:stylesheet => ['text/css'],
:javascript => ['text/javascript', 'text/js', 'application/x-javascript']
}
end
def extension_white_list
%w(jpg jpeg gif png css js)
end
def filename
Rails.logger.debug "slug ===> #{model.slug} / #{model.content_type} / #{original_filename}"
if model.slug.present?
model.filename
else
extension = File.extname(original_filename)
basename = File.basename(original_filename, extension).slugify(:underscore => true)
"#{basename}#{extension}"
end
#
# original_filename
end
end

View File

@ -34,7 +34,7 @@
= f.custom_input :language, { :css => 'full', :with_label => false } do
- Locomotive.config.locales.each do |locale|
%span
= image_tag "admin/flags/#{locale}.png"
= image_tag "admin/icons/flags/#{locale}.png"
= f.radio_button :locale, locale
&nbsp;
= t(".#{locale}")

View File

@ -2,4 +2,5 @@
= admin_submenu_item 'site', edit_admin_current_site_url
= admin_submenu_item 'layouts', admin_layouts_url
= admin_submenu_item 'snippets', admin_snippets_url
= admin_submenu_item 'theme_assets', admin_theme_assets_url
= admin_submenu_item 'account', edit_admin_my_account_url

View File

@ -0,0 +1,7 @@
%li{ :class => "asset #{'last' if (asset_counter + 1) % 6 == 0}"}
%h4= link_to truncate(asset.slug, :length => 22), edit_admin_theme_asset_path(asset)
.image
.inside
= vignette_tag(asset)
.actions
= link_to image_tag('admin/list/icons/cross.png'), admin_theme_asset_path(asset), :class => 'remove', :confirm => t('admin.messages.confirm'), :method => :delete

View File

@ -0,0 +1,36 @@
- content_for :head do
= javascript_include_tag 'admin/plugins/codemirror/codemirror', 'admin/theme_assets.js'
= f.hidden_field :performing_plain_text
#file-selector{ :class => "selector #{'hidden' if @asset.performing_plain_text?}" }
= f.inputs :name => :information do
= f.input :source
- if @asset.new_record? || !@asset.image?
%span.alt
= t('admin.theme_assets.form.choose_plain_text')
- if allow_plain_text_editing?(@asset)
#text-selector{ :class => "selector #{'hidden' if !@asset.performing_plain_text?}", :style => "#{'display: none' if !@asset.performing_plain_text?}" }
= f.inputs :name => :code, :class => 'inputs code' do
- if @asset.new_record?
= f.input :slug
= f.custom_input :content_type do
= f.select :content_type, ["stylesheet", "javascript"]
= f.custom_input :plain_text, :css => 'full', :with_label => false do
%code{ :class => (@asset.new_record? || (@asset.size && @asset.size > 40000) ? 'nude' : @asset.content_type) }
= f.text_area :plain_text
%span.alt
= t('admin.theme_assets.form.choose_file')
- if @asset.image?
= f.foldable_inputs :name => "#{t('formtastic.titles.preview')} #{image_dimensions_and_size(@asset)}", :class => 'preview' do
%li
.image
.inside
= image_tag(@asset.source.url(:preview))

View File

@ -0,0 +1,15 @@
- title t('.title', :file => @asset.source_filename)
- content_for :submenu do
= render 'admin/shared/menu/settings'
- content_for :buttons do
= admin_button_tag t('admin.theme_assets.index.new'), new_admin_theme_asset_url, :class => 'add'
%p= t('.help')
= semantic_form_for @asset, :url => admin_theme_asset_url(@asset), :html => { :multipart => true } do |form|
= render 'form', :f => form
= render 'admin/shared/form_actions', :back_url => admin_theme_assets_url, :button_label => :update

View File

@ -0,0 +1,27 @@
- title t('.title')
- content_for :submenu do
= render 'admin/shared/menu/settings'
- content_for :buttons do
= admin_button_tag :new, new_admin_theme_asset_url, :class => 'add'
%p= t('.help')
%h3= t('.css_and_js')
- if @non_image_assets.empty?
%p.no-items= t('.no_items', :url => new_admin_theme_asset_url)
- else
%ul.assets
= render :partial => 'asset', :collection => @non_image_assets
%li.clear
%br
%h3= t('.images')
- if @image_assets.empty?
%p.no-items= t('.no_items', :url => new_admin_theme_asset_url)
- else
%ul.assets
= render :partial => 'asset', :collection => @image_assets
%li.clear

View File

@ -0,0 +1,12 @@
- title t('.title')
- content_for :submenu do
= render 'admin/shared/menu/settings'
%p= t('.help')
= semantic_form_for @asset, :url => admin_theme_assets_url, :html => { :multipart => true } do |form|
= render 'form', :f => form
= render 'admin/shared/form_actions', :back_url => admin_theme_assets_url, :button_label => :create

View File

@ -27,3 +27,9 @@ Locomotive::Application.configure do
:domain => "example.com"
}
end
CarrierWave.configure do |config|
config.storage = :file
config.root = File.join(Rails.root, 'public')
end

View File

@ -27,3 +27,7 @@ Locomotive::Application.configure do
# like if you have constraints or database-specific column types
# config.active_record.schema_format = :sql
end
CarrierWave.configure do |config|
config.storage = :file
end

View File

@ -0,0 +1,48 @@
require 'carrierwave/orm/mongoid'
module CarrierWave
class SanitizedFile
def original_filename=(filename)
@original_filename = filename
end
def content_type=(content_type)
@content_type = content_type
end
# http://github.com/jnicklas/carrierwave/issuesearch?state=closed&q=content+type#issue/48
def copy_to_with_content_type(new_path, permissions=nil)
new_file = self.copy_to_without_content_type(new_path, permissions)
new_file.content_type = self.content_type
new_file
end
alias_method_chain :copy_to, :content_type
# FIXME (Did) CarrierWave speaks mime type now
def content_type
return @content_type if @content_type
if @file.respond_to?(:content_type) and @file.content_type
@file.content_type.chomp
else
File.mime_type?(@file) if @file.is_a?(String)
end
end
end
end
module CarrierWave
module Mongoid
def validates_integrity_of(*attrs)
options = attrs.last.is_a?(Hash) ? attrs.last : {}
options[:message] ||= I18n.t('carrierwave.errors.integrity', :default => 'is not an allowed type of file.')
validates_each(*attrs) do |record, attr, value|
record.errors.add attr, options[:message] if record.send("#{attr}_integrity_error")
end
end
end
end

View File

@ -23,6 +23,7 @@ en:
snippets: Snippets
account: My account
site: Site
theme_assets: Theme files
footer:
developed_by: Developed by
powered_by: and Powered by
@ -97,6 +98,20 @@ en:
en: English
fr: French
theme_assets:
index:
title: Listing theme files
new: new file
css_and_js: Style and javascript
images: Images
no_items: "There are no files for now. Just click <a href=\"{{url}}\">here</a> to create the first one."
new:
title: New file
edit:
title: "Editing {{file}}"
form:
choose_file: Choose file
choose_plain_text: Choose plain text
formtastic:
titles:
@ -109,6 +124,14 @@ en:
access_points: Access points
memberships: Accounts
membership_email: Account email
file: File
preview: Preview
labels:
theme_asset:
new:
source: File
edit:
source: Replace file
hints:
page:
keywords: "Meta keywords used within the head tag of the page. They are separeted by an empty space. Required for SEO."
@ -117,4 +140,8 @@ en:
slug: "You need to know it in order to insert the snippet inside a page or a layout"
site:
domain_name: "ex: locomotiveapp.org"
theme_asset:
slug: "You do not need to add the extension file (.css or .js)"
edit:
source: "You can replace it by a file of the same extension"

View File

@ -0,0 +1,4 @@
en:
carrierwave:
errors:
integrity: 'is not an allowed type of file.'

View File

@ -0,0 +1,4 @@
fr:
carrierwave:
errors:
integrity: "n'est pas un type de fichier autorisé"

View File

@ -6,6 +6,7 @@ en:
missing_content_for_layout: "should contain 'content_for_layout' liquid tag"
needs_admin_account: "One admin account is required at least"
protected_page: "You can not remove index or 404 pages"
extname_changed: "New file does not have the original extension"
attributes:
defaults:

View File

@ -35,9 +35,12 @@ Locomotive::Application.routes.draw do |map|
resource :my_account
resources :memberships
resources :memberships
resources :theme_assets
end
# magic url
match '/' => 'pages#show'
match '*path' => 'pages#show'
end

View File

@ -34,6 +34,15 @@ x create 404 + index pages once a site is created
x can not delete index + 404 pages
x validates_uniqueness_of :slug, :scope => :id
x domain scoping when authenticating
- theme assets
x create / update
x slug
x filename from slug
x can not replace a javascript by a stylesheet
- disable version if not image
- asset collections
- assets
- custom resizing
BACKLOG:

View File

@ -5,7 +5,7 @@ class String
# end
def slugify(options = {})
options = { :sep => '_', :without_extension => false, :downcase => false }.merge(options)
options = { :sep => '_', :without_extension => false, :downcase => false, :underscore => false }.merge(options)
# replace accented chars with ther ascii equivalents
s = ActiveSupport::Inflector.transliterate(self).to_s
# No more than one slash in a row
@ -20,6 +20,8 @@ class String
s.downcase! if options[:downcase]
# Turn unwanted chars into the seperator
s.gsub!(/[^a-zA-Z0-9\-_\+\/]+/i, options[:sep])
# Underscore
s.gsub!(/[\-]/i, '') if options[:underscore]
s
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Some files were not shown because too many files have changed in this diff Show More