homeland 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. data/README.markdown +32 -0
  2. data/app/assets/images/reply.png +0 -0
  3. data/app/assets/javascripts/homeland.coffee +48 -0
  4. data/app/assets/stylesheets/homeland.scss +84 -0
  5. data/app/controllers/homeland/application_controller.rb +14 -0
  6. data/app/controllers/homeland/replies_controller.rb +20 -0
  7. data/app/controllers/homeland/topics_controller.rb +124 -0
  8. data/app/helpers/homeland/application_helper.rb +27 -0
  9. data/app/helpers/homeland/topics_helper.rb +8 -0
  10. data/app/models/homeland/node.rb +24 -0
  11. data/app/models/homeland/reply.rb +29 -0
  12. data/app/models/homeland/section.rb +17 -0
  13. data/app/models/homeland/topic.rb +43 -0
  14. data/app/views/homeland/replies/edit.html.erb +28 -0
  15. data/app/views/homeland/shared/_error_messages.html.erb +11 -0
  16. data/app/views/homeland/topics/_base.html.erb +4 -0
  17. data/app/views/homeland/topics/_form.html.erb +23 -0
  18. data/app/views/homeland/topics/_sidebar.html.erb +65 -0
  19. data/app/views/homeland/topics/_topic.html.erb +26 -0
  20. data/app/views/homeland/topics/edit.html.erb +11 -0
  21. data/app/views/homeland/topics/feed.builder +20 -0
  22. data/app/views/homeland/topics/index.html.erb +51 -0
  23. data/app/views/homeland/topics/new.html.erb +11 -0
  24. data/app/views/homeland/topics/show.html.erb +88 -0
  25. data/config/routes.rb +16 -0
  26. data/lib/generators/homeland/install/templates/initializer.rb +8 -0
  27. data/lib/generators/homeland/install/templates/javascripts/jquery.hotkeys.js +99 -0
  28. data/lib/generators/homeland/install/templates/javascripts/jquery.timeago.js +147 -0
  29. data/lib/generators/homeland/install/templates/locales/homeland.zh-CN.yml +14 -0
  30. data/lib/generators/homeland/install_generator.rb +45 -0
  31. data/lib/generators/homeland/views_generator.rb +23 -0
  32. data/lib/homeland.rb +6 -0
  33. data/lib/homeland/engine.rb +32 -0
  34. data/lib/homeland/setting.rb +37 -0
  35. metadata +91 -0
