wikidotrb 3.0.7.pre.6

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