nbudin-disqus 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,27 @@
1
+ module Disqus
2
+
3
+ class BaseAuthor
4
+ attr_reader :url, :email_hash
5
+ end
6
+
7
+ class Author < BaseAuthor
8
+ attr_reader :id, :username, :display_name, :has_avatar
9
+ def initialize(id, username, display_name, url, email_hash, has_avatar)
10
+ @id, @username, @display_name, @url, @email_hash, @has_avatar = id, username, display_name, url, email_hash, has_avatar
11
+ end
12
+
13
+ # Returns the user's <tt>display_name</tt> or <tt>username</tt> if <tt>display_name</tt> is blank.
14
+ def name
15
+ @display_name.blank? ? @username : @display_name
16
+ end
17
+
18
+ end
19
+
20
+ class AnonymousAuthor < BaseAuthor
21
+ attr_reader :name
22
+ def initialize(name, url, email_hash)
23
+ @name, @url, @email_hash = name, url, email_hash
24
+ end
25
+ end
26
+ end
27
+
@@ -0,0 +1,126 @@
1
+ module Disqus
2
+
3
+ class Forum
4
+ attr_reader :id, :shortname, :name, :created_at, :threads
5
+
6
+ def initialize(id, shortname, name, created_at, include_threads = false)
7
+ @id, @shortname, @name, @created_at = id.to_i, shortname, name, Time.parse(created_at.to_s)
8
+ @key = nil
9
+ @forum_threads = include_threads ? load_threads : []
10
+ end
11
+
12
+ def ==(other_forum)
13
+ id == other_forum.id &&
14
+ shortname == other_forum.shortname &&
15
+ name == other_forum.name &&
16
+ key == other_forum.key
17
+ end
18
+
19
+ # Returns an array of Forum objects belonging to the user indicated by the API key.
20
+ def self.list(user_api_key = nil)
21
+ opts = user_api_key ? {:api_key => user_api_key} : {}
22
+ response = Disqus::Api::get_forum_list(opts)
23
+ if response["succeeded"]
24
+ return response["message"].map{|forum| Forum.new(forum["id"], forum["shortname"], forum["name"], forum["created_at"])}
25
+ else
26
+ raise_api_error(response)
27
+ end
28
+ end
29
+
30
+ # Returns a Forum object corresponding to the given forum_id or nil if it was not found.
31
+ def self.find(forum_id, user_api_key = nil)
32
+ opts = user_api_key ? {:api_key => user_api_key} : {}
33
+ list = Forum.list(opts)
34
+ if list
35
+ list.select{|f| f.id == forum_id}.first
36
+ end
37
+ end
38
+
39
+ # Returns the forum API Key for this forum.
40
+ def key(user_api_key = nil)
41
+ @key ||= load_key(user_api_key)
42
+ end
43
+
44
+ # Returns an array of threads belonging to this forum.
45
+ def forum_threads(force_update = false)
46
+ if (@forum_threads.nil? or @forum_threads.empty? or force_update)
47
+ @forum_threads = Disqus::Thread.list(self)
48
+ end
49
+ @forum_threads
50
+ end
51
+
52
+ # Returns a thread associated with the given URL.
53
+ #
54
+ # A thread will only have an associated URL if it was automatically
55
+ # created by Disqus javascript embedded on that page.
56
+ def get_thread_by_url(url)
57
+ response = Disqus::Api::get_thread_by_url(:url => url, :forum_api_key => key)
58
+ if response["succeeded"]
59
+ t = response["message"]
60
+ Thread.new(t["id"], self, t["slug"], t["title"], t["created_at"], t["allow_comments"], t["url"], t["identifier"])
61
+ else
62
+ raise_api_error(response)
63
+ end
64
+ end
65
+
66
+ # Create or retrieve a thread by an arbitrary identifying string of your
67
+ # choice. For example, you could use your local database's ID for the
68
+ # thread. This method allows you to decouple thread identifiers from the
69
+ # URL's on which they might be appear. (Disqus would normally use a
70
+ # thread's URL to identify it, which is problematic when URL's do not
71
+ # uniquely identify a resource.) If no thread exists for the given
72
+ # identifier (paired with the forum) yet, one will be created.
73
+ #
74
+ # Returns a Thread object representing the thread that was created or
75
+ # retrieved.
76
+ def thread_by_identifier(identifier, title)
77
+ # TODO - should we separate thread retrieval from thread creation? The API to me seems confusing here.
78
+ response = Disqus::Api::thread_by_identifier(:identifier => identifier, :title => title, :forum_api_key => key)
79
+ if response["succeeded"]
80
+ t = response["message"]["thread"]
81
+ Thread.new(t["id"], self, t["slug"], t["title"], t["created_at"], t["allow_comments"], t["url"], t["identifier"])
82
+ else
83
+ raise_api_error(response)
84
+ end
85
+ end
86
+
87
+ # Sets the provided values on the thread object.
88
+ #
89
+ # Returns an empty success message.
90
+ #
91
+ # Options:
92
+ #
93
+ # * <tt>:title</tt> - the title of the thread
94
+ # * <tt>:slug</tt> - the per-forum-unique string used for identifying this thread in disqus.com URL's relating to this thread. Composed of underscore-separated alphanumeric strings.
95
+ # * <tt>:url</tt> - the URL this thread is on, if known.
96
+ # * <tt>:allow_comment</tt> - whether this thread is open to new comments
97
+ def update_thread(thread_id, opts = {})
98
+ result = Disqus::Api::update_thread(
99
+ :forum_api_key => key,
100
+ :thread_id => thread_id,
101
+ :title => opts[:title],
102
+ :slug => opts[:slug],
103
+ :url => opts[:url],
104
+ :allow_comments => opts[:allow_comments]
105
+ )
106
+ return result["succeeded"]
107
+ end
108
+
109
+ private
110
+
111
+ def raise_api_error(response)
112
+ raise "Error: #{response['code']}: #{response['message']}"
113
+ end
114
+
115
+ def load_key(user_api_key = nil)
116
+ opts = user_api_key ? {:api_key => user_api_key} : {}
117
+ response = Disqus::Api::get_forum_api_key(opts.merge(:forum_id => self.id))
118
+ if response["succeeded"]
119
+ return @key = response["message"]
120
+ else
121
+ raise_api_error(response)
122
+ end
123
+ end
124
+
125
+ end
126
+ end
@@ -0,0 +1,51 @@
1
+ module Disqus
2
+
3
+ class Post
4
+ attr_reader :id, :forum, :thread, :created_at, :message, :parent_post, :shown, :is_anonymous, :author
5
+
6
+ def initialize(id, forum, thread, created_at, message, parent_post, shown, is_anonymous, author)
7
+ @id, @forum, @thread, @created_at, @message, @parent_post, @shown, @is_anonymous, @author = id.to_i, forum, thread, Time.parse(created_at.to_s), message, parent_post, shown, is_anonymous, author
8
+ end
9
+
10
+ # Returns an array of Post objects representing all posts belonging to the
11
+ # given thread. The array is sorted by the "created_at" date descending.
12
+ def self.list(thread)
13
+ response = Disqus::Api::get_thread_posts(:thread_id =>thread.id, :forum_api_key => thread.forum.key)
14
+ if response["succeeded"]
15
+ posts = response["message"].map do |post|
16
+ author = nil
17
+ if post["is_anonymous"]
18
+ author = AnonymousAuthor.new(
19
+ post["anonymous_author"]["name"],
20
+ post["anonymous_author"]["url"],
21
+ post["anonymous_author"]["email_hash"]
22
+ )
23
+ else
24
+ author = Author.new(
25
+ post["author"]["id"].to_i,
26
+ post["author"]["username"],
27
+ post["author"]["display_name"],
28
+ post["author"]["url"],
29
+ post["author"]["email_hash"],
30
+ post["author"]["has_avatar"]
31
+ )
32
+ end
33
+ Post.new(
34
+ post["id"],
35
+ thread.forum,
36
+ thread,
37
+ post["created_at"],
38
+ post["message"],
39
+ post["parent_post"],
40
+ post["shown"],
41
+ post["is_anonymous"],
42
+ author
43
+ )
44
+ end
45
+ posts.sort!{|a,b| a.created_at <=> b.created_at}
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+
@@ -0,0 +1,73 @@
1
+ module Disqus
2
+
3
+ class Thread
4
+ attr_reader :id, :forum, :slug, :title, :created_at, :allow_comments, :url, :identifier, :forum
5
+
6
+ def initialize(id, forum, slug, title, created_at, allow_comments, url, identifier)
7
+ @id, @forum, @slug, @title, @created_at, @allow_comments, @url, @identifier = id.to_i, forum, slug, title, Time.parse(created_at.to_s), allow_comments, url, identifier
8
+ @posts = []
9
+ end
10
+
11
+ # Threads are equal if all their attributes are equal.
12
+ def ==(other_thread)
13
+ id == other_thread.id &&
14
+ forum == other_thread.forum &&
15
+ slug == other_thread.slug &&
16
+ title == other_thread.title &&
17
+ created_at == other_thread.created_at &&
18
+ allow_comments == other_thread.allow_comments &&
19
+ url == other_thread.url &&
20
+ identifier == other_thread.identifier
21
+ end
22
+
23
+ # Returns an array of Thread objects representing the threads belonging to the given Forum.
24
+ def self.list(forum, opts = {})
25
+ response = Disqus::Api::get_thread_list(opts.merge(:forum_id =>forum.id, :forum_api_key => forum.key))
26
+ if response["succeeded"]
27
+ list = response["message"].map do |thread|
28
+ Thread.new(
29
+ thread["id"],
30
+ forum,
31
+ thread["slug"],
32
+ thread["title"],
33
+ thread["created_at"],
34
+ thread["allow_comments"],
35
+ thread["url"],
36
+ thread["identifier"]
37
+ )
38
+ end
39
+ end
40
+ end
41
+
42
+ # Returns an array of posts belonging to this thread.
43
+ def posts(force_update = false)
44
+ if (@posts.nil? or @posts.empty? or force_update)
45
+ @posts = Disqus::Post.list(self)
46
+ end
47
+ @posts
48
+ end
49
+
50
+ # Sets the provided values on the thread object.
51
+ #
52
+ # Options:
53
+ #
54
+ # * :thread_id
55
+ # * :title
56
+ # * :slug
57
+ # * :url
58
+ # * :allow_comments
59
+ def update(opts = {})
60
+ result = Disqus::Api::update_thread(opts.merge(
61
+ :forum_api_key => forum.key,
62
+ :thread_id => id,
63
+ :title => title,
64
+ :slug => slug,
65
+ :url => url,
66
+ :allow_comments => allow_comments)
67
+ )
68
+ return result["succeeded"]
69
+ end
70
+
71
+ end
72
+
73
+ end
@@ -0,0 +1,8 @@
1
+ module Disqus #:nodoc:
2
+ module Version #:nodoc:
3
+ MAJOR = 1
4
+ MINOR = 0
5
+ TINY = 1
6
+ STRING = [MAJOR, MINOR, TINY].join('.')
7
+ end
8
+ end
@@ -0,0 +1,45 @@
1
+ module Disqus
2
+
3
+ # Shortcuts to access the widgets as simple functions as opposed to using
4
+ # their full qualified names. These helpers are loaded automatically in
5
+ # Rails and Merb apps.
6
+ #
7
+ # For Sinatra, Camping, Nitro or other frameworks, you can include the
8
+ # helper if you wish, or use the fully-qualified names. Really this is just
9
+ # here for aesthetic purposes and to make it less likely to step on anyone's
10
+ # namespace.
11
+ module ViewHelpers
12
+
13
+ # See Disqus::Widget.thread
14
+ def disqus_thread(options = {})
15
+ Disqus::Widget::thread(options)
16
+ end
17
+
18
+ # See Disqus::Widget.comment_counts
19
+ def disqus_comment_counts(options = {})
20
+ Disqus::Widget::comment_counts(options)
21
+ end
22
+
23
+ # See Disqus::Widget.top_commenters
24
+ def disqus_top_commenters(options = {})
25
+ Disqus::Widget::top_commenters(options)
26
+ end
27
+
28
+ # See Disqus::Widget.popular_threads
29
+ def disqus_popular_threads(options = {})
30
+ Disqus::Widget::popular_threads(options)
31
+ end
32
+
33
+ # See Disqus::Widget.recent_comments
34
+ def disqus_recent_comments(options = {})
35
+ Disqus::Widget::recent_comments(options)
36
+ end
37
+
38
+ # See Disqus::Widget.combo
39
+ def disqus_combo(options = {})
40
+ Disqus::Widget::combo(options)
41
+ end
42
+
43
+ end
44
+
45
+ end
@@ -0,0 +1,185 @@
1
+ module Disqus
2
+
3
+ # Disqus Widget generator.
4
+ #
5
+ # All of the methods accept various options, and "account" is required for
6
+ # all of them. You can avoid having to pass in the account each time by
7
+ # setting it in the defaults like this:
8
+ #
9
+ # Disqus::defaults[:account] = "my_account"
10
+ class Widget
11
+
12
+ VALID_COLORS = ['blue', 'grey', 'green', 'red', 'orange']
13
+ VALID_NUM_ITEMS = 5..20
14
+ VALID_DEFAULT_TABS = ['people', 'recent', 'popular']
15
+ VALID_AVATAR_SIZES = [24, 32, 48, 92, 128]
16
+ VALID_ORIENTATIONS = ['horizontal', 'vertical']
17
+
18
+ ROOT_PATH = 'http://disqus.com/forums/%s/'
19
+ THREAD = ROOT_PATH + 'embed.js'
20
+ COMBO = ROOT_PATH + 'combination_widget.js?num_items=%d&color=%s&default_tab=%s'
21
+ RECENT = ROOT_PATH + 'recent_comments_widget.js?num_items=%d&avatar_size=%d'
22
+ POPULAR = ROOT_PATH + 'popular_threads_widget.js?num_items=%d'
23
+ TOP = ROOT_PATH + 'top_commenters_widget.js?num_items=%d&avatar_size=%d&orientation=%s'
24
+ class << self
25
+
26
+ # Show the main Disqus thread widget.
27
+ # Options:
28
+ # * <tt>account:</tt> Your Discus account (required).
29
+ def thread(opts = {})
30
+ opts = Disqus::defaults.merge(opts)
31
+ opts[:view_thread_text] ||= "View the discussion thread"
32
+ validate_opts!(opts)
33
+ s = ''
34
+ if opts[:developer]
35
+ s << '<script type="text/javascript">var disqus_developer = 1;</script>'
36
+ end
37
+ s << '<div id="disqus_thread"></div>'
38
+ s << '<script type="text/javascript" src="' + THREAD + '"></script>'
39
+ s << '<noscript><a href="http://%s.disqus.com/?url=ref">'
40
+ s << opts[:view_thread_text]
41
+ s << '</a></noscript>'
42
+ if opts[:show_powered_by]
43
+ s << '<a href="http://disqus.com" class="dsq-brlink">blog comments '
44
+ s << 'powered by <span class="logo-disqus">Disqus</span></a>'
45
+ end
46
+ s % [opts[:account], opts[:account]]
47
+ end
48
+
49
+ # Loads Javascript to show the number of comments for the page.
50
+ #
51
+ # The Javascript sets the inner html to the comment count for any links
52
+ # on the page that have the anchor "disqus_thread". For example, "View
53
+ # Comments" below would be replaced by "1 comment" or "23 comments" etc.
54
+ #
55
+ # <a href="http://my.website/article-permalink#disqus_thread">View Comments</a>
56
+ # <a href="http://my.website/different-permalink#disqus_thread">View Comments</a>
57
+ # Options:
58
+ # * <tt>account:</tt> Your Discus account (required).
59
+ def comment_counts(opts = {})
60
+ opts = Disqus::defaults.merge(opts)
61
+ validate_opts!(opts)
62
+ s = <<-WHIMPER
63
+ <script type="text/javascript">
64
+ //<[CDATA[
65
+ (function() {
66
+ var links = document.getElementsByTagName('a');
67
+ var query = '?';
68
+ for(var i = 0; i < links.length; i++) {
69
+ if(links[i].href.indexOf('#disqus_thread') >= 0) {
70
+ query += 'url' + i + '=' + encodeURIComponent(links[i].href) + '&';
71
+ }
72
+ }
73
+ document.write('<' + 'script type="text/javascript" src="#{ROOT_PATH}get_num_replies.js' + query + '"></' + 'script>');
74
+ })();
75
+ //]]>
76
+ </script>
77
+ WHIMPER
78
+ s % opts[:account]
79
+ end
80
+
81
+ # Show the top commenters Disqus thread widget.
82
+ # Options:
83
+ # * <tt>account:</tt> Your Discus account (required).
84
+ # * <tt>header:</tt> HTML snipper with header (default h2) tag and text.
85
+ # * <tt>show_powered_by:</tt> Show or hide the powered by Disqus text.
86
+ # * <tt>num_items:</tt>: How many items to show.
87
+ # * <tt>hide_mods:</tt> Don't show moderators.
88
+ # * <tt>hide_avatars:</tt> Don't show avatars.
89
+ # * <tt>avatar_size:</tt> Avatar size.
90
+ def top_commenters(opts = {})
91
+ opts = Disqus::defaults.merge(opts)
92
+ opts[:header] ||= '<h2 class="dsq-widget-title">Top Commenters</h2>'
93
+ validate_opts!(opts)
94
+ s = '<div id="dsq-topcommenters" class="dsq-widget">'
95
+ s << opts[:header]
96
+ s << '<script type="text/javascript" src="'
97
+ s << TOP
98
+ s << '&hide_avatars=1' if opts[:hide_avatars]
99
+ s << '&hide_mods=1' if opts[:hide_mods]
100
+ s << '"></script>'
101
+ s << '</div>'
102
+ if opts[:show_powered_by]
103
+ s << '<a href="http://disqus.com">Powered by Disqus</a>'
104
+ end
105
+ s % [opts[:account], opts[:num_items], opts[:avatar_size], opts[:orientation]]
106
+ end
107
+
108
+ # Show the popular threads Disqus widget.
109
+ # Options:
110
+ # * <tt>account:</tt> Your Discus account (required).
111
+ # * <tt>header:</tt> HTML snipper with header (default h2) tag and text.
112
+ # * <tt>num_items:</tt>: How many items to show.
113
+ # * <tt>hide_mods:</tt> Don't show moderators.
114
+ def popular_threads(opts = {})
115
+ opts = Disqus::defaults.merge(opts)
116
+ opts[:header] ||= '<h2 class="dsq-widget-title">Popular Threads</h2>'
117
+ validate_opts!(opts)
118
+ s = '<div id="dsq-popthreads" class="dsq-widget">'
119
+ s << opts[:header]
120
+ s << '<script type="text/javascript" src="'
121
+ s << POPULAR
122
+ s << '&hide_mods=1' if opts[:hide_mods]
123
+ s << '"></script>'
124
+ s << '</div>'
125
+ s << '<a href="http://disqus.com">Powered by Disqus</a>' if opts[:show_powered_by]
126
+ s % [opts[:account], opts[:num_items]]
127
+ end
128
+
129
+ # Show the recent comments Disqus widget.
130
+ # Options:
131
+ # * <tt>account:</tt> Your Discus account (required).
132
+ # * <tt>header:</tt> HTML snipper with header (default h2) tag and text.
133
+ # * <tt>num_items:</tt>: How many items to show.
134
+ # * <tt>hide_avatars:</tt> Don't show avatars.
135
+ # * <tt>avatar_size:</tt> Avatar size.
136
+ def recent_comments(opts = {})
137
+ opts = Disqus::defaults.merge(opts)
138
+ opts[:header] ||= '<h2 class="dsq-widget-title">Recent Comments</h2>'
139
+ validate_opts!(opts)
140
+ s = '<div id="dsq-recentcomments" class="dsq-widget">'
141
+ s << opts[:header]
142
+ s << '<script type="text/javascript" src="'
143
+ s << RECENT
144
+ s << '&hide_avatars=1' if opts[:hide_avatars]
145
+ s << '"></script>'
146
+ s << '</div>'
147
+ if opts[:show_powered_by]
148
+ s << '<a href="http://disqus.com">Powered by Disqus</a>'
149
+ end
150
+ s % [opts[:account], opts[:num_items], opts[:avatar_size]]
151
+ end
152
+
153
+ # Show the Disqus combo widget. This is a three-tabbed box with links
154
+ # popular threads, top posters, and recent threads.
155
+ # Options:
156
+ # * <tt>:account:</tt> Your Discus account (required).
157
+ # * <tt>:num_items:</tt> How many items to show.
158
+ # * <tt>:hide_mods:</tt> Don't show moderators.
159
+ # * <tt>:default_tab:</tt> Should be 'people', 'recent', or 'popular'.
160
+ def combo(opts = {})
161
+ opts = Disqus::defaults.merge(opts)
162
+ validate_opts!(opts)
163
+ s = '<script type="text/javascript" src="'
164
+ s << COMBO
165
+ s << '&hide_mods=1' if opts[:hide_mods]
166
+ s << '"></script>'
167
+ s % [opts[:account], opts[:num_items], opts[:color], opts[:default_tab]]
168
+ end
169
+
170
+ private
171
+
172
+ def validate_opts!(opts)
173
+ raise ArgumentError.new("You must specify an :account") if !opts[:account]
174
+ raise ArgumentError.new("Invalid color") if opts[:color] && !VALID_COLORS.include?(opts[:color])
175
+ raise ArgumentError.new("Invalid num_items") if opts[:num_items] && !VALID_NUM_ITEMS.include?(opts[:num_items])
176
+ raise ArgumentError.new("Invalid default_tab") if opts[:default_tab] && !VALID_DEFAULT_TABS.include?(opts[:default_tab])
177
+ raise ArgumentError.new("Invalid avatar size") if opts[:avatar_size] && !VALID_AVATAR_SIZES.include?(opts[:avatar_size])
178
+ raise ArgumentError.new("Invalid orientation") if opts[:orientation] && !VALID_ORIENTATIONS.include?(opts[:orientation])
179
+ end
180
+
181
+ end
182
+
183
+ end
184
+
185
+ end