homeland 0.0.9 → 1.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +139 -43
  4. data/Rakefile +23 -0
  5. data/app/assets/javascripts/homeland/application.coffee +31 -0
  6. data/app/assets/javascripts/homeland/bootstrap.min.js +7 -0
  7. data/{lib/generators/homeland/install/templates/javascripts → app/assets/javascripts/homeland}/jquery.timeago.js +13 -13
  8. data/app/assets/javascripts/homeland/tether.min.js +1 -0
  9. data/app/assets/stylesheets/homeland/application.scss +114 -0
  10. data/app/assets/stylesheets/homeland/bootstrap-theme.scss +165 -0
  11. data/app/assets/stylesheets/homeland/bootstrap.min.css +5 -0
  12. data/app/assets/stylesheets/homeland/markdown.scss +13 -0
  13. data/app/assets/stylesheets/homeland/tether.min.css +1 -0
  14. data/app/controllers/homeland/application_controller.rb +37 -12
  15. data/app/controllers/homeland/replies_controller.rb +25 -11
  16. data/app/controllers/homeland/topics_controller.rb +50 -58
  17. data/app/helpers/homeland/application_helper.rb +45 -13
  18. data/app/models/homeland/concerns/markdown_body.rb +17 -0
  19. data/app/models/homeland/concerns/pagination.rb +14 -0
  20. data/app/models/homeland/concerns/soft_delete.rb +29 -0
  21. data/app/models/homeland/concerns/user_methods.rb +23 -0
  22. data/app/models/homeland/node.rb +10 -25
  23. data/app/models/homeland/reply.rb +16 -17
  24. data/app/models/homeland/topic.rb +19 -29
  25. data/app/views/homeland/replies/_reply.html.erb +35 -0
  26. data/app/views/homeland/replies/edit.html.erb +17 -27
  27. data/app/views/homeland/shared/_error_messages.html.erb +2 -4
  28. data/app/views/homeland/topics/_form.html.erb +21 -24
  29. data/app/views/homeland/topics/_node_info.html.erb +9 -0
  30. data/app/views/homeland/topics/_reply_form.html.erb +16 -0
  31. data/app/views/homeland/topics/_topic.html.erb +17 -26
  32. data/app/views/homeland/topics/edit.html.erb +6 -10
  33. data/app/views/homeland/topics/index.html.erb +28 -51
  34. data/app/views/homeland/topics/new.html.erb +5 -10
  35. data/app/views/homeland/topics/show.html.erb +16 -89
  36. data/app/views/layouts/homeland/application.html.erb +42 -0
  37. data/config/locales/homeland.en.yml +40 -0
  38. data/config/locales/homeland.zh-CN.yml +8 -0
  39. data/config/routes.rb +6 -8
  40. data/db/migrate/20160321125035_create_homeland_nodes.rb +15 -0
  41. data/db/migrate/20160321125610_create_homeland_topics.rb +25 -0
  42. data/db/migrate/20160321125920_create_homeland_replies.rb +16 -0
  43. data/db/migrate/20160322062228_add_reply_to_id_to_replies.rb +7 -0
  44. data/lib/generators/homeland/controllers_generator.rb +14 -0
  45. data/lib/generators/homeland/i18n_generator.rb +22 -0
  46. data/lib/generators/homeland/install_generator.rb +8 -54
  47. data/lib/generators/homeland/templates/config/initializers/homeland.rb +34 -0
  48. data/lib/generators/homeland/views_generator.rb +3 -10
  49. data/lib/homeland.rb +29 -3
  50. data/lib/homeland/action_view/will_paginate.rb +38 -0
  51. data/lib/homeland/configuration.rb +66 -0
  52. data/lib/homeland/engine.rb +4 -27
  53. data/lib/homeland/markup.rb +17 -0
  54. data/lib/homeland/markup/base.rb +11 -0
  55. data/lib/homeland/markup/html.rb +12 -0
  56. data/lib/homeland/markup/markdown.rb +17 -0
  57. data/lib/homeland/markup/simple.rb +14 -0
  58. data/lib/homeland/version.rb +3 -0
  59. data/lib/tasks/homeland_tasks.rake +4 -0
  60. metadata +119 -38
  61. data/Changelogs.md +0 -29
  62. data/app/assets/images/reply.png +0 -0
  63. data/app/assets/javascripts/homeland.coffee +0 -51
  64. data/app/assets/stylesheets/homeland.scss +0 -100
  65. data/app/helpers/homeland/topics_helper.rb +0 -14
  66. data/app/models/homeland/section.rb +0 -21
  67. data/app/views/homeland/topics/_base.html.erb +0 -4
  68. data/app/views/homeland/topics/_sidebar.html.erb +0 -49
  69. data/app/views/homeland/topics/feed.builder +0 -20
  70. data/lib/generators/homeland/install/templates/images/jquery.chosen.png +0 -0
  71. data/lib/generators/homeland/install/templates/initializer.rb +0 -8
  72. data/lib/generators/homeland/install/templates/javascripts/jquery.chosen.js +0 -898
  73. data/lib/generators/homeland/install/templates/javascripts/jquery.hotkeys.js +0 -99
  74. data/lib/generators/homeland/install/templates/locales/homeland.zh-CN.yml +0 -14
  75. data/lib/generators/homeland/install/templates/stylesheets/jquery.chosen.scss +0 -367
  76. data/lib/homeland/setting.rb +0 -37
