weixin_pam 0.0.1

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 (114) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +165 -0
  4. data/Rakefile +37 -0
  5. data/app/assets/javascripts/jquery-sortable.js +699 -0
  6. data/app/assets/javascripts/weixin_pam/application.js +30 -0
  7. data/app/assets/javascripts/weixin_pam/diymenus.js +90 -0
  8. data/app/assets/javascripts/weixin_pam/public_accounts.js +2 -0
  9. data/app/assets/javascripts/weixin_pam/user_accounts.js +23 -0
  10. data/app/assets/stylesheets/weixin_pam/application.scss +31 -0
  11. data/app/assets/stylesheets/weixin_pam/diymenus.scss +52 -0
  12. data/app/assets/stylesheets/weixin_pam/jquery-dragable.scss +26 -0
  13. data/app/assets/stylesheets/weixin_pam/public_accounts.css +4 -0
  14. data/app/assets/stylesheets/weixin_pam/user_accounts.css +4 -0
  15. data/app/controllers/weixin_pam/application_controller.rb +4 -0
  16. data/app/controllers/weixin_pam/diymenus_controller.rb +100 -0
  17. data/app/controllers/weixin_pam/public_accounts_controller.rb +62 -0
  18. data/app/controllers/weixin_pam/user_accounts_controller.rb +76 -0
  19. data/app/decorators/controllers/weixin_rails_middleware/weixin_controller_decorator.rb +31 -0
  20. data/app/helpers/weixin_pam/application_helper.rb +18 -0
  21. data/app/helpers/weixin_pam/diymenus_helper.rb +4 -0
  22. data/app/helpers/weixin_pam/public_accounts_helper.rb +4 -0
  23. data/app/helpers/weixin_pam/user_accounts_helper.rb +4 -0
  24. data/app/models/weixin_pam/diymenu.rb +58 -0
  25. data/app/models/weixin_pam/public_account.rb +87 -0
  26. data/app/models/weixin_pam/user_account.rb +67 -0
  27. data/app/views/layouts/weixin_pam/_nav_bar.html.erb +37 -0
  28. data/app/views/layouts/weixin_pam/application.html.erb +26 -0
  29. data/app/views/weixin_pam/diymenus/_form.html.erb +18 -0
  30. data/app/views/weixin_pam/diymenus/edit.html.erb +3 -0
  31. data/app/views/weixin_pam/diymenus/index.html.slim +32 -0
  32. data/app/views/weixin_pam/diymenus/new.html.erb +3 -0
  33. data/app/views/weixin_pam/public_accounts/_form.html.erb +12 -0
  34. data/app/views/weixin_pam/public_accounts/edit.html.erb +3 -0
  35. data/app/views/weixin_pam/public_accounts/index.html.erb +30 -0
  36. data/app/views/weixin_pam/public_accounts/new.html.erb +3 -0
  37. data/app/views/weixin_pam/public_accounts/show.html.erb +17 -0
  38. data/app/views/weixin_pam/user_accounts/_form.html.erb +37 -0
  39. data/app/views/weixin_pam/user_accounts/edit.html.erb +6 -0
  40. data/app/views/weixin_pam/user_accounts/index.html.slim +25 -0
  41. data/app/views/weixin_pam/user_accounts/new.html.erb +5 -0
  42. data/app/views/weixin_pam/user_accounts/show.html.erb +29 -0
  43. data/config/initializers/simple_form.rb +165 -0
  44. data/config/initializers/simple_form_bootstrap.rb +149 -0
  45. data/config/initializers/weixin_rails_middleware.rb +27 -0
  46. data/config/locales/diymenu.zh-CN.yml +9 -0
  47. data/config/locales/public_account.zh-CN.yml +16 -0
  48. data/config/locales/simple_form.en.yml +31 -0
  49. data/config/locales/user_account.zh-CN.yml +13 -0
  50. data/config/routes.rb +17 -0
  51. data/db/migrate/20151211153307_create_weixin_pam_public_accounts.rb +14 -0
  52. data/db/migrate/20151211153353_create_weixin_pam_user_accounts.rb +19 -0
  53. data/db/migrate/20151212152624_create_weixin_pam_diymenus.rb +18 -0
  54. data/db/migrate/20151215140830_add_weixin_secret_key_and_weixin_token_to_public_accounts.rb +13 -0
  55. data/db/migrate/20151218053505_add_host_to_weixin_pam_public_account.rb +6 -0
  56. data/lib/tasks/weixin_pam_tasks.rake +4 -0
  57. data/lib/templates/erb/scaffold/_form.html.erb +13 -0
  58. data/lib/weixin_pam.rb +3 -0
  59. data/lib/weixin_pam/api_error.rb +3 -0
  60. data/lib/weixin_pam/api_error/failed_result.rb +11 -0
  61. data/lib/weixin_pam/engine.rb +24 -0
  62. data/lib/weixin_pam/public_account_reply.rb +222 -0
  63. data/lib/weixin_pam/version.rb +3 -0
  64. data/test/controllers/weixin_pam/diymenus_controller_test.rb +52 -0
  65. data/test/controllers/weixin_pam/public_accounts_controller_test.rb +52 -0
  66. data/test/controllers/weixin_pam/user_accounts_controller_test.rb +52 -0
  67. data/test/dummy/README.rdoc +28 -0
  68. data/test/dummy/Rakefile +6 -0
  69. data/test/dummy/app/assets/javascripts/application.js +13 -0
  70. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  71. data/test/dummy/app/controllers/application_controller.rb +5 -0
  72. data/test/dummy/app/helpers/application_helper.rb +2 -0
  73. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  74. data/test/dummy/bin/bundle +3 -0
  75. data/test/dummy/bin/rails +4 -0
  76. data/test/dummy/bin/rake +4 -0
  77. data/test/dummy/bin/setup +29 -0
  78. data/test/dummy/config.ru +4 -0
  79. data/test/dummy/config/application.rb +26 -0
  80. data/test/dummy/config/boot.rb +5 -0
  81. data/test/dummy/config/database.yml +25 -0
  82. data/test/dummy/config/environment.rb +5 -0
  83. data/test/dummy/config/environments/development.rb +41 -0
  84. data/test/dummy/config/environments/production.rb +79 -0
  85. data/test/dummy/config/environments/test.rb +42 -0
  86. data/test/dummy/config/initializers/assets.rb +11 -0
  87. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  88. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  89. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  90. data/test/dummy/config/initializers/inflections.rb +16 -0
  91. data/test/dummy/config/initializers/mime_types.rb +4 -0
  92. data/test/dummy/config/initializers/session_store.rb +3 -0
  93. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  94. data/test/dummy/config/locales/en.yml +23 -0
  95. data/test/dummy/config/routes.rb +4 -0
  96. data/test/dummy/config/secrets.yml +22 -0
  97. data/test/dummy/db/development.sqlite3 +0 -0
  98. data/test/dummy/db/schema.rb +60 -0
  99. data/test/dummy/db/test.sqlite3 +0 -0
  100. data/test/dummy/log/development.log +594 -0
  101. data/test/dummy/public/404.html +67 -0
  102. data/test/dummy/public/422.html +67 -0
  103. data/test/dummy/public/500.html +66 -0
  104. data/test/dummy/public/favicon.ico +0 -0
  105. data/test/fixtures/weixin_pam/diymenus.yml +19 -0
  106. data/test/fixtures/weixin_pam/public_accounts.yml +17 -0
  107. data/test/fixtures/weixin_pam/user_accounts.yml +15 -0
  108. data/test/integration/navigation_test.rb +8 -0
  109. data/test/models/weixin_pam/diymenu_test.rb +9 -0
  110. data/test/models/weixin_pam/public_account_test.rb +9 -0
  111. data/test/models/weixin_pam/user_account_test.rb +9 -0
  112. data/test/test_helper.rb +21 -0
  113. data/test/weixin_pam_test.rb +7 -0
  114. metadata +374 -0
