# Adds helper methods for generating the TinyMCE initialization script
# within your views
module TinyMCEHelper
# The path to the file which contains all valid options that can be used
# to configure TinyMCE
OPTIONS_FILE_PATH = "#{Rails.root}/config/tiny_mce_options.yml"
# A regular expression matching options that are dynamic (i.e. they can
# vary on an integer or string basis)
DYNAMIC_OPTIONS = /theme_advanced_buttons|theme_advanced_container/
# Whether or not to use verbose output
mattr_accessor :verbose
@@verbose = true
# A list of all valid options that can be used to configure TinyMCE
mattr_accessor :valid_options
@@valid_options = File.exists?(OPTIONS_FILE_PATH) ? File.open(OPTIONS_FILE_PATH) {|f| YAML.load(f.read)} : []
class << self
# Installs TinyMCE by downloading it and adding it to your application's
# javascripts folder.
#
# Configuration options:
# * :version - The version of TinyMCE to install. Default is the
# latest version.
# * :target - The path to install TinyMCE to, relative to the
# project root. Default is "public/javascripts/tiny_mce"
# * :force - Whether to install TinyMCE, regardless of whether it
# already exists on the filesystem.
#
# == Versions
#
# By default, this will install the latest version of TinyMCE. You can
# install a specific version of TinyMCE (if you are using an old API) by
# passing in the version number.
#
# For example,
# TinyMCEHelper.install # Installs the latest version
# TinyMCEHelper.install(:version => '3.2.2') # Installs version 3.2.2
#
# An exception will be raised if the specified version cannot be found.
#
# == Target path
#
# By default, this will install TinyMCE into Rails.root/public/javascripts/tiny_mce.
# If you want to install it to a different directory, you can pass in a
# parameter with the relative path from Rails.root.
#
# For example,
# TinyMCEHelper.install(:target => 'public/javascripts/richtext')
#
# == Conflicting paths
#
# If TinyMCE is found to already be installed on the filesystem, a prompt
# will be displayed for whether to overwrite the existing directory. This
# prompt can be automatically skipped by passing in the :force option.
def install(options = {})
options.assert_valid_keys(:version, :target, :force)
options.reverse_merge!(:force => false)
version = options[:version]
base_target = options[:target] || 'public/javascripts/tiny_mce'
source_path = 'tinymce'
target_path = File.expand_path(File.join(Rails.root, base_target))
# If TinyMCE is already installed, make sure the user wants to continue
if !options[:force] && File.exists?(target_path)
print "TinyMCE already installed in #{target_path}. Overwrite? (y/n): "
while !%w(y n).include?(option = STDIN.gets.chop)
print "Invalid option. Overwrite #{target_path}? (y/n): "
end
return if option == 'n'
end
require 'open-uri'
# Get the latest TinyMCE version
unless version
require 'hpricot'
puts 'Finding latest version of TinyMCE to download...' if verbose
doc = Hpricot(open('http://sourceforge.net/project/showfiles.php?group_id=103281&package_id=111430'))
file_element = (doc/'tr[@id^="pkg0_1rel0_"] a').detect {|file| file.innerHTML.to_s =~ /tinymce_([\d_]+).zip$/}
raise ArgumentError, 'Could not find latest TinyMCE version' if !file_element
version = $1. gsub('_', '.')
end
file_url = "http://prdownloads.sourceforge.net/tinymce/tinymce_#{version.gsub('.', '_')}.zip?download"
# Download the file
puts "Downloading TinyMCE-#{version} source from #{file_url}..." if verbose
open(file_url) do |file|
file_path = file.path
# Extract and install
puts "Extracting source from #{file_path}..." if verbose
require 'zip/zip'
require 'zip/zipfilesystem'
Zip::ZipFile.open(file_path) do |zipfile|
zipfile.entries.each do |entry|
if match = /jscripts\/tiny_mce\/(.*)/.match(entry.name)
FileUtils.mkdir_p("#{target_path}/#{File.dirname(match[1])}")
entry.extract("#{target_path}/#{match[1]}") { true }
end
end
end
puts 'Done!' if verbose
end
end
# Uninstalls the TinyMCE installation and optional configuration file
#
# Configuration options:
# * +target+ - The path that TinyMCE was installed to. Default is "public/javascripts/tiny_mce"
def uninstall(options = {})
# Remove the TinyMCE configuration file
File.delete(OPTIONS_FILE_PATH)
# Remove the TinyMCE installation
FileUtils.rm_rf(options[:target] || "#{Rails.root}/public/javascripts/tiny_mce")
end
# Updates the list of possible configuration options that can be used
# when initializing the TinyMCE script. These are always installed to
# the application folder, config/tiny_mce_options.yml. If this file
# does not exist, then the TinyMCE helper will not be able to verify
# that all of the initialization options are valid.
def update_options
require 'hpricot'
require 'open-uri'
require 'yaml'
puts 'Downloading configuration options from TinyMCE Wiki...' if verbose
doc = Hpricot(open('http://wiki.moxiecode.com/index.php/TinyMCE:Configuration'))
options = (doc/'a[@title*="Configuration/"]/').collect {|option| option.to_s}.sort
options.reject! {|option| option =~ DYNAMIC_OPTIONS}
File.open(OPTIONS_FILE_PATH, 'w') do |out|
YAML.dump(options, out)
end
puts 'Done!' if verbose
end
end
# Is TinyMCE being used?
def using_tiny_mce?
@uses_tiny_mce
end
# Create the TinyMCE initialization scripts. The default configuration
# is for a simple theme that replaces all textareas on the page. For
# example, the default initialization script will generate the following:
#
# tinyMCE.init({
# 'mode' : 'textareas',
# 'theme' : 'simple'
# });
#
# == Customizing initialization options
#
# To customize the options to be included in the initialization script,
# you can pass in a hash to +tiny_mce_init_script+. For example,
#
# tiny_mce_init_script(
# :theme => 'advanced',
# :editor_selector => 'rich_text',
# :content_css => '/stylesheets/tiny_mce_content.css',
# :editor_css => '/stylesheets/tiny_mce_editor.css',
# :auto_reset_designmode => true
# )
#
# will generate:
#
# tinyMCE.init({
# 'mode' : 'textareas',
# 'theme' : 'advanced',
# 'editor_selected' : 'rich_text',
# 'content_css' : '/stylesheets/tiny_mce_content.css'
# });
#
# == Validating options
#
# If additional options are passed in to initialize TinyMCE, they will be
# validated against the list of valid options in TinyMCEHelper#valid_options.
# These options are configured in the file config/tiny_mce_options.yml.
# You can generate this file by invoke the rake task tiny_mce:update_options.
def tiny_mce_init_script(options = @tiny_mce_options)
options ||= {}
options.stringify_keys!.reverse_merge!(
'mode' => 'textareas',
'theme' => 'simple'
)
# Check validity
plugins = options['plugins']
options_to_validate = options.reject {|option, value| plugins && plugins.include?(option.split('_')[0]) || option =~ DYNAMIC_OPTIONS}
options_to_validate.assert_valid_keys(@@valid_options) if @@valid_options && @@valid_options.any?
init_script = 'tinyMCE.init({'
options.sort.each do |key, value|
init_script += "\n#{key} : "
case value
when String, Symbol, Fixnum
init_script << "'#{value}'"
when Array
init_script << "'#{value.join(',')}'"
when TrueClass
init_script << 'true'
when FalseClass
init_script << 'false'
else
raise ArgumentError, "Cannot parse value of type #{value.class} passed for TinyMCE option #{key}"
end
init_script << ','
end
init_script.chop << "\n});"
end
# Generate the TinyMCE. Any arguments will be passed to tiny_mce_init_script.
def tiny_mce(*args)
javascript_tag tiny_mce_init_script(*args)
end
# The name of the TinyMCE javascript file to use. In development, this
# will use the source (uncompressed) file in order to help with debugging
# issues that occur within TinyMCE. In production, the compressed version
# of TinyMCE will be used in order to increased download speed.
def tiny_mce_file_name
Rails.env == 'development' ? 'tiny_mce/tiny_mce_src' : 'tiny_mce/tiny_mce'
end
# Generates the javascript include for TinyMCE. For example,
#
# javascript_include_tiny_mce
#
# will generate:
#
#
def javascript_include_tiny_mce
javascript_include_tag tiny_mce_file_name
end
# Conditionally includes the TinyMCE javascript file if the variable
# @uses_tiny_mce has been set to true.
def javascript_include_tiny_mce_if_used
javascript_include_tiny_mce if using_tiny_mce?
end
end
ActionController::Base.class_eval do
helper TinyMCEHelper
end