@@ -1,10 +1,11 @@
1
- # coding: utf-8
2
1
  module Homeland
3
2
  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
3
+ DEFAULT_AVATAR = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPAAAADwCAMAAAAJixmgAAAAFVBMVEWkpKSnp6eqqqq3t7fS0tLV1dXZ2dmshcKEAAAAtklEQVR4Ae3XsRGAAAjAQFRk/5HtqaTz5H+DlInvAQAAAAAAAAAAAAAAAAAAAACymiveO6o7BQsWLFiwYMGCBS8PFixYsGDBggULFixYsGDBggULFixYsGDBggULFixYsGDBc4IFCxYsWLBgwYIFC14ZfOeAPRQ8IliwYMGCBQsWLFiwYMGCBQsWLFiwYMGCBQsWLFiwYMGCBQsWLFiwYMGCBQv+JQAAAAAAAAAAAAAAAAAAAOAB4KJfdHmj+kwAAAAASUVORK5CYII='
4
+
5
+ def render_errors(target)
6
+ if target.errors.present?
7
+ render partial: '/homeland/shared/error_messages', locals: { target: target }
8
+ end
8
9
  end
9
10
 
10
11
  def timeago(time, options = {})
@@ -12,16 +13,47 @@ module Homeland
12
13
  content_tag(:abbr, time.to_s, options.merge(:title => time.getutc.iso8601)) if time
13
14
  end
14
15
 
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])
16
+ def node_tag(node)
17
+ return '' if node.blank?
18
+
19
+ label = [node.badge_html, content_tag(:span, node.name, class: 'name')].join(' ')
20
+
21
+ link_to raw(label), homeland.node_topics_path(node.id), class: 'node'
18
22
  end
19
23
 
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
+ def user_name_tag(obj)
25
+ return '' if obj.blank?
26
+ return '' if obj.user_name.blank?
27
+
28
+ link_to obj.user_name, obj.user_profile_url, class: 'user-name'
24
29
  end
25
30
 
31
+ def user_avatar_tag(obj, opts = {})
32
+ default = image_tag(DEFAULT_AVATAR, class: 'avatar media-object')
33
+ return default if obj.blank?
34
+ return default if obj.user.blank?
35
+
36
+ if obj.user_avatar_url == nil
37
+ img = default
38
+ else
39
+ img = image_tag(obj.user_avatar_url, class: 'avatar media-object')
40
+ end
41
+
42
+ link_to img, obj.user_profile_url, title: obj.user_name, class: 'user-avatar'
43
+ end
44
+
45
+ def notice_message
46
+ flash_messages = []
47
+
48
+ flash.each do |type, message|
49
+ type = :danger
50
+ type = :success if type.to_sym == :notice
51
+ close_button = raw %(<button type="button" class="close" data-dismiss="alert">×</button>)
52
+ text = content_tag(:div, close_button + message, class: "alert alert-dismissible alert-#{type}")
53
+ flash_messages << text if message
54
+ end
55
+
56
+ flash_messages.join("\n").html_safe
57
+ end
26
58
  end
27
- end
59
+ end
@@ -0,0 +1,17 @@
1
+ module Homeland
2
+ module Concerns
3
+ module MarkdownBody
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ before_save :render_markup
8
+ end
9
+
10
+ def render_markup
11
+ if self.body_changed?
12
+ self.body_html = Homeland::Markup.render(self.body)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,14 @@
1
+ require 'will_paginate'
2
+ require 'will_paginate/active_record'
3
+
4
+ module Homeland
5
+ module Concerns
6
+ module Pagination
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ self.per_page = Homeland.config.per_page
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,29 @@
1
+ module Homeland
2
+ module Concerns
3
+ module SoftDelete
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ default_scope -> { where(deleted_at: nil) }
8
+
9
+ alias_method :destroy!, :destroy
10
+ end
11
+
12
+ def destroy
13
+ run_callbacks(:destroy) do
14
+ if persisted?
15
+ t = Time.now.utc
16
+ update_columns(deleted_at: t, updated_at: t)
17
+ end
18
+
19
+ @destroyed = true
20
+ end
21
+ freeze
22
+ end
23
+
24
+ def deleted?
25
+ deleted_at.present?
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,23 @@
1
+ module Homeland
2
+ module Concerns
3
+ module UserMethods
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ end
8
+
9
+ %w(user_name user_avatar_url user_profile_url).each do |method|
10
+ define_method(method) do
11
+ user_method = Homeland.config.send([method, "method"].join("_"))
12
+ return nil if user_method.blank?
13
+ return nil if self.user.blank?
14
+ self.user.send(user_method)
15
+ end
16
+ end
17
+
18
+ def user_admin?
19
+ @user_admin ||= self.user && self.user.send(Homeland.config.user_admin_method) || false
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,31 +1,16 @@
1
- # coding: utf-8
2
1
  module Homeland
3
- class Node
4
- include Mongoid::Document
5
- include Mongoid::Timestamps
2
+ class Node < ActiveRecord::Base
3
+ validates :name, :description, presence: true
4
+ validates :name, uniqueness: true
6
5
 
7
- field :name
8
- field :summary
9
- field :sort, :type => Integer, :default => 0
10
- field :topics_count, :type => Integer, :default => 0
6
+ has_many :topics, class_name: 'Homeland::Topic'
11
7
 
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 => 1
19
- index :sort => -1
20
-
21
- scope :hots, desc(:topics_count)
22
- scope :sorted, desc(:sort)
8
+ def badge_color
9
+ self.color || '#AAA'
10
+ end
23
11
 
24
- # 热门节电给 select 用的
25
- def self.node_collection
26
- Rails.cache.fetch("node:node_collection:#{CacheVersion.section_node_updated_at}") do
27
- Node.all.collect { |n| [n.name,n.id] }
28
- end
12
+ def badge_html
13
+ %(<i class="node-badge" style="background: #{self.badge_color};"></i>)
29
14
  end
30
15
  end
31
- end
16
+ end
@@ -1,29 +1,28 @@
1
- # coding: utf-8
2
1
  module Homeland
3
- class Reply
4
- include Mongoid::Document
5
- include Mongoid::Timestamps
6
- include Mongoid::Paranoia
2
+ class Reply < ActiveRecord::Base
3
+ include Homeland::Concerns::SoftDelete
4
+ include Homeland::Concerns::MarkdownBody
5
+ include Homeland::Concerns::UserMethods
6
+ include Homeland::Concerns::Pagination
7
7
 
8
- field :body
8
+ belongs_to :user, class_name: Homeland.config.user_class.to_s
9
+ belongs_to :topic, class_name: 'Homeland::Topic'
10
+ belongs_to :reply_to, class_name: 'Homeland::Reply'
9
11
 
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
+ has_many :replies, class_name: "Homeland::Reply", foreign_key: "reply_to_id"
12
13
 
13
- attr_protected :user_id, :topic_id
14
+ validates :user_id, :body, :topic_id, presence: true
14
15
 
15
- index :topic_id => 1
16
- index :user_id => 1
16
+ scope :recent, -> { order('id desc') }
17
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
18
+ after_commit :update_topic_last_reply_at, on: [:create, :update]
19
+ def update_topic_last_reply_at
20
+ return if self.topic.blank?
23
21
  self.topic.replied_at = Time.now
22
+ self.topic.last_active_mark = Time.now.to_i
24
23
  self.topic.last_reply_user_id = self.user_id
25
24
  self.topic.replies_count = self.topic.replies.count
26
25
  self.topic.save
27
26
  end
28
27
  end
29
- end
28
+ end
@@ -1,38 +1,28 @@
1
- # coding: utf-8
2
1
  module Homeland
3
- class Topic
4
- include Mongoid::Document
5
- include Mongoid::Timestamps
6
- include Mongoid::Paranoia
2
+ class Topic < ActiveRecord::Base
3
+ include Homeland::Concerns::SoftDelete
4
+ include Homeland::Concerns::MarkdownBody
5
+ include Homeland::Concerns::UserMethods
6
+ include Homeland::Concerns::Pagination
7
7
 
