homeland 0.0.9 → 1.0.0.beta2

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 (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 %>