wikidotrb 3.0.7.pre.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +77 -0
  4. data/CHANGELOG.md +52 -0
  5. data/CODE_OF_CONDUCT.md +132 -0
  6. data/Rakefile +12 -0
  7. data/_config.yml +4 -0
  8. data/lib/wikidotrb/common/decorators.rb +81 -0
  9. data/lib/wikidotrb/common/exceptions.rb +88 -0
  10. data/lib/wikidotrb/common/logger.rb +25 -0
  11. data/lib/wikidotrb/connector/ajax.rb +192 -0
  12. data/lib/wikidotrb/connector/api.rb +20 -0
  13. data/lib/wikidotrb/module/auth.rb +76 -0
  14. data/lib/wikidotrb/module/client.rb +146 -0
  15. data/lib/wikidotrb/module/forum.rb +92 -0
  16. data/lib/wikidotrb/module/forum_category.rb +197 -0
  17. data/lib/wikidotrb/module/forum_group.rb +109 -0
  18. data/lib/wikidotrb/module/forum_post.rb +223 -0
  19. data/lib/wikidotrb/module/forum_thread.rb +346 -0
  20. data/lib/wikidotrb/module/page.rb +598 -0
  21. data/lib/wikidotrb/module/page_revision.rb +142 -0
  22. data/lib/wikidotrb/module/page_source.rb +17 -0
  23. data/lib/wikidotrb/module/page_votes.rb +31 -0
  24. data/lib/wikidotrb/module/private_message.rb +142 -0
  25. data/lib/wikidotrb/module/site.rb +207 -0
  26. data/lib/wikidotrb/module/site_application.rb +97 -0
  27. data/lib/wikidotrb/module/user.rb +119 -0
  28. data/lib/wikidotrb/util/parser/odate.rb +47 -0
  29. data/lib/wikidotrb/util/parser/user.rb +105 -0
  30. data/lib/wikidotrb/util/quick_module.rb +61 -0
  31. data/lib/wikidotrb/util/requestutil.rb +51 -0
  32. data/lib/wikidotrb/util/stringutil.rb +39 -0
  33. data/lib/wikidotrb/util/table/char_table.rb +477 -0
  34. data/lib/wikidotrb/version.rb +5 -0
  35. data/lib/wikidotrb.rb +41 -0
  36. data/sig/wikidotrb.rbs +4 -0
  37. metadata +197 -0
