??xml version="1.0" encoding="utf-8" standalone="yes"?> 另外Q还要在environment.rb的最后插入以下代码:
在日志里昄如下Q表面上看好象是路由配置的问?/p> ActionController::RoutingError (no route found to match "/fckblank.html" with {:method=>:get}):
解决Ҏ Q?br /> 修改Qvendor/plugins/fckeditor/app/controller/fckeditor_controller.rb
]]>
(1)首先要在域名服务商端做一个泛域名解析. 我用的是godaddy.com,新Z个A记录(host=*,point to指向我的服务器的ip),q样所有二U域名都会{到我的服务器来了
(2)接着在web.xml配一个自已写的域名过滤器,
<filter-name>URLFilter</filter-name>
<filter-class>com.yowob.commons.URLFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>URLFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
qo器的代码如下。这里先二U域名和全球域名和用户ID的映?保存在一个数据表? 然后讉Kq来时对地址做一个判? 再取出对应的用户ID. 再{一下就行了. 我的静态文仉在static目录Q所以还加了一个static的判断?br />
比如: http://time.you.com/board/21 用time对应用户ID?,则访问效果有 http://www.you.com/6/board/21相同, 不过地址栏还是显C?a >http://time.you.com/board/21?br />
再比如:htttp://www.userdomain.com/board/21Q这个是用户ID?的全球域名,讉K效果也和上面一栗?br />
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.yowob.Constants;
import com.yowob.dao.SiteDAO;
import com.yowob.dto.SiteDTO;
public class URLFilter implements Filter {
private static final Log log = LogFactory.getLog(URLFilter.class);
private static final String DOMAIN_END = "." + Constants.DOMAIN; //.you.com
private static final Map<String, Long> NAME_MAP = new HashMap<String, Long>();
private static final Map<String, Long> DOMAIN_MAP = new HashMap<String, Long>();
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("------------------------------init");
SiteDAO siteDAO = new SiteDAO();
List<SiteDTO> list = siteDAO.getAll();
for (SiteDTO siteDTO : list) {
String name = siteDTO.getName();
if (StringUtils.isNotEmpty(name)) {
NAME_MAP.put(name, siteDTO.getId());
}
String domain = siteDTO.getDomain();
if (StringUtils.isNotEmpty(domain)) {
DOMAIN_MAP.put(domain, siteDTO.getId());
}
}
}
public static void updateName(String oldValue, String newValue, Long siteId) {
if (StringUtils.equals(oldValue, newValue)) {
return;
}
if (StringUtils.isNotEmpty(oldValue)) {
NAME_MAP.remove(oldValue);
}
if (StringUtils.isNotEmpty(newValue)) {
NAME_MAP.put(newValue, siteId);
}
}
public static void updateDomain(String oldValue, String newValue, Long siteId) {
if (StringUtils.equals(oldValue, newValue)) {
return;
}
if (StringUtils.isNotEmpty(oldValue)) {
DOMAIN_MAP.remove(oldValue);
}
if (StringUtils.isNotEmpty(newValue)) {
DOMAIN_MAP.put(newValue, siteId);
}
}
@Override
public void destroy() {
log.info("------------------------------destroy");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
String requestURI = request.getRequestURI();
String serverName = request.getServerName().toLowerCase();
String realURI = getRealRequestURI(serverName, requestURI);
request.getRequestDispatcher(realURI).forward(request, response);
}
private String getRealRequestURI(String serverName, String requestURI) {
if (Constants.WWW_DOMAIN.equals(serverName) || requestURI.startsWith("/static/") || Constants.DOMAIN.equals(serverName)) {
return requestURI;
}
if (serverName.endsWith(DOMAIN_END)) {
String secondDomain = serverName.substring(0, serverName.indexOf("."));
//|站id
if (NumberUtils.isNumber(secondDomain))
return getURI(secondDomain, requestURI);
//|站英文?/span>
Long siteId = NAME_MAP.get(secondDomain);
if (siteId == null) {
//保留的二U域?/span>
if (Constants.isPrivateSecondDomain(secondDomain)) {
return requestURI;
}
return "/message?msg=不存在二U域?/span>" + secondDomain;
// throw new RuntimeException("do not exist second domain: " + secondDomain);
}
return getURI(siteId + "", requestURI);
}
//域名
Long siteId = DOMAIN_MAP.get(serverName);
if (siteId == null) {
return requestURI;
} else {
return getURI(siteId + "", requestURI);
}
}
private static String getURI(String siteId, String requestURI) {
if (requestURI.equals("/")) {
return "/" + siteId;
} else {
return "/" + siteId + requestURI;
}
}
}
其他一些小技巧:
Q1Qؓ了便于本机测试,可以修改windows 的hosts文g。我的如下:
127.0.0.1 you.com
127.0.0.1 www.you.com
127.0.0.1 time1.you.com
127.0.0.1 time2.you.com
127.0.0.1 6.you.com
127.0.0.1 www.bobo.com
Q2Qweb늚各种地址则要注意相对路径的问题。主要考虑
首先讄<base />Q或<base />其中地址最后要加一?
然后面中其他地址前面不加/Q就是相对地址Q以地址栏ؓ基础Q。加?Q则是绝对地址?br />
]]>
http://www.dentisthealthcenter.com/Files/chengang/myvim.zip
在ubuntu7.10中的gvim出现qQ这旉要在ȝ录下的?vimrc”文件中加上定义字体的一句。我用的是“微软雅黑”字体所以此句ؓQ?br />
]]>
q个设计其实很简单,是“引?配置?-M面只定义一个rthmlQ可以把它看做页面引擎,然后用一个配|文件指定了面所应具有的模块和数据?
面模块p一个个装有数据的盒子,通过“页面引?+
配置文g”把q些盒子l合hQ象搭积木一栗页面引擎是各用户共用的Q配|文件是各用L有的Q这样一装配hQ就形成了用L个性化面?br />
剩下的重点就是怎么定义配置文g。首先是要划清配|文件的责Q界线----它只负责定义盒子里的数据Q还有盒子的嵌套关系Q而大和位置{布局斚w则全部交lCSS去负责?br />
上面是初步想法,下面看看具体实现Q代码仅供参?br />
以下是某个用L配置文gQ我没用XMLQ而是用YAMLQ。board_1是指ID=1q个栏目所用的配置定义。topContent是一?div><div>的id|我把每个栏目的页面分?span>topContent、sideContent边(左或右由CSS军_Q?/span>primaryContent
主要、bottomContent底。topic是指d一个模块(盒子Q,昄一个主题内宏V和topiccM是还有显C图像的image、显CZ题列
表的topics、显C分cd表的categories{等{等。它们各有不同的属性|比如topic模块Q它需要定指它的主题idQ以及它所用的
view(topics/_show_hot.rhtml or topics/_show.rhtml{等)?br />
Q虽然这个配|文件抽象得q不够,但这样子已经可以解决我的需要了Q那?/span>暂时q样先了。)
template.yml
然后在controller里把配置文gdQ再转化成模型类。我把各个界面模块看做一个个盒子Box
q是它的Box
q是topic模块?br />
q是Image模块?br />
.....{?{, 其他的Box子类大同异
然后在一个controller里把q些配置信息转成Box模型c?br />
最后是它页面引擎(逻辑代码和页面代码在一P比较丑陋Q?
q样Q以后想在页面上增加删除什么模块,修改配置文gp了。当然给用户用,q必d用AJAX来写个GUI界面QM能让用户手工L配置文g吧?br />
]]>
1. 新增两个javascript函数?br />
//FCKEditor的文件浏览窗关闭后,会调用此函数Qƈ把所选图片的url传入?/span>
function SetUrl(url){
document.getElementById(currentImageTextID).value=url;
}
//imageTextID: 囄文本框的ID?/span>
//uploadPath: 服务器的囄目录
//type: 览cdQ值可为Image/Flash/File/MediaQ如果ؓI字Ԍ则表C浏览所有类型的文g
function OpenImageBrowser(imageTextID, uploadPath, type ) {
currentImageTextID = imageTextID;
window.open('/javascripts/fckeditor/editor/filemanager/browser/default/browser.html?uploaded='+uploadPath +'&Type='+type+'&Connector=/fckeditor/command','Browse/Upload Images','toolbar=no,status=no, resizable=yes,dependent=yes, scrollbars=yes,width=600,height=400')
}
2.在View中这样?br />
<input id="topic_image" name="topic[image]" size="30" type="text">
<input value="览服务?/span>" onclick="OpenImageBrowser('topic_image', '/uploads/s<%= params[:user_id]%>', 'Image')" type="button">
]]>
private
def current_directory_path
base_dir = "#{RAILS_ROOT}/public"
#TODO 在创建用hQ就建立好目录。这时可以去掉这部䆾代码Q提高运行效率?br /> ("#{params[:uploaded]||UPLOADED}/#{params[:Type]}").split('/').each do |s|
next if s==''
base_dir += '/' + s
Dir.mkdir(base_dir,0775) unless File.exists?(base_dir)
end
check_path("#{base_dir}#{params[:CurrentFolder]}")
end
def upload_directory_path
uploaded = @request.relative_url_root.to_s+"#{params[:uploaded]||UPLOADED}/#{params[:Type]}"
"#{uploaded}#{params[:CurrentFolder]}"
end
def check_file(file)
# check that the file is a tempfile object
unless "#{file.class}" == "Tempfile" || "#{file.class}" == "StringIO"
@errorNumber = 403
throw Exception.new
end
file
end
def check_path(path)
exp_path = File.expand_path path
if exp_path !~ %r[^#{File.expand_path(RAILS_ROOT)}/public#{params[:uploaded]||UPLOADED}]
@errorNumber = 403
throw Exception.new
end
path
end
另外Q它前面的常量UPLOADED_ROOT也没用了Q可以删掉?br />
2. 在上面的代码?span style="color: rgb(0, 0, 0);">params[:uploaded]是关键,它就是我们动态定义的上传目录。该值来自于FCKEditor的一些html面Q它是通过get参数传入的。修改browser.html文gQ如下粗体部份)Q在它的urlh中把我们定义目录加入到get参数列中Q这样它可以传?/span>fckeditor_controller.rb里了?br />
var sServerPath = GetUrlParam( 'ServerPath' ) ;
if ( sServerPath.length > 0 )
oConnector.ConnectorUrl += 'ServerPath=' + escape( sServerPath ) + '&' ;
var sUploaded = GetUrlParam( 'uploaded' ) ;
if ( sUploaded.length > 0 )
oConnector.ConnectorUrl += 'uploaded=' + escape( sUploaded ) + '&' ;
oConnector.ResourceType = GetUrlParam( 'Type' ) ;
oConnector.ShowAllTypes = ( oConnector.ResourceType.length == 0 ) ;
3. 上面的GetUrlParam( 'uploaded' ) 的值来自于fckcustom.js。修改fckcustom.jsQ如下粗体部份)Q把uploaded加入到get参数列中?br />
FCKRelativePath = '';
// DON'T CHANGE THESE
FCKConfig.LinkBrowserURL = FCKConfig.BasePath + 'filemanager/browser/default/browser.html?Connector='+FCKRelativePath+'/fckeditor/command';
FCKConfig.ImageBrowserURL = FCKConfig.BasePath + 'filemanager/browser/default/browser.html?uploaded='+FCKConfig.uploaded+'&Type=Image&Connector='+FCKRelativePath+'/fckeditor/command';
FCKConfig.FlashBrowserURL = FCKConfig.BasePath + 'filemanager/browser/default/browser.html?uploaded='+FCKConfig.uploaded+'&Type=Flash&Connector='+FCKRelativePath+'/fckeditor/command';
FCKConfig.LinkUploadURL = FCKRelativePath+'/fckeditor/upload';
FCKConfig.ImageUploadURL = FCKRelativePath+'/fckeditor/upload?Type=Image&uploaded='+FCKConfig.uploaded;
FCKConfig.FlashUploadURL = FCKRelativePath+'/fckeditor/upload?Type=Flash&uploaded='+FCKConfig.uploaded;
FCKConfig.AllowQueryStringDebug = false;
FCKConfig.SpellChecker = 'SpellerPages';
// ONLY CHANGE BELOW HERE
FCKConfig.SkinPath = FCKConfig.BasePath + 'skins/silver/';
FCKConfig.AutoDetectLanguage = false ;
FCKConfig.DefaultLanguage = 'zh-cn' ;
FCKConfig.FontNames = '微Y雅黑;宋体;黑体;隶书;楷体_GB2312;Arial;Comic Sans MS;Courier New;Tahoma;Times New Roman;Verdana' ;
FCKConfig.ToolbarSets["Simple"] = [
['Source','-','FitWindow','Preview','-','Templates'],
['PasteText','PasteWord'],
['Undo','Redo','Find','Replace'],
'/',
['RemoveFormat','Bold','Italic','Underline','StrikeThrough'],
['OrderedList','UnorderedList','Outdent','Indent'],
['JustifyLeft','JustifyCenter','JustifyRight','JustifyFull'],
['TextColor','BGColor'],
['Link','Unlink','Anchor'],
['Image','Flash','Table','Rule','Smiley'],
'/',
['Style','FontFormat','FontName','FontSize']
] ;
4. 上面FCKConfig.uploaded的值来自于fckeditor.rb。在fckeditor.rb中加入一句(如下_体所C)?br />
"oFCKeditor.BasePath = \"#{base_path}\"\n"+
"oFCKeditor.Config['CustomConfigurationsPath'] = '../../fckcustom.js';\n"+
"oFCKeditor.Config['uploaded'] = '#{options[:path]}';\n"+
"oFCKeditor.ReplaceTextarea();\n")
5.不过上面oFCKeditor.Config['uploaded']的D传到fckcustom.js的FCKConfig.uploaded里,q需要修改fckeditorcode_gecko.js和fckeditorcode_ie.jsQ这两个文g对javascriptq行了压~处理,修改h较难操作Q。我是参考了oFCKeditor.Config['CustomConfigurationsPath'] q个参数的蝲入实玎ͼ才找到这U鸟不生蛋的地方。搜索这两个文g的关键字CustomConfigurationsPathQ找到如下一行,然后加入一个else if判断(如下_体所C)?br />
6.最后在fckeditor.rb里的#{options[:path]}来自于我们前台的view了。如下粗体所C,把标准的fckeditor_textarea新增加了一个参敎ͼ其中params[:user_id]是把用户的ID值做?/span>目录名。这样就实现了动态改变FCKEditor的上传目录?/span>
修改完后需要重启WEB服务Q最后别忘记把public/javascripts/fckeditor和vendor/plugins/fckeditor/public/javascripts同步一下,原因见http://www.dentisthealthcenter.com/chengang/archive/2007/09/24/147867.html
]]>
在启动WEBrick( ruby script/server)Ӟ会自动把vendor/plugins/fckeditor/public/javascripts的内容复制到public/javascripts目录。因此如果你修改了FCKEditor的配|文件之后,需要把复制到public/javascripts目录的FCKEditor相关文g删除掉,然后再重启WEBrick。当Ӟ你也可以直接修改public/javascripts目录的FCKEditor的缓存的配置文gQ这样不必重启WEBrickQ就可以立即看到修改效果。不q徏议你在完成修改后Q同时也要更新vendor/plugins/fckeditor/public/javascripts下的配置文gQ毕竟public/javascripts里的应该是临时文g?br />
1.中文?br />
在fckcustom.js里加入两(_体昄Q?br />
FCKConfig.AutoDetectLanguage = false ;
FCKConfig.DefaultLanguage = 'zh-cn' ;
2. 定制FCKEditor的工h
修改fckcustom.js里的如下目Q增删改自便?br />
q里要注意一点,有些|上文章?span style="color: rgb(0, 0, 0);">:toolbarSet写成?/span>:toolbarKitQ这是错误的。如果你发现对工h的配|不起作用,那么要检查一下。正的写法如下Q?br />
]]>
#add by glchengang
key_hash = {}
if params.first.is_a?(Hash)
key_hash = params.first
params.delete_at(0)
end
#add end
options = params.last.is_a?(Hash) ? params.pop.symbolize_keys : {}
objects = params.collect {|object_name| instance_variable_get("@#{object_name}") }.compact
count = objects.inject(0) {|sum, object| sum + object.errors.count }
unless count.zero?
html = {}
[:id, :class].each do |key|
if options.include?(key)
value = options[key]
html[key] = value unless value.blank?
else
html[key] = 'errorExplanation'
end
end
# change by glchengang
header_message = "?{count}个错?/span>"
# header_message = "#{pluralize(count, 'error')} prohibited this #{(options[:object_name] || params.first).to_s.gsub('_', ' ')} from being saved"
#add by glchengang
error_messages = objects.map do |object|
temp = []
object.errors.each do |attr, msg|
temp << content_tag(:li, (key_hash[attr] || attr) + msg)
end
temp
end
#add end
# error_messages = objects.map {|object| object.errors.full_messages.map {|msg| content_tag(:li, msg) } }
content_tag(:div,
content_tag(options[:header_tag] || :h2, header_message) <<
# content_tag(:p, 'There were problems with the following fields:') <<
content_tag(:ul, error_messages),
html
)
else
''
end
end
使用依然兼容老的方式Q你也可以传入一个哈希表Q把模型字段昄成对应的中文Q示例如下:
h = {'username'=>'用户?/span>', 'password'=>'密码'}
error_messages_for h, :user
%>
errors[:taken] = '已经被?/span>'
errors[:blank] = '不能为空'
]]>
#-----------------------------------------
# will_paginate插g的ajax分页
#-----------------------------------------
@@pagination_options = { :class => 'pagination',
:prev_label => '上一?/span>',
:next_label => '下一?/span>',
:inner_window => 4, # links around the current page
:outer_window => 1, # links around beginning and end
:separator => ' ', # single space is friendly to spiders and non-graphic browsers
:param_name => :page,
#add by chengang
:update =>nil, #ajax所要更新的html元素的id
:url_suffix => '' #url的后~Q主要是Z补全REST所需要的url
#add end
}
mattr_reader :pagination_options
def will_paginate_remote(entries = @entries, options = {})
total_pages = entries.page_count
if total_pages > 1
options = options.symbolize_keys.reverse_merge(pagination_options)
page, param = entries.current_page, options.delete(:param_name)
inner_window, outer_window = options.delete(:inner_window).to_i, options.delete(:outer_window).to_i
#add by chengang
update = options.delete(:update)
suffix = options.delete(:url_suffix)
url = request.env['PATH_INFO']
url += suffix if suffix
#add end
min = page - inner_window
max = page + inner_window
if max > total_pages then min -= max - total_pages
elsif min < 1 then max += 1 - min
end
current = min..max
beginning = 1..(1 + outer_window)
tail = (total_pages - outer_window)..total_pages
visible = [beginning, current, tail].map(&:to_a).flatten.sort.uniq
links, prev = [], 0
visible.each do |n|
next if n < 1
break if n > total_pages
unless n - prev > 1
prev = n
#change by chengang
text = (n==page ? n : "[#{n}]")
links << page_link_remote_or_span((n != page ? n : nil), 'current', text, param, update, url)
else
prev = n - 1
links << ''
redo
end
end
#change by chengang
links.unshift page_link_remote_or_span(entries.previous_page, 'disabled', options.delete(:prev_label), param, update, url)
links.push page_link_remote_or_span(entries.next_page, 'disabled', options.delete(:next_label), param, update, url)
#change end
content_tag :div, links.join(options.delete(:separator)), options
end
end
protected
def page_link_remote_or_span(page, span_class, text, param, update, url)
unless page
content_tag :span, text, :class => span_class
else
link_to_remote text, :update => update, :url => "#{url}?#{param.to_sym}=#{page}", :method=>:get
end
end
在view中的使用如下所C?
]]>
环境Qubuntu linux 7.0.4 + ruby 1.8.5 + Rails 1.2.3 + FCKeditor 0.4.1
直接从它的subversion库里取得该Rails插g。先q入C的项目根目录Q再执行如下命o
其他说明Q?br />(1)你的linux必须先安装了subversion。(ubuntu里用新立得搜subversion卛_Q?br />(2)把命令中的install 改ؓ destoryQ可以删除安装?br />(3)我取到的?007q?月䆾最后更新的, v 0.4.1?br />(4)FCKeditor安装在项目根目录下的vendor/plugins/fckeditor
里,
(5)vendor/plugins/fckeditor里的README很值得一读,我碰到Ajax问题Q查了所以网上的中文资料都没有提刎ͼ在这个README却有?br />
用如下语句在面里含入它的javascript?br />
在需要富文本的Form表单用如下语句生成一个富文g~辑框:
说明Qtopic对应模型对象,content对应它的字段。也是要求当前要有@topicq个实例变量?br />
如果是用了ajaxQ则需要在form_remote_tag加上一个before?br />
:before => fckeditor_before_js('topic', 'content'),
:url => {:controller => 'topics', :action => 'create', :template => 'show'} )%>
q且富文件编辑框要加一个ajax=true的选项Q?br />
在上传囄的功能时到了错误。弹出出alert对话框,昄QError on file upload.Error number: 403
/var/lib/gems/1.8/gems/actionpack-1.13.3/lib/action_controller/routing.rb:1292:in `recognize_path'
/var/lib/gems/1.8/gems/actionpack-1.13.3/lib/action_controller/routing.rb:1282:in `recognize'
/var/lib/gems/1.8/gems/rails-1.2.3/lib/dispatcher.rb:40:in `dispatch'
/var/lib/gems/1.8/gems/rails-1.2.3/lib/webrick_server.rb:113:in `handle_dispatch'
/var/lib/gems/1.8/gems/rails-1.2.3/lib/webrick_server.rb:79:in `service'
/usr/lib/ruby/1.8/webrick/httpserver.rb:104:in `service'
/usr/lib/ruby/1.8/webrick/httpserver.rb:65:in `run'
/usr/lib/ruby/1.8/webrick/server.rb:173:in `start_thread'
/usr/lib/ruby/1.8/webrick/server.rb:162:in `start'
/usr/lib/ruby/1.8/webrick/server.rb:162:in `start_thread'
最后在http://blog.caronsoftware.com/articles/2006/12/03/fckeditor-0-4-0-released#comment-1745扑ֈ了答案,q是一个BUG
原来的
改ؓ
]]>