8
- field :title
9
- field :body
10
- field :replied_at , :type => DateTime
11
- field :replies_count, :type => Integer, :default => 0
8
+ belongs_to :user, class_name: Homeland.config.user_class.to_s
9
+ belongs_to :last_reply_user, class_name: Homeland.config.user_class.to_s
10
+ belongs_to :node, class_name: 'Homeland::Node'
11
+ has_many :replies, class_name: 'Homeland::Reply'
12
12
 
13
- belongs_to :user, :inverse_of => :topics, :class_name => Homeland.user_class.to_s
14
- belongs_to :node, :class_name => "Homeland::Node"
15
- belongs_to :last_reply_user, :class_name => Homeland.user_class.to_s
16
- has_many :replies, :class_name => "Homeland::Reply"
13
+ validates :user_id, :title, :body, :node_id, presence: true
17
14
 
18
- attr_protected :user_id
19
- validates_presence_of :user_id, :title, :body, :node_id
15
+ scope :recent, -> { order('id desc') }
16
+ scope :latest, -> { order('last_active_mark desc, id desc') }
17
+ scope :features, -> { where('replies_count >= 20').latest }
20
18
 
21
- index :replied_at => -1
22
- index :user_id => 1
23
- index :node_id => 1
24
-
25
- # scopes
26
- scope :last_actived, desc(:replied_at).desc(:_id)
27
- scope :recent, desc(:_id)
28
- before_save :set_replied_at
29
- def set_replied_at
30
- self.replied_at = Time.now
19
+ before_create :set_last_active_mark
20
+ def set_last_active_mark
21
+ self.last_active_mark = Time.now.to_i
31
22
  end
32
23
 
33
- def node_name
34
- return "" if self.node.blank?
35
- self.node.name
24
+ def activity_at
25
+ self.replied_at || self.updated_at || self.created_at
36
26
  end
37
27
  end
38
- end
28
+ end
@@ -0,0 +1,35 @@
1
+ <%
2
+ is_reply = reply.respond_to?(:reply_to_id)
3
+ %>
4
+ <div class="reply media" id="reply-<%= reply.id %>" data-id="<%= reply.id %>">
5
+ <div class="media-left">
6
+ <%= user_avatar_tag(reply) %>
7
+ </div>
8
+ <div class="media-body">
9
+ <div class="media-heading">
10
+ <%= user_name_tag(reply) %>
11
+
12
+ <span class="date pull-xs-right"><i class="fa fa-clock-o"></i> <%= timeago reply.created_at %></span>
13
+ </div>
14
+
15
+ <div class="markdown">
16
+ <%= raw reply.body_html %>
17
+ </div>
18
+
19
+ <div class="media-footer clearfix">
20
+ <% if owner?(reply) %>
21
+ <a href="<%= is_reply ? edit_reply_path(reply) : edit_topic_path(reply) %>" class="btn-opt"><i class="fa fa-pencil"></i></a>
22
+ <a href="<%= is_reply ? reply_path(reply) : topic_path(reply) %>" data-method="DELETE" data-confirm="<%= t('homeland.are_you_sure_to_delete') %>" class="btn-opt"><i class="fa fa-trash"></i></a>
23
+ <% end %>
24
+
25
+ <% if is_reply && reply.reply_to_id %>
26
+ <a href="#reply-<%= reply.reply_to_id %>" title="<%= t('homeland.topics.reply_to') %>" class="btn-opt"><i class="fa fa-quote-left"></i></a>
27
+ <% end %>
28
+
29
+ <span class="pull-xs-right">
30
+ <a href="<%= homeland.topic_path(@topic, anchor: "reply-#{reply.id}") %>" class="btn-opt anchor"><i class="fa fa-link"></i></a>
31
+ <a href="#" class="btn-opt" data-action="reply" data-reply-id="<%= is_reply ? reply.id : '' %>"><i class="fa fa-reply"></i> <%= t('homeland.reply') %></a>
32
+ </span>
33
+ </div>
34
+ </div>
35
+ </div>
@@ -1,28 +1,18 @@
1
- <% content_for :breadcrumb do %>
2
- <span class="current">修改回帖</span>
3
- <% end %>
4
- <div class="sidebar">
5
- <div class="box">
6
- 回帖所在文章:<br />
7
- <a href="<%= topic_path(@reply.topic_id) %>"><%= @reply.topic.title %></a>
1
+ <ol class="breadcrumb">
2
+ <li><a href="<%= homeland.root_path %>"><%= t('homeland.nav.home') %></a></li>
3
+ <li><a href="<%= homeland.topic_path(@reply.topic_id) %>" title="<%= @reply.topic.title %>"><%= truncate @reply.topic.title, length: 20 %></a></li>
4
+ <li class="active"><%= t('homeland.nav.edit') %></li>
5
+ </ol>
6
+ <%= form_for(@reply) do |f| %>
7
+ <%= render_errors @reply %>
8
+
9
+ <div class="form-group">
10
+ <%= f.label :body %>
11
+ <%= f.text_area :body, class: "form-control", rows: "10" %>
12
+ </div>
13
+
14
+ <div class="form-group">
15
+ <button type="submit" class="btn btn-primary btn-lg" data-disable-with="<%= t('homeland.submiting') %>"><%= t('homeland.submit') %></button>
16
+ <a href="<%= homeland.topic_path(@reply.topic_id) %>" class="btn btn-lg btn-default"><%= t('homeland.cancel') %></a>
8
17
  </div>
9
- </div>
10
- <div class="content">
11
- <div class="box">
12
- <%= form_for(@reply) do |f| %>
13
- <fieldset>
14
- <legend>修改回帖</legend>
15
- <%= render "homeland/shared/error_messages", :target => @reply %>
16
- <div class="clearfix">
17
- <%= f.label :body %>
18
- <div class="input">
19
- <%= f.text_area :body, :class => "topic_body_text_area", :rows => "20" %>
20
- </div>
21
- </div>
22
- <div class="actions">
23
- <button class="btn primary">保存更新</button>
24
- <a href="<%= topic_path(@reply.topic_id) %>" class="btn">取消</a>
25
- </div>
26
- <% end %>
27
- </div>
28
- </div>
18
+ <% end %>
@@ -1,7 +1,5 @@
1
- <% if target.errors.any? %>
2
- <div class="alert-message block-message error">
3
- <a class="close" href="#">×</a>
4
- <p><strong>有 <%= target.errors.count %> 处问题导至无法提交:</strong></p>
1
+ <% if target.errors.any? %>
2
+ <div class="alert alert-danger">
5
3
  <ul>
6
4
  <% target.errors.full_messages.each do |msg| %>
7
5
  <li><%= msg %></li>
@@ -1,26 +1,23 @@
1
1
  <%= form_for(@topic) do |f| %>
2
- <fieldset>
3
- <legend><%= @topic.id.blank? ? "新建帖子" : "修改帖子" %></legend>
4
- <%= render "homeland/shared/error_messages", :target => @topic %>
5
- <%= f.hidden_field :node_id %>
6
- <div class="clearfix">
7
- <%= f.label :title %>
8
- <div class="input">
9
- <span class="span2" style="margin-left:0px;margin-right:10px;">
10
- <%= render_topic_node_select_tag(@topic, :style => "width:140px;") %>
11
- </span>
12
- <%= f.text_field :title, :class => "span6" %>
13
- </div>
14
- </div>
15
- <div class="clearfix">
16
- <%= f.label :body %>
17
- <div class="input">
18
- <%= f.text_area :body, :class => "topic_body_text_area", :style => "height:200px" %>
19
- </div>
20
- </div>
21
- <div class="actions">
22
- <input type="submit" value="保存" class="btn btn-primary">
23
- <a href="<%= @topic.id.blank? ? topics_path : topic_path(@topic) %>" class="btn">取消</a>
24
- </div>
25
- </fieldset>
2
+ <%= render_errors @topic %>
3
+ <%= f.hidden_field :node_id %>
4
+ <div class="form-group">
5
+ <%= f.label :node %>
6
+ <%= f.select :node_id, Homeland::Node.all.collect { |n| [n.name, n.id] }, {}, { class: 'form-control' } %>
7
+ </div>
8
+
9
+ <div class="form-group">
10
+ <%= f.label :title %>
11
+ <%= f.text_field :title, class: "form-control" %>
12
+ </div>
13
+
14
+ <div class="form-group">
15
+ <%= f.label :body %>
16
+ <%= f.text_area :body, class: "form-control", rows: "10" %>
17
+ </div>
18
+
19
+ <div class="form-group">
20
+ <button type="submit" class="btn btn-primary btn-lg" data-disable-with="<%= t('homeland.submiting') %>"><%= t('homeland.submit') %></button>
21
+ <a href="<%= @topic.id.blank? ? homeland.topics_path : homeland.topic_path(@topic) %>" class="btn btn-lg btn-default"><%= t('homeland.cancel') %></a>
22
+ </div>
26
23
  <% end %>