@@ -0,0 +1,223 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "nokogiri"
4
+ require "date"
5
+ require_relative "../common/exceptions"
6
+
7
+ module Wikidotrb
8
+ module Module
9
+ class ForumPostCollection < Array
10
+ attr_accessor :thread
11
+
12
+ # 初期化メソッド
13
+ # @param thread [ForumThread] スレッドオブジェクト
14
+ # @param posts [Array<ForumPost>] 投稿オブジェクトのリスト
15
+ def initialize(thread:, posts: [])
16
+ super(posts)
17
+ @thread = thread
18
+ end
19
+
20
+ # IDで投稿を検索する
21
+ # @param target_id [Integer] 投稿ID
22
+ # @return [ForumPost, nil] 投稿が見つかった場合はForumPostオブジェクト、見つからなければnil
23
+ def find(target_id)
24
+ find { |post| post.id == target_id }
25
+ end
26
+
27
+ # 親投稿を取得して設定する
28
+ # @param thread [ForumThread] スレッドオブジェクト
29
+ # @param posts [Array<ForumPost>] 投稿オブジェクトのリスト
30
+ # @return [Array<ForumPost>] 更新された投稿のリスト
31
+ def self.acquire_parent_post(thread:, posts:)
32
+ return posts if posts.empty?
33
+
34
+ posts.each { |post| post.parent = thread.get(post.parent_id) }
35
+ posts
36
+ end
37
+
38
+ # 親投稿をリビジョンに取得する
39
+ def get_parent_post
40
+ ForumPostCollection.acquire_parent_post(thread: @thread, posts: self)
41
+ end
42
+
43
+ # 投稿情報を取得して設定する
44
+ # @param thread [ForumThread] スレッドオブジェクト
45
+ # @param posts [Array<ForumPost>] 投稿オブジェクトのリスト
46
+ # @return [Array<ForumPost>] 更新された投稿のリスト
47
+ def self.acquire_post_info(thread:, posts:)
48
+ return posts if posts.empty?
49
+
50
+ responses = thread.site.amc_request(
51
+ bodies: posts.map do |post|
52
+ {
53
+ "postId" => post.id,
54
+ "threadId" => thread.id,
55
+ "moduleName" => "forum/sub/ForumEditPostFormModule"
56
+ }
57
+ end
58
+ )
59
+
60
+ responses.each_with_index do |response, index|
61
+ html = Nokogiri::HTML(response.body.to_s)
62
+ title = html.at_css("input#np-title")&.text&.strip
63
+ source = html.at_css("textarea#np-text")&.text&.strip
64
+ posts[index].title = title
65
+ posts[index].source = source
66
+ end
67
+
68
+ posts
69
+ end
70
+
71
+ # 投稿情報をリビジョンに取得する
72
+ def get_post_info
73
+ ForumPostCollection.acquire_post_info(thread: @thread, posts: self)
74
+ end
75
+ end
76
+
77
+ class ForumPost
78
+ attr_accessor :site, :id, :forum, :thread, :parent_id, :created_by, :created_at, :edited_by, :edited_at,
79
+ :source_text, :source_ele, :parent, :title, :source
80
+
81
+ # 初期化メソッド
82
+ def initialize(site:, id:, forum:, thread: nil, parent_id: nil, created_by: nil, created_at: nil, edited_by: nil,
83
+ edited_at: nil, source_text: nil, source_ele: nil)
84
+ @site = site
85
+ @id = id
86
+ @forum = forum
87
+ @thread = thread
88
+ @parent_id = parent_id
89
+ @created_by = created_by
90
+ @created_at = created_at
91
+ @edited_by = edited_by
92
+ @edited_at = edited_at
93
+ @source_text = source_text
94
+ @source_ele = source_ele
95
+ @parent = nil
96
+ @title = nil
97
+ @source = nil
98
+ end
99
+
100
+ # 親投稿を設定する
101
+
102
+ # タイトルを設定する
103
+
104
+ # ソースを設定する
105
+
106
+ # 投稿のURLを取得する
107
+ def get_url
108
+ "#{@thread.get_url}#post-#{@id}"
109
+ end
110
+
111
+ # 親投稿のゲッターメソッド
112
+ def parent
113
+ ForumPostCollection.new(thread: @thread, posts: [self]).get_parent_post unless @parent
114
+ @parent
115
+ end
116
+
117
+ # タイトルのゲッターメソッド
118
+ def title
119
+ ForumPostCollection.new(thread: @thread, posts: [self]).get_post_info unless @title
120
+ @title
121
+ end
122
+
123
+ # ソースのゲッターメソッド
124
+ def source
125
+ ForumPostCollection.new(thread: @thread, posts: [self]).get_post_info unless @source
126
+ @source
127
+ end
128
+
129
+ # 投稿への返信を行う
130
+ def reply(title: "", source: "")
131
+ client = @site.client
132
+ client.login_check
133
+ raise Wikidotrb::Common::UnexpectedException, "Post body can not be left empty." if source == ""
134
+
135
+ response = @site.amc_request(
136
+ bodies: [
137
+ {
138
+ "parentId" => @id,
139
+ "title" => title,
140
+ "source" => source,
141
+ "action" => "ForumAction",
142
+ "event" => "savePost"
143
+ }
144
+ ]
145
+ ).first
146
+ body = JSON.parse(response.body.to_s)
147
+
148
+ ForumPost.new(
149
+ site: @site,
150
+ id: body["postId"].to_i,
151
+ forum: @forum,
152
+ title: title,
153
+ source: source,
154
+ thread: @thread,
155
+ parent_id: @id,
156
+ created_by: client.user.get(client.username),
157
+ created_at: DateTime.parse(body["CURRENT_TIMESTAMP"])
158
+ )
159
+ end
160
+
161
+ # 投稿の編集を行う
162
+ def edit(title: nil, source: nil)
163
+ client = @site.client
164
+ client.login_check
165
+
166
+ return self if title.nil? && source.nil?
167
+ raise Wikidotrb::Common::UnexpectedException, "Post source can not be left empty." if source == ""
168
+
169
+ begin
170
+ response = @site.amc_request(
171
+ bodies: [
172
+ {
173
+ "postId" => @id,
174
+ "threadId" => @thread.id,
175
+ "moduleName" => "forum/sub/ForumEditPostFormModule"
176
+ }
177
+ ]
178
+ ).first
179
+ html = Nokogiri::HTML(response.body.to_s)
180
+ current_id = html.at_css("form#edit-post-form>input")[1].get("value").to_i
181
+
182
+ @site.amc_request(
183
+ bodies: [
184
+ {
185
+ "postId" => @id,
186
+ "currentRevisionId" => current_id,
187
+ "title" => title || @title,
188
+ "source" => source || @source,
189
+ "action" => "ForumAction",
190
+ "event" => "saveEditPost",
191
+ "moduleName" => "Empty"
192
+ }
193
+ ]
194
+ )
195
+ rescue Wikidotrb::Common::WikidotStatusCodeException
196
+ return self
197
+ end
198
+
199
+ @edited_by = client.user.get(client.username)
200
+ @edited_at = DateTime.now
201
+ @title = title || @title
202
+ @source = source || @source
203
+
204
+ self
205
+ end
206
+
207
+ # 投稿の削除を行う
208
+ def destroy
209
+ @site.client.login_check
210
+ @site.amc_request(
211
+ bodies: [
212
+ {
213
+ "postId" => @id,
214
+ "action" => "ForumAction",
215
+ "event" => "deletePost",
216
+ "moduleName" => "Empty"
217
+ }
218
+ ]
219
+ )
220
+ end
221
+ end
222
+ end
223
+ end
@@ -0,0 +1,346 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "nokogiri"
4
+ require "date"
5
+ require_relative "../common/exceptions"
6
+ require_relative "forum_post"
7
+
8
+ module Wikidotrb
9
+ module Module
10
+ class ForumThreadCollection < Array
11
+ attr_accessor :forum
12
+
13
+ # 初期化メソッド
14
+ # @param forum [Forum] フォーラムオブジェクト
15
+ # @param threads [Array<ForumThread>] スレッドのリスト
16
+ def initialize(forum:, threads: [])
17
+ super(threads)
18
+ @forum = forum
19
+ end
20
+
21
+ # スレッド情報を取得して更新する
22
+ # @param forum [Forum] フォーラムオブジェクト
23
+ # @param threads [Array<ForumThread>] スレッドのリスト
24
+ # @return [Array<ForumThread>] 更新されたスレッドのリスト
25
+ def self.acquire_update(forum:, threads:)
26
+ return threads if threads.empty?
27
+
28
+ client = forum.site.client
29
+ responses = forum.site.amc_request(
30
+ bodies: threads.map { |thread| { "t" => thread.id, "moduleName" => "forum/ForumViewThreadModule" } }
31
+ )
32
+
33
+ responses.each_with_index do |response, index|
34
+ thread = threads[index]
35
+ html = Nokogiri::HTML(response.body.to_s)
36
+ statistics = html.at_css("div.statistics")
37
+ user = statistics.at_css("span.printuser")
38
+ odate = statistics.at_css("span.odate")
39
+ category_url = html.css("div.forum-breadcrumbs a")[1]["href"]
40
+ category_id = category_url.match(/c-(\d+)/)[1]
41
+ title = html.at_css("div.forum-breadcrumbs").text.strip
42
+ counts = statistics.text.scan(/\n.+\D(\d+)/).last.first.to_i
43
+
44
+ thread.title = title.match(/»([ \S]*)$/)[1].strip
45
+ thread.category = thread.forum.category.get(category_id.to_i)
46
+ description_block = html.at_css("div.description-block div.head")
47
+ thread.description = description_block.nil? ? "" : html.at_css("div.description-block").text.strip.match(/[ \S]+$/).to_s
48
+
49
+ thread.last = nil if thread.posts_counts != counts
50
+ thread.posts_counts = counts
51
+ thread.created_by = user_parser(client, user)
52
+ thread.created_at = odate_parser(odate)
53
+
54
+ pager_no = html.at_css("span.pager-no")
55
+ thread.pagerno = pager_no.nil? ? 1 : pager_no.text.match(/of (\d+)/)[1].to_i
56
+
57
+ page_ele = html.at_css("div.description-block>a")
58
+ if page_ele
59
+ thread.page = thread.site.page.get(page_ele["href"][1..])
60
+ thread.page.discuss = thread
61
+ end
62
+ end
63
+
64
+ threads
65
+ end
66
+
67
+ # スレッドを更新する
68
+ def update
69
+ ForumThreadCollection.acquire_update(forum: @forum, threads: self)
70
+ end
71
+ end
72
+
73
+ class ForumThread
74
+ attr_accessor :site, :id, :forum, :category, :title, :description, :created_by, :created_at, :posts_counts,
75
+ :page, :pagerno
76
+ attr_reader :last
77
+
78
+ def initialize(site:, id:, forum:, category: nil, title: nil, description: nil, created_by: nil, created_at: nil,
79
+ posts_counts: nil, page: nil, pagerno: nil, last_post_id: nil)
80
+ @site = site
81
+ @id = id
82
+ @forum = forum
83
+ @category = category
84
+ @title = title
85
+ @description = description
86
+ @created_by = created_by
87
+ @created_at = created_at
88
+ @posts_counts = posts_counts
89
+ @page = page
90
+ @pagerno = pagerno
91
+ @_last_post_id = last_post_id
92
+ @_last = nil
93
+ end
94
+
95
+ # 最後の投稿の取得
96
+ def last
97
+ if @_last_post_id && @_last.nil?
98
+ update
99
+ @_last = get(@_last_post_id)
100
+ end
101
+ @_last
102
+ end
103
+
104
+ # 最後の投稿を設定
105
+ def last=(value)
106
+ @_last = value
107
+ end
108
+
109
+ # 投稿のコレクションを取得する
110
+ # @return [ForumPostCollection] 投稿オブジェクトのコレクション
111
+ def posts
112
+ client = @site.client
113
+ responses = @site.amc_request(
114
+ bodies: (1..@pagerno).map do |no|
115
+ { "pagerNo" => no, "t" => @id, "order" => "", "moduleName" => "forum/ForumViewThreadPostsModule" }
116
+ end
117
+ )
118
+
119
+ posts = []
120
+
121
+ responses.each do |response|
122
+ html = Nokogiri::HTML(response.body.to_s)
123
+ html.css("div.post").each do |post|
124
+ cuser = post.at_css("div.info span.printuser")
125
+ codate = post.at_css("div.info span.odate")
126
+ parent = post.parent["id"]
127
+ parent_id = parent == "thread-container-posts" ? nil : parent.match(/fpc-(\d+)/)[1].to_i
128
+ euser = post.at_css("div.changes span.printuser")
129
+ eodate = post.at_css("div.changes span.odate a")
130
+
131
+ posts << ForumPost.new(
132
+ site: @site,
133
+ id: post["id"].match(/post-(\d+)/)[1].to_i,
134
+ forum: @forum,
135
+ thread: self,
136
+ _title: post.at_css("div.title").text.strip,
137
+ parent_id: parent_id,
138
+ created_by: user_parser(client, cuser),
139
+ created_at: odate_parser(codate),
140
+ edited_by: euser.nil? ? nil : client.user.get(euser.text),
141
+ edited_at: eodate.nil? ? nil : odate_parser(eodate),
142
+ source_ele: post.at_css("div.content"),
143
+ source_text: post.at_css("div.content").text.strip
144
+ )
145
+ end
146
+ end
147
+
148
+ ForumPostCollection.new(thread: self, posts: posts)
149
+ end
150
+
151
+ # スレッドのURLを取得する
152
+ def get_url
153
+ "#{@site.get_url}/forum/t-#{@id}"
154
+ end
155
+
156
+ # スレッドを更新する
157
+ def update
158
+ ForumThreadCollection.new(forum: @forum, threads: [self]).update.first
159
+ end
160
+
161
+ # スレッドの編集
162
+ def edit(title: nil, description: nil)
163
+ @site.client.login_check
164
+ raise Wikidotrb::Common::UnexpectedException, "Title can not be left empty." if title == ""
165
+
166
+ raise Wikidotrb::Common::UnexpectedException, "Page's discussion can not be edited." if @page
167
+
168
+ return self if title.nil? && description.nil?
169
+
170
+ @site.amc_request(
171
+ bodies: [
172
+ {
173
+ "threadId" => @id,
174
+ "title" => @title.nil? ? title : @title,
175
+ "description" => description.nil? ? @description : description,
176
+ "action" => "ForumAction",
177
+ "event" => "saveThreadMeta",
178
+ "moduleName" => "Empty"
179
+ }
180
+ ]
181
+ )
182
+
183
+ @title = title.nil? ? @title : title
184
+ @description = description.nil? ? @description : description
185
+
186
+ self
187
+ end
188
+
189
+ # スレッドのカテゴリ移動
190
+ def move_to(category_id)
191
+ @site.client.login_check
192
+ @site.amc_request(
193
+ bodies: [
194
+ {
195
+ "categoryId" => category_id,
196
+ "threadId" => @id,
197
+ "action" => "ForumAction",
198
+ "event" => "moveThread",
199
+ "moduleName" => "Empty"
200
+ }
201
+ ]
202
+ )
203
+ end
204
+
205
+ # スレッドのロック
206
+ def lock
207
+ @site.client.login_check
208
+ @site.amc_request(
209
+ bodies: [
210
+ {
211
+ "threadId" => @id,
212
+ "block" => "true",
213
+ "action" => "ForumAction",
214
+ "event" => "saveBlock",
215
+ "moduleName" => "Empty"
216
+ }
217
+ ]
218
+ )
219
+ self
220
+ end
221
+
222
+ # スレッドのアンロック
223
+ def unlock
224
+ @site.client.login_check
225
+ @site.amc_request(
226
+ bodies: [
227
+ {
228
+ "threadId" => @id,
229
+ "action" => "ForumAction",
230
+ "event" => "saveBlock",
231
+ "moduleName" => "Empty"
232
+ }
233
+ ]
234
+ )
235
+ self
236
+ end
237
+
238
+ # スレッドがロックされているか確認
239
+ def locked?
240
+ @site.client.login_check
241
+ response = @site.amc_request(
242
+ bodies: [
243
+ {
244
+ "threadId" => @id,
245
+ "moduleName" => "forum/sub/ForumEditThreadBlockModule"
246
+ }
247
+ ]
248
+ ).first
249
+
250
+ html = Nokogiri::HTML(response.body.to_s)
251
+ checked = html.at_css("input.checkbox")["checked"]
252
+
253
+ !checked.nil?
254
+ end
255
+
256
+ # スレッドを固定する
257
+ def stick
258
+ @site.client.login_check
259
+ @site.amc_request(
260
+ bodies: [
261
+ {
262
+ "threadId" => @id,
263
+ "sticky" => "true",
264
+ "action" => "ForumAction",
265
+ "event" => "saveSticky",
266
+ "moduleName" => "Empty"
267
+ }
268
+ ]
269
+ )
270
+ self
271
+ end
272
+
273
+ # スレッドの固定を解除する
274
+ def unstick
275
+ @site.client.login_check
276
+ @site.amc_request(
277
+ bodies: [
278
+ {
279
+ "threadId" => @id,
280
+ "action" => "ForumAction",
281
+ "event" => "saveSticky",
282
+ "moduleName" => "Empty"
283
+ }
284
+ ]
285
+ )
286
+ self
287
+ end
288
+
289
+ # スレッドが固定されているか確認
290
+ def sticked?
291
+ @site.client.login_check
292
+ response = @site.amc_request(
293
+ bodies: [
294
+ {
295
+ "threadId" => @id,
296
+ "moduleName" => "forum/sub/ForumEditThreadStickinessModule"
297
+ }
298
+ ]
299
+ ).first
300
+
301
+ html = Nokogiri::HTML(response.body.to_s)
302
+ checked = html.at_css("input.checkbox")["checked"]
303
+
304
+ !checked.nil?
305
+ end
306
+
307
+ # 新しい投稿を作成する
308
+ def new_post(title: "", source: "", parent_id: "")
309
+ client = @site.client
310
+ client.login_check
311
+ raise Wikidotrb::Common::UnexpectedException, "Post body can not be left empty." if source == ""
312
+
313
+ response = @site.amc_request(
314
+ bodies: [
315
+ {
316
+ "parentId" => parent_id,
317
+ "title" => title,
318
+ "source" => source,
319
+ "action" => "ForumAction",
320
+ "event" => "savePost"
321
+ }
322
+ ]
323
+ ).first
324
+
325
+ body = JSON.parse(response.body.to_s)
326
+
327
+ ForumPost.new(
328
+ site: @site,
329
+ id: body["postId"].to_i,
330
+ forum: @forum,
331
+ title: title,
332
+ source: source,
333
+ thread: self,
334
+ parent_id: parent_id.empty? ? nil : parent_id.to_i,
335
+ created_by: client.user.get(client.username),
336
+ created_at: DateTime.parse(body["CURRENT_TIMESTAMP"])
337
+ )
338
+ end
339
+
340
+ # 投稿を取得する
341
+ def get(post_id)
342
+ posts.find { |post| post.id == post_id }
343
+ end
344
+ end
345
+ end
346
+ end