data/README.markdown ADDED
@@ -0,0 +1,32 @@
1
+ A new style forum for small communitys as a Rails Engine. you can mount this in you any apps.
2
+
3
+ ## Install
4
+
5
+ * You need install *Ruby 1.9.2*, *Rubygems* and *Rails 3.1* and *Mongoid* first.
6
+
7
+ 1. in you Gemfile:
8
+
9
+ gem "homeland"
10
+
11
+ 1. Install thought this commands:
12
+
13
+ rails g homeland:install
14
+
15
+ 2. change you application.html.erb
16
+
17
+ before:
18
+
19
+ <%= link_to "Home", root_path %>
20
+ <%= link_to "Posts", posts_path %>
21
+
22
+ after:
23
+
24
+ <%= link_to "Home", main_app.root_path %>
25
+ <%= link_to "Home", main_app.posts_path %>
26
+
27
+
28
+ ## Generate views to custom
29
+
30
+ rails g homeland:views
31
+
32
+ Thanks [V2EX](http://v2ex.com) forum idea.
Binary file
@@ -0,0 +1,48 @@
1
+ #= require jquery.hotkeys
2
+ #= require jquery.timeago
3
+ #= require_self
4
+ # TopicsController 下所有页面的 JS 功能
5
+ window.Topics =
6
+ # 往话题编辑器里面插入图片代码
7
+ appendImageFromUpload : (srcs) ->
8
+ txtBox = $(".topic_body_text_area")
9
+ for src in srcs
10
+ txtBox.val("#{txtBox.val()}[img]#{src}[/img]\n")
11
+ txtBox.focus()
12
+ $("#add_image").jDialog.close()
13
+
14
+ # 上传图片
15
+ addImageClick : () ->
16
+ opts =
17
+ title:"插入图片"
18
+ width: 350
19
+ height: 145
20
+ content: '<iframe src="/photos/tiny_new" frameborder="0" style="width:330px; height:145px;"></iframe>',
21
+ close_on_body_click : false
22
+
23
+ $("#add_image").jDialog(opts)
24
+ return false
25
+
26
+ # 回复
27
+ reply : (floor,login) ->
28
+ reply_body = $("#reply_body")
29
+ new_text = "##{floor}楼 @#{login} "
30
+ if reply_body.val().trim().length == 0
31
+ new_text += ''
32
+ else
33
+ new_text = "\n#{new_text}"
34
+ reply_body.focus().val(reply_body.val() + new_text)
35
+ return false
36
+
37
+ # 高亮楼层
38
+ hightlightReply : (floor) ->
39
+ $("#replies .reply").removeClass("light")
40
+ $("#reply"+floor).addClass("light")
41
+
42
+ # Page.ready
43
+ $(document).ready ->
44
+ $("abbr.timeago").timeago()
45
+ $("textarea").bind "keydown","ctrl+return",(el) ->
46
+ if $(el.target).val().trim().length > 0
47
+ $("#reply form").submit()
48
+
@@ -0,0 +1,84 @@
1
+ .topics .topic .title a:link,
2
+ .topics .topic .title a:visited,
3
+ .node_topics .title a:link,
4
+ .node_topics .title a:visited {color:#2c64d7; text-decoration: none;}
5
+ .topics .topic .title a:hover,
6
+ .node_topics .title a:hover { text-decoration: underline; }
7
+
8
+ .sidebar p { margin-bottom:8px;}
9
+
10
+ .topics { }
11
+ .topics .topic { }
12
+ .topics .topic_line {border-top:1px solid #DDD; padding:10px 0; margin:0 -10px; vertical-align:top; }
13
+ #main .topics .topic h1 { font-size:22px; color:#333; margin-top:6px; text-align: left; line-height:100%;margin-bottom:8px;}
14
+ .topics .topic .avatar_large { width:80px; }
15
+ .topics .topic .avatar { width:56px; margin-left:10px; }
16
+ .topics .topic .infos { padding-right:90px;}
17
+ .topics .topic .right_info { padding-left:66px;}
18
+ .topics .topic .title { font-size:14px; }
19
+ .topics .topic .info { color:#999; margin-bottom:2px; }
20
+ .topics .topic .info a:link,
21
+ .topics .topic .info a:visited { color:#999; text-decoration: underline; }
22
+ .topics .body { line-height:160%; font-size:14px; margin-top:8px;}
23
+ .topics .body p { margin-bottom:10px; }
24
+ .topics .tools { text-align:right; }
25
+ .topics .tools a {margin-left:6px;}
26
+ .topics .topic .replies { width:70px; margin-right:10px; text-align:right; vertical-align:middle; }
27
+ .topics .topic .replies a.count,
28
+ .topics .topic .replies a.count:visited { line-height: 12px; font-weight: bold; color: white;
29
+ background-color: #1c7fdb; display: inline-block;
30
+ padding: 2px 10px 2px 10px;
31
+ -moz-border-radius: 12px; -webkit-border-radius: 12px;
32
+ margin: 18px 12px 0px 0px; text-decoration: none; }
33
+ .topics .topic .replies a.count:hover { color:write; background-color:#1F8AEE; }
34
+ .topics .topic .replies a.state0:link,
35
+ .topics .topic .replies a.state0:visited { background:#DDD; }
36
+ .topics .topic .replies a.state0:hover { background:#1dc115;}
37
+ .topics .topic .replies a.state2:link,
38
+ .topics .topic .replies a.state2:visited { background:#22D419; }
39
+ .topics .topic .replies a.state2:hover {background:#1dc115; }
40
+ .topics tr.topic td.replies { text-align:right; width:50px; font-size:14px; color:green;font-weight:bold; }
41
+ .more { padding:8px; border-top:1px solid #DDD; font-size:14px; text-align:right; }
42
+
43
+ /* topics list in node page */
44
+ .node_topics { margin-bottom:16px;}
45
+ .node_topics tr td { padding:8px; }
46
+ .node_topics tr.head td { padding-top:14px; color:#CCC; font-weight:bold; font-size:12px; }
47
+ .node_topics tr.topic td.title { font-size:14px; font-weight:bold; }
48
+ .node_topics tr.topic td.author { width:80px;}
49
+ .node_topics tr.topic td.author a { color:#666; font-weight:bold; }
50
+
51
+ .no_result { margin-bottom:20px; text-align:center; }
52
+
53
+ #node_info { padding:8px;margin-bottom:8px;padding-top:0px; }
54
+ #node_info h1 { font-size:18px; color:000; display:inline; margin-right:5px; }
55
+ #node_info p { margin-bottom:8px; }
56
+ #node_info p.summary { color:#999; }
57
+
58
+ #replies {}
59
+ #replies .total { padding-bottom:6px; color:#999; }
60
+ #replies .reply { border-top:1px solid #DDD; padding:8px 10px; margin:0 -10px;}
61
+ #replies .reply.light { background:#EFFCEE;}
62
+ #replies .reply .face { padding-right:8px; width:48px; margin-left:0; }
63
+ #replies .reply .infos { padding-left:58px;}
64
+ #replies .reply .info { margin-top:4px; color:#999;height:20px;}
65
+ #replies .reply .info a:link,
66
+ #replies .reply .info a:visited { color:#999; text-decoration: underline;}
67
+ #replies .reply .info .name { float:left; }
68
+ #replies .reply .info .time { float:right; text-align:right; }
69
+ #replies .reply .info a.reply_link img { vertical-align:middle;}
70
+ #replies .reply .body { line-height:180%; font-size:14px; text-overflow: ellipsis;}
71
+ #replies .reply .body img { max-width:622px;}
72
+
73
+ #reply.form textarea { width:578px; }
74
+
75
+ .edit_tools { text-align:right; margin-bottom:3px;}
76
+
77
+ .totals ul li { line-height:150%;}
78
+
79
+ #topic_new_tip li { margin-bottom:6px;}
80
+
81
+ .body a.at_floor { color:#376B43; }
82
+ .body a.at_user { }
83
+
84
+ .topic_body_text_area { width:600px;}
@@ -0,0 +1,14 @@
1
+ # coding: utf-8
2
+ class Homeland::ApplicationController < ApplicationController
3
+ helper_method :current_user
4
+
5
+ alias_method :origin_current_user, Homeland.current_user_method.to_sym
6
+ def current_user
7
+ origin_current_user
8
+ end
9
+
10
+ def homeland_require_user
11
+ self.send(Homeland.require_user_method)
12
+ end
13
+
14
+ end
@@ -0,0 +1,20 @@
1
+ # coding: utf-8
2
+ module Homeland
3
+ class RepliesController < Homeland::ApplicationController
4
+ before_filter :homeland_require_user
5
+
6
+ def edit
7
+ @reply = current_user.replies.find(params[:id])
8
+ end
9
+
10
+ def update
11
+ @reply = current_user.replies.find(params[:id])
12
+
13
+ if @reply.update_attributes(params[:reply])
14
+ redirect_to(topic_path(@reply.topic_id), :notice => '回帖删除成功.')
15
+ else
16
+ render :action => "edit"
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,124 @@
1
+ # coding: utf-8
2
+ module Homeland
3
+ class TopicsController < Homeland::ApplicationController
4
+ before_filter :homeland_require_user, :only => [:new,:edit,:create,:update,:destroy,:reply]
5
+ before_filter :init_list_sidebar, :only => [:index,:recent,:show,:cate,:search]
6
+ caches_page :feed, :expires_in => 1.hours
7
+
8
+ private
9
+ def init_list_sidebar
10
+ if !fragment_exist? "topic/init_list_sidebar/hot_nodes"
11
+ @hot_nodes = Node.hots.limit(20)
12
+ end
13
+ end
14
+
15
+ public
16
+ # GET /topics
17
+ # GET /topics.xml
18
+ def index
19
+ @topics = Topic.last_actived.limit(10)
20
+ @sections = Section.all
21
+
22
+ end
23
+
24
+ def feed
25
+ @topics = Topic.recent.limit(20)
26
+ response.headers['Content-Type'] = 'application/rss+xml'
27
+ render :layout => false
28
+ end
29
+
30
+ def node
31
+ @node = Node.find(params[:id])
32
+ @topics = @node.topics.last_actived.paginate(:page => params[:page],:per_page => 50)
33
+
34
+ render :action => "index"
35
+ end
36
+
37
+ def recent
38
+ @topics = Topic.recent.limit(50)
39
+
40
+ render :action => "index"
41
+ end
42
+
43
+ def search
44
+ result = Redis::Search.query("Topic", params[:key], :limit => 500)
45
+ ids = result.collect { |r| r["id"] }
46
+ @topics = Topic.find(ids).paginate(:page => params[:page], :per_page => 20)
47
+
48
+ render :action => "index"
49
+ end
50
+
51
+ def show
52
+ @topic = Topic.find(params[:id])
53
+ @topic.hit!
54
+ @node = @topic.node
55
+ @replies = @topic.replies.all
56
+
57
+ end
58
+
59
+ # GET /topics/new
60
+ # GET /topics/new.xml
61
+ def new
62
+ @topic = Topic.new
63
+ @topic.node_id = params[:node]
64
+ @node = Node.find(params[:node])
65
+ end
66
+
67
+ def reply
68
+ @topic = Topic.find(params[:id])
69
+ @reply = @topic.replies.build(params[:reply])
70
+ @reply.user_id = current_user.id
71
+ if @reply.save
72
+ flash[:notice] = "回复成功。"
73
+ else
74
+ flash[:notice] = @reply.errors.full_messages.join("<br />")
75
+ end
76
+ redirect_to topic_path(params[:id],:anchor => 'reply')
77
+ end
78
+
79
+ # GET /topics/1/edit
80
+ def edit
81
+ @topic = current_user.topics.find(params[:id])
82
+ @node = @topic.node
83
+ end
84
+
85
+ # POST /topics
86
+ # POST /topics.xml
87
+ def create
88
+ pt = params[:topic]
89
+ @topic = Topic.new(pt)
90
+ @topic.user_id = current_user.id
91
+ @topic.node_id = params[:node] || pt[:node_id]
92
+
93
+ if @topic.save
94
+ redirect_to(topic_path(@topic.id), :notice => '帖子创建成功.')
95
+ else
96
+ render :action => "new"
97
+ end
98
+ end
99
+
100
+ # PUT /topics/1
101
+ # PUT /topics/1.xml
102
+ def update
103
+ @topic = current_user.topics.find(params[:id])
104
+ pt = params[:topic]
105
+ @topic.node_id = pt[:node_id]
106
+ @topic.title = pt[:title]
107
+ @topic.body = pt[:body]
108
+
109
+ if @topic.save
110
+ redirect_to(topic_path(@topic.id), :notice => '帖子修改成功.')
111
+ else
112
+ render :action => "edit"
113
+ end
114
+ end
115
+
116
+ # DELETE /topics/1
117
+ # DELETE /topics/1.xml
118
+ def destroy
119
+ @topic = current_user.topics.find(params[:id])
120
+ @topic.destroy
121
+ redirect_to(topics_path, :notice => '帖子删除成功.')
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ module Homeland
3
+ module ApplicationHelper
4
+ def owner?(item)
5
+ return false if item.blank?
6
+ return if current_user.blank?
7
+ item.user_id == current_user.id
8
+ end
9
+
10
+ def timeago(time, options = {})
11
+ options[:class] ||= "timeago"
12
+ content_tag(:abbr, time.to_s, options.merge(:title => time.getutc.iso8601)) if time
13
+ end
14
+
15
+ def user_name_tag(user,options = {})
16
+ return "" if user.blank?
17
+ link_to_if(Homeland.user_path_method, user.name, [main_app, user])
18
+ end
19
+
20
+ def user_avatar_tag(user)
21
+ return "" if user.blank?
22
+ img = image_tag(user.try(Homeland.user_avatar_method.to_sym))
23
+ link_to_if(Homeland.user_path_method, raw(img), [main_app, user])
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,8 @@
1
+ # coding: utf-8
2
+ module Homeland
3
+ module TopicsHelper
4
+ def format_topic_body(text,title = "",allow_image = true)
5
+ auto_link(simple_format(text),:all, :target => '_blank', :rel => "nofollow")
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ module Homeland
3
+ class Node
4
+ include Mongoid::Document
5
+ include Mongoid::Timestamps
6
+
7
+ field :name
8
+ field :summary
9
+ field :sort, :type => Integer, :default => 0
10
+ field :topics_count, :type => Integer, :default => 0
11
+
12
+ has_many :topics, :class_name => "Homeland::Topic"
13
+ belongs_to :section, :class_name => "Homeland::Section"
14
+
15
+ validates_presence_of :name, :summary
16
+ validates_uniqueness_of :name
17
+
18
+ index :topics_count
19
+ index :sort
20
+
21
+ scope :hots, desc(:topics_count)
22
+ scope :sorted, desc(:sort)
23
+ end
24
+ end
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ module Homeland
3
+ class Reply
4
+ include Mongoid::Document
5
+ include Mongoid::Timestamps
6
+ include Mongoid::Paranoia
7
+
8
+ field :body
9
+
10
+ belongs_to :user, :inverse_of => :replies, :class_name => Homeland.user_class.to_s
11
+ belongs_to :topic, :inverse_of => :replies, :class_name => "Homeland::Topic"
12
+
13
+ attr_protected :user_id, :topic_id
14
+
15
+ index :topic_id
16
+ index :user_id
17
+
18
+ validates_presence_of :body
19
+ scope :recent, desc(:_id)
20
+
21
+ after_create :update_parent_last_replied
22
+ def update_parent_last_replied
23
+ self.topic.replied_at = Time.now
24
+ self.topic.last_reply_user_id = self.user_id
25
+ self.topic.replies_count = self.topic.replies.count
26
+ self.topic.save
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,17 @@
1
+ # coding: utf-8
2
+ module Homeland
3
+ class Section
4
+ include Mongoid::Document
5
+ include Mongoid::Timestamps
6
+
7
+ field :name
8
+ field :sort, :type => Integer, :default => 0
9
+ has_many :nodes, :dependent => :destroy, :class_name => "Homeland::Node"
10
+
11
+ validates_presence_of :name
12
+ validates_uniqueness_of :name
13
+
14
+
15
+ default_scope desc(:sort)
16
+ end
17
+ end