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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +165 -0
- data/Rakefile +37 -0
- data/app/assets/javascripts/jquery-sortable.js +699 -0
- data/app/assets/javascripts/weixin_pam/application.js +30 -0
- data/app/assets/javascripts/weixin_pam/diymenus.js +90 -0
- data/app/assets/javascripts/weixin_pam/public_accounts.js +2 -0
- data/app/assets/javascripts/weixin_pam/user_accounts.js +23 -0
- data/app/assets/stylesheets/weixin_pam/application.scss +31 -0
- data/app/assets/stylesheets/weixin_pam/diymenus.scss +52 -0
- data/app/assets/stylesheets/weixin_pam/jquery-dragable.scss +26 -0
- data/app/assets/stylesheets/weixin_pam/public_accounts.css +4 -0
- data/app/assets/stylesheets/weixin_pam/user_accounts.css +4 -0
- data/app/controllers/weixin_pam/application_controller.rb +4 -0
- data/app/controllers/weixin_pam/diymenus_controller.rb +100 -0
- data/app/controllers/weixin_pam/public_accounts_controller.rb +62 -0
- data/app/controllers/weixin_pam/user_accounts_controller.rb +76 -0
- data/app/decorators/controllers/weixin_rails_middleware/weixin_controller_decorator.rb +31 -0
- data/app/helpers/weixin_pam/application_helper.rb +18 -0
- data/app/helpers/weixin_pam/diymenus_helper.rb +4 -0
- data/app/helpers/weixin_pam/public_accounts_helper.rb +4 -0
- data/app/helpers/weixin_pam/user_accounts_helper.rb +4 -0
- data/app/models/weixin_pam/diymenu.rb +58 -0
- data/app/models/weixin_pam/public_account.rb +87 -0
- data/app/models/weixin_pam/user_account.rb +67 -0
- data/app/views/layouts/weixin_pam/_nav_bar.html.erb +37 -0
- data/app/views/layouts/weixin_pam/application.html.erb +26 -0
- data/app/views/weixin_pam/diymenus/_form.html.erb +18 -0
- data/app/views/weixin_pam/diymenus/edit.html.erb +3 -0
- data/app/views/weixin_pam/diymenus/index.html.slim +32 -0
- data/app/views/weixin_pam/diymenus/new.html.erb +3 -0
- data/app/views/weixin_pam/public_accounts/_form.html.erb +12 -0
- data/app/views/weixin_pam/public_accounts/edit.html.erb +3 -0
- data/app/views/weixin_pam/public_accounts/index.html.erb +30 -0
- data/app/views/weixin_pam/public_accounts/new.html.erb +3 -0
- data/app/views/weixin_pam/public_accounts/show.html.erb +17 -0
- data/app/views/weixin_pam/user_accounts/_form.html.erb +37 -0
- data/app/views/weixin_pam/user_accounts/edit.html.erb +6 -0
- data/app/views/weixin_pam/user_accounts/index.html.slim +25 -0
- data/app/views/weixin_pam/user_accounts/new.html.erb +5 -0
- data/app/views/weixin_pam/user_accounts/show.html.erb +29 -0
- data/config/initializers/simple_form.rb +165 -0
- data/config/initializers/simple_form_bootstrap.rb +149 -0
- data/config/initializers/weixin_rails_middleware.rb +27 -0
- data/config/locales/diymenu.zh-CN.yml +9 -0
- data/config/locales/public_account.zh-CN.yml +16 -0
- data/config/locales/simple_form.en.yml +31 -0
- data/config/locales/user_account.zh-CN.yml +13 -0
- data/config/routes.rb +17 -0
- data/db/migrate/20151211153307_create_weixin_pam_public_accounts.rb +14 -0
- data/db/migrate/20151211153353_create_weixin_pam_user_accounts.rb +19 -0
- data/db/migrate/20151212152624_create_weixin_pam_diymenus.rb +18 -0
- data/db/migrate/20151215140830_add_weixin_secret_key_and_weixin_token_to_public_accounts.rb +13 -0
- data/db/migrate/20151218053505_add_host_to_weixin_pam_public_account.rb +6 -0
- data/lib/tasks/weixin_pam_tasks.rake +4 -0
- data/lib/templates/erb/scaffold/_form.html.erb +13 -0
- data/lib/weixin_pam.rb +3 -0
- data/lib/weixin_pam/api_error.rb +3 -0
- data/lib/weixin_pam/api_error/failed_result.rb +11 -0
- data/lib/weixin_pam/engine.rb +24 -0
- data/lib/weixin_pam/public_account_reply.rb +222 -0
- data/lib/weixin_pam/version.rb +3 -0
- data/test/controllers/weixin_pam/diymenus_controller_test.rb +52 -0
- data/test/controllers/weixin_pam/public_accounts_controller_test.rb +52 -0
- data/test/controllers/weixin_pam/user_accounts_controller_test.rb +52 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/controllers/application_controller.rb +5 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/setup +29 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +26 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +41 -0
- data/test/dummy/config/environments/production.rb +79 -0
- data/test/dummy/config/environments/test.rb +42 -0
- data/test/dummy/config/initializers/assets.rb +11 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +4 -0
- data/test/dummy/config/secrets.yml +22 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/schema.rb +60 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/development.log +594 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/fixtures/weixin_pam/diymenus.yml +19 -0
- data/test/fixtures/weixin_pam/public_accounts.yml +17 -0
- data/test/fixtures/weixin_pam/user_accounts.yml +15 -0
- data/test/integration/navigation_test.rb +8 -0
- data/test/models/weixin_pam/diymenu_test.rb +9 -0
- data/test/models/weixin_pam/public_account_test.rb +9 -0
- data/test/models/weixin_pam/user_account_test.rb +9 -0
- data/test/test_helper.rb +21 -0
- data/test/weixin_pam_test.rb +7 -0
- 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,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,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,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,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' %>
|