@@ -0,0 +1,31 @@
1
+ # encoding: utf-8
2
+ # 1, @weixin_message: 获取微信所有参数.
3
+ # 2, @weixin_public_account: 如果配置了public_account_class选项,则会返回当前实例,否则返回nil.
4
+ # 3, @keyword: 目前微信只有这三种情况存在关键字: 文本消息, 事件推送, 接收语音识别结果
5
+ WeixinRailsMiddleware::WeixinController.class_eval do
6
+
7
+ def reply
8
+ Rails.logger.debug "========== Request ============"
9
+ Rails.logger.debug
10
+ Rails.logger.debug @weixin_message.inspect
11
+ Rails.logger.debug
12
+ Rails.logger.debug "@keyword = #{@keyword.inspect}"
13
+ Rails.logger.debug
14
+ Rails.logger.debug "==============================="
15
+
16
+ response_data = @weixin_public_account.reply_weixin(@weixin_message, @keyword)
17
+
18
+ Rails.logger.debug "========== Response ==========="
19
+ Rails.logger.debug
20
+ Rails.logger.debug response_data.inspect
21
+ Rails.logger.debug
22
+ Rails.logger.debug "==============================="
23
+
24
+ if response_data == :no_content
25
+ render text: 'success'
26
+ else
27
+ render xml: response_data
28
+ end
29
+ end
30
+
31
+ end
@@ -0,0 +1,18 @@
1
+ module WeixinPam
2
+ module ApplicationHelper
3
+ include FontAwesome::Rails::IconHelper
4
+ def bootstrap_class_for flash_type
5
+ { success: "alert-success", error: "alert-danger", alert: "alert-warning", notice: "alert-info" }[flash_type.intern] || flash_type.to_s
6
+ end
7
+
8
+ def flash_messages(opts = {})
9
+ flash.each do |msg_type, message|
10
+ concat(content_tag(:div, message, class: "alert #{bootstrap_class_for(msg_type)} fade in") do
11
+ concat content_tag(:button, 'x', class: "close", data: { dismiss: 'alert' })
12
+ concat message
13
+ end)
14
+ end
15
+ nil
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,4 @@
1
+ module WeixinPam
2
+ module DiymenusHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module WeixinPam
2
+ module PublicAccountsHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module WeixinPam
2
+ module UserAccountsHelper
3
+ end
4
+ end
@@ -0,0 +1,58 @@
1
+ module WeixinPam
2
+ class Diymenu < ActiveRecord::Base
3
+
4
+ BUTTON_TYPES = {
5
+ click: '点击推事件',
6
+ view: '跳转URL',
7
+ scancode_push: '扫码推事件',
8
+ scancode_waitmsg: '扫码推事件且弹出“消息接收中”提示框',
9
+ pic_sysphoto: '弹出系统拍照发图',
10
+ pic_photo_or_album: '弹出拍照或者相册发图',
11
+ pic_weixin: '弹出微信相册发图器',
12
+ location_select: '弹出地理位置选择器',
13
+ media_id: '下发消息(除文本消息)',
14
+ view_limited: '跳转图文消息URL'
15
+ }.freeze
16
+
17
+ enum button_type: BUTTON_TYPES.keys.freeze
18
+
19
+ validates_presence_of :name
20
+ validates_presence_of :button_type, if: -> { parent.present? }
21
+ validates_presence_of :key, if: -> { button_type.present? && !url_required? }
22
+ validates_presence_of :url, if: :url_required?
23
+ before_validation :set_default_values
24
+
25
+ has_many :diymenus, foreign_key: :parent_id, dependent: :destroy
26
+ has_many :sub_menus,
27
+ -> { where(is_show: true).order('sort').limit(5) },
28
+ class_name: 'Diymenu', foreign_key: :parent_id
29
+
30
+ belongs_to :parent, foreign_key: :parent_id, class_name: "Diymenu"
31
+ belongs_to :public_account
32
+
33
+ def has_sub_menu?
34
+ sub_menus.present?
35
+ end
36
+
37
+ def button_type_json(jbuilder)
38
+ view? ? (jbuilder.url url) : (jbuilder.key key)
39
+ end
40
+
41
+ def displayable_name
42
+ str = name
43
+ str += " (#{button_type} - #{url_required? ? url : key}) " if button_type.present?
44
+ str
45
+ end
46
+
47
+ private
48
+
49
+ def set_default_values
50
+ self.is_show = false if is_show.blank?
51
+ self.sort = (public_account.diymenus.order('sort desc').first.try(:sort) || 0) + 1 if self.sort.blank?
52
+ end
53
+
54
+ def url_required?
55
+ button_type.present? && (view? || view_limited?)
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,87 @@
1
+
2
+ module WeixinPam
3
+ class PublicAccount < ActiveRecord::Base
4
+ include WeixinRailsMiddleware::AutoGenerateWeixinTokenSecretKey
5
+
6
+ has_many :user_accounts, dependent: :destroy
7
+ has_many :diymenus, dependent: :destroy
8
+ has_many :parent_menus, -> { includes(:sub_menus).where(parent_id: nil, is_show: true).order("sort").limit(3) }, class_name: "WeixinPam::Diymenu", foreign_key: :public_account_id
9
+
10
+ before_save :set_default_values
11
+
12
+ def client
13
+ @client ||= WeixinAuthorize::Client.new(app_id, app_secret)
14
+ end
15
+
16
+ def reply_weixin(message, keyword)
17
+ klass = reply_class.present? ? reply_class.constantize : PublicAccountReply
18
+ klass.new(self, message, keyword).reply
19
+ end
20
+
21
+ def build_menu
22
+ Jbuilder.encode do |json|
23
+ json.button(parent_menus) do |menu|
24
+ json.name menu.name
25
+ if menu.has_sub_menu?
26
+ json.sub_button(menu.sub_menus) do |sub_menu|
27
+ json.type sub_menu.button_type
28
+ json.name sub_menu.name
29
+ sub_menu.button_type_json(json)
30
+ end
31
+ else
32
+ json.type menu.button_type
33
+ menu.button_type_json(json)
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ def upload_menu
40
+ client.create_menu(build_menu)
41
+ end
42
+
43
+ def download_menu!
44
+ ret = client.menu
45
+ fail ApiError::FailedResult.new(ret, '下载公众号菜单失败') unless ret.ok?
46
+ # Reset menu
47
+ diymenus.where(parent: nil).update_all(is_show: false)
48
+ data = ret.result
49
+ return unless data.key?('menu') && data['menu'].key?('button')
50
+
51
+ i = 0
52
+ data['menu']['button'].each do |button|
53
+ i += 1
54
+ sub_buttons = button.delete('sub_button')
55
+ button['button_type'] = Diymenu::button_types[button.delete('type')]
56
+ parent_menu = diymenus.find_or_initialize_by(button)
57
+ parent_menu.parent = nil
58
+ parent_menu.is_show = true
59
+ parent_menu.sort = i
60
+ parent_menu.save! if parent_menu.changed?
61
+ parent_menu.diymenus.update_all(parent_id: nil, is_show: false)
62
+
63
+ j = 0
64
+ sub_buttons.each do |sub_button|
65
+ j += 1
66
+ sub_button.delete('sub_button')
67
+ sub_button['button_type'] = Diymenu::button_types[sub_button.delete('type')]
68
+ sub_menu = diymenus.find_or_initialize_by(sub_button)
69
+ sub_menu.parent = parent_menu
70
+ sub_menu.is_show = true
71
+ sub_menu.sort = j
72
+ sub_menu.save! if sub_menu.changed?
73
+ end
74
+ end
75
+ end
76
+
77
+ def temp_qrcode(sence_id = 1)
78
+ client.qr_code_url(client.create_qr_scene(sence_id).result['ticket'])
79
+ end
80
+
81
+ private
82
+
83
+ def set_default_values
84
+ self.reply_class = 'CommonWeixinReplyService' if reply_class.blank?
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,67 @@
1
+ module WeixinPam
2
+ class UserAccount < ActiveRecord::Base
3
+ belongs_to :public_account
4
+ before_save :populate_with_api_info, on: :create, if: -> { nickname.blank? && headimgurl.blank? }
5
+ before_save :set_headimg_fingerprint
6
+ validates_presence_of :uid, scope: [:public_account]
7
+ validates_uniqueness_of :uid, scope: [:public_account]
8
+
9
+ def self.from_omniauth(public_account, auth, auto_create = true)
10
+ public_account.user_accounts.where(uid: auth.uid).send(auto_create ? "first_or_create" : "first_or_initialize") do |u|
11
+ u.nickname = auth.info.nickname
12
+ u.sex = auth.info.sex
13
+ u.province = auth.info.province
14
+ u.city = auth.info.city
15
+ u.country = auth.info.country
16
+ u.headimgurl = auth.info.headimgurl
17
+ end
18
+ end
19
+
20
+ def self.sync_from_server(public_account)
21
+ transaction do
22
+ update_all subscribed: false
23
+ public_account.client.followers.result['data']['openid'].each do |openid|
24
+ u = find_or_initialize_by uid: openid
25
+ u.public_account_id = public_account.id
26
+ u.populate_with_api_info
27
+ yield u if block_given?
28
+ u.save if u.changed?
29
+ end
30
+ end
31
+ end
32
+
33
+ def api_info
34
+ public_account.client.user(uid)
35
+ end
36
+
37
+ def same_with_headimg
38
+ set_headimg_fingerprint
39
+ return [] if headimg_fingerprint.blank?
40
+ records = UserAccount.where(headimg_fingerprint: headimg_fingerprint).all.to_a
41
+ records.delete_if { |r| r.id == id } if id
42
+ records
43
+ end
44
+
45
+ def populate_with_api_info
46
+ weixin_api_info = api_info
47
+ if weixin_api_info.result['subscribe'] != 1
48
+ self.subscribed = false
49
+ return
50
+ end
51
+ self.subscribed = true
52
+ [:nickname, :sex, :city, :province, :country, :headimgurl].each do |attr|
53
+ send("#{attr}=", weixin_api_info.result[attr])
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def set_headimg_fingerprint
60
+ if headimgurl.present? && headimgurl_changed?
61
+ Rails.logger.debug "Fetching headimg for weixin user #{public_account.id}-#{uid}..."
62
+ self.headimg_fingerprint = Digest::MD5.hexdigest(open(headimgurl).read) rescue nil
63
+ Rails.logger.debug " Headimg fingerprint is #{headimg_fingerprint}"
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,37 @@
1
+ <nav class="navbar navbar-default">
2
+ <div class="container-fluid">
3
+ <div class="navbar-header">
4
+ <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
5
+ <span class="sr-only">Toggle navigation</span>
6
+ <span class="icon-bar"></span>
7
+ <span class="icon-bar"></span>
8
+ <span class="icon-bar"></span>
9
+ </button>
10
+ <a class="navbar-brand" href="#">Project name</a>
11
+ </div>
12
+ <div id="navbar" class="navbar-collapse collapse">
13
+ <ul class="nav navbar-nav">
14
+ <li class="active"><a href="#">Home</a></li>
15
+ <li><a href="#">About</a></li>
16
+ <li><a href="#">Contact</a></li>
17
+ <li class="dropdown">
18
+ <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a>
19
+ <ul class="dropdown-menu">
20
+ <li><a href="#">Action</a></li>
21
+ <li><a href="#">Another action</a></li>
22
+ <li><a href="#">Something else here</a></li>
23
+ <li role="separator" class="divider"></li>
24
+ <li class="dropdown-header">Nav header</li>
25
+ <li><a href="#">Separated link</a></li>
26
+ <li><a href="#">One more separated link</a></li>
27
+ </ul>
28
+ </li>
29
+ </ul>
30
+ <ul class="nav navbar-nav navbar-right">
31
+ <li class="active"><a href="./">Default <span class="sr-only">(current)</span></a></li>
32
+ <li><a href="../navbar-static-top/">Static top</a></li>
33
+ <li><a href="../navbar-fixed-top/">Fixed top</a></li>
34
+ </ul>
35
+ </div><!--/.nav-collapse -->
36
+ </div><!--/.container-fluid -->
37
+ </nav>
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1">
7
+ <title>Weixin Public Account Management</title>
8
+ <%= stylesheet_link_tag "weixin_pam/application", media: "all" %>
9
+ <%= javascript_include_tag "weixin_pam/application" %>
10
+ <%= csrf_meta_tags %>
11
+ <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
12
+ <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
13
+ <!--[if lt IE 9]>
14
+ <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
15
+ <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
16
+ <![endif]-->
17
+ </head>
18
+ <body data-page="<%= @page_name %>">
19
+ <div id="flash-message-box"></div>
20
+ <div class="container">
21
+ <%= render 'layouts/weixin_pam/nav_bar' %>
22
+ <%= flash_messages %>
23
+ <%= yield %>
24
+ </div>
25
+ </body>
26
+ </html>
@@ -0,0 +1,18 @@
1
+ <%
2
+ opts = if @diymenu.new_record?
3
+ { url: public_account_diymenus_path(@public_account), method: :post }
4
+ else
5
+ { url: public_account_diymenu_path(@public_account, @diymenu), method: :patch }
6
+ end
7
+ %>
8
+ <%= simple_form_for @diymenu, opts do |f| %>
9
+ <%= f.input :name %>
10
+ <%= f.input :button_type, as: :select, collection: WeixinPam::Diymenu::BUTTON_TYPES.map { |k, v| ["#{k} - #{v}", k] } %>
11
+ <%= f.input :key %>
12
+ <%= f.input :url %>
13
+ <%= f.submit '保存', class: 'btn btn-primary' %>
14
+ <% unless @diymenu.new_record? %>
15
+ <%= link_to '删除', public_account_diymenu_path(@public_account, @diymenu), method: :delete, data: { confirm: '确认要删除吗?' }, class: 'btn btn-danger' %>
16
+ <% end %>
17
+ <%= link_to '返回', public_account_diymenus_path(@public_account), class: 'btn btn-default' %>
18
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <h1>修改微信菜单</h1>
2
+
3
+ <%= render 'form' %>
@@ -0,0 +1,32 @@
1
+ h1 #{@public_account.name} - 微信菜单
2
+ = form_tag sort_public_account_diymenus_path(@public_account), id: 'form-sort-diymenu', remote: true do |f|
3
+ = hidden_field_tag :state
4
+ .row
5
+ .col-md-6
6
+ h3 当前菜单
7
+ ol.parent-menus.sortMenu
8
+ - @diymenus.where(parent_id: nil, is_show: true).order('sort').each do |parent_menu|
9
+ li[data-id=parent_menu.id]
10
+ => fa_icon('arrows')
11
+ = link_to parent_menu.displayable_name, edit_public_account_diymenu_path(@public_account, parent_menu)
12
+ ol.sub-menus
13
+ - parent_menu.diymenus.where(is_show: true).order('sort').each do |sub_menu|
14
+ li[data-id=sub_menu.id]
15
+ => fa_icon('arrows')
16
+ = link_to sub_menu.displayable_name, edit_public_account_diymenu_path(@public_account, sub_menu)
17
+ .col-md-6
18
+ h3 未启用的菜单
19
+ ol.parent-menus.sortMenu
20
+ - @diymenus.where(parent_id: nil, is_show: false).order('sort').each do |parent_menu|
21
+ li[data-id=parent_menu.id]
22
+ => fa_icon('arrows')
23
+ = link_to parent_menu.displayable_name, edit_public_account_diymenu_path(@public_account, parent_menu)
24
+ ol.sub-menus
25
+ - parent_menu.diymenus.order('sort').each do |sub_menu|
26
+ li[data-id=sub_menu.id]
27
+ => fa_icon('arrows')
28
+ = link_to sub_menu.displayable_name, edit_public_account_diymenu_path(@public_account, sub_menu)
29
+ => link_to fa_icon('cloud-upload', text: '上传到公众号菜单'), upload_public_account_diymenus_path(@public_account), class: 'btn btn-success', id: 'upload-menu-to-weixin', remote: true, method: :post, data: { confirm: '确定要把当前菜单设置更新到公众号吗?', disable_with: "<i class='fa fa-spin fa-cog'></i> 正在上传中..." }
30
+ => link_to fa_icon('cloud-download', text: '下载公众号菜单'), download_public_account_diymenus_path(@public_account), class: 'btn btn-danger', id: 'upload-menu-to-weixin', remote: true, method: :post, data: { confirm: '确定要下载公众号菜单吗?下载后,当前页面上的菜档将被放入“未启用”队列', disable_with: "<i class='fa fa-spin fa-cog'></i> 正在下载中..." }
31
+ => link_to fa_icon('pencil', text: '添加菜单'), new_public_account_diymenu_path(@public_account), class: 'btn btn-default'
32
+ = link_to fa_icon('reply', text: '返回'), public_account_path(@public_account), class: 'btn btn-default'
@@ -0,0 +1,3 @@
1
+ <h1>添加微信菜单</h1>
2
+
3
+ <%= render 'form' %>
@@ -0,0 +1,12 @@
1
+ <%= simple_form_for(@public_account) do |f| %>
2
+ <%= f.input :name %>
3
+ <%= f.input :app_id %>
4
+ <%= f.input :app_secret %>
5
+ <%= f.input :api_url %>
6
+ <%= f.input :api_token %>
7
+ <%= f.input :host %>
8
+ <%= f.input :reply_class %>
9
+ <%= f.input :enabled %>
10
+ <%= f.submit '保存', class: 'btn btn-primary' %>
11
+ <%= link_to '返回', @public_account.new_record? ? public_accounts_path : @public_account, class: 'btn btn-default' %>
12
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <h1>修改公众号</h1>
2
+
3
+ <%= render 'form' %>
@@ -0,0 +1,30 @@
1
+ <h1>公众号列表</h1>
2
+
3
+ <table class="table table-striped">
4
+ <thead>
5
+ <tr>
6
+ <th>公众号名称</th>
7
+ <th>app_id</th>
8
+ <th>是否启用</th>
9
+ <th>操作</th>
10
+ </tr>
11
+ </thead>
12
+
13
+ <tbody>
14
+ <% @public_accounts.each do |public_account| %>
15
+ <tr>
16
+ <td><%= link_to public_account.name, public_account %></td>
17
+ <td><%= public_account.app_id %></td>
18
+ <td><%= public_account.enabled ? '是' : '否' %></td>
19
+ <td>
20
+ <%= link_to '修改', edit_public_account_path(public_account), class: 'btn btn-xs btn-default' %>
21
+ <%= link_to '删除', public_account, method: :delete, data: { confirm: 'Are you sure?' }, class: 'btn btn-danger btn-xs' %>
22
+ </td>
23
+ </tr>
24
+ <% end %>
25
+ </tbody>
26
+ </table>
27
+
28
+ <br>
29
+
30
+ <%= link_to '添加', new_public_account_path, class: 'btn btn-success' %>