├── README.md
├── assets
├── javascripts
│ └── discourse
│ │ ├── helpers
│ │ ├── latest-posts-category.js.es6
│ │ ├── leaderboard-list.js.es6
│ │ └── recent-replies.js.es6
│ │ ├── initializers
│ │ └── home-sidebar.js.es6
│ │ ├── templates
│ │ └── discovery.hbs
│ │ └── widgets
│ │ ├── sidebar-category-posts.js.es6
│ │ ├── sidebar-custom-content.js.es6
│ │ ├── sidebar-items.js.es6
│ │ ├── sidebar-latest-replies.js.es6
│ │ ├── sidebar-leaderboard.js.es6
│ │ ├── sidebar-post-item.js.es6
│ │ └── sidebar-reply-item.js.es6
└── stylesheets
│ └── sidebar.scss
├── config
├── locales
│ ├── client.en.yml
│ ├── client.es.yml
│ ├── client.sq.yml
│ ├── server.en.yml
│ └── server.es.yml
└── settings.yml
└── plugin.rb
/README.md:
--------------------------------------------------------------------------------
1 | # discourse-sidebar-blocks
2 |
3 | A discourse plugin that adds a sidebar to topic lists (discovery) with several blocks available: posts from a category, recent replies
4 |
5 | ## Support and more info
6 | Please [read and discuss here](https://meta.discourse.org/t/discourse-sidebar-blocks/51457).
7 |
8 | ## ⚠️ This plugin is deprecated
9 |
10 | Please don't use this plugin, https://github.com/discourse/discourse-right-sidebar-blocks is now a much better option.
11 |
--------------------------------------------------------------------------------
/assets/javascripts/discourse/helpers/latest-posts-category.js.es6:
--------------------------------------------------------------------------------
1 | export function getLatestPosts(context) {
2 | const store = context.register.lookup('service:store');
3 | var filter = "c/" + context.attrs.category;
4 | return store.findFiltered("topicList", {filter: filter}).then((result) => {
5 | return result.topic_list.topics;
6 | }).catch(() => {
7 | console.log('getting topic list failed')
8 | return [];
9 | })
10 | }
11 |
--------------------------------------------------------------------------------
/assets/javascripts/discourse/helpers/leaderboard-list.js.es6:
--------------------------------------------------------------------------------
1 | import { ajax } from 'discourse/lib/ajax';
2 |
3 | export function getLeaderboardList(context) {
4 | const period = context.siteSettings.sidebar_leaderboard_period;
5 | const count = context.siteSettings.sidebar_leaderboard_count;
6 | return ajax('/directory_items.json?period='+period+'&order=likes_received').then(function (result) {
7 | // console.log("Leaderboard list :", result);
8 | return result.directory_items.splice(0,count);
9 | }).catch(() => {
10 | // console.log('getting user list failed')
11 | return [];
12 | })
13 | }
14 |
--------------------------------------------------------------------------------
/assets/javascripts/discourse/helpers/recent-replies.js.es6:
--------------------------------------------------------------------------------
1 | import { ajax } from 'discourse/lib/ajax';
2 |
3 | export function getLatestReplies(context) {
4 | return ajax('/posts.json').then(function (result) {
5 | return result.latest_posts;
6 | }).catch(() => {
7 | console.log('getting topic list failed')
8 | return [];
9 | })
10 | }
11 |
--------------------------------------------------------------------------------
/assets/javascripts/discourse/initializers/home-sidebar.js.es6:
--------------------------------------------------------------------------------
1 | export default {
2 | name: 'home-sidebar',
3 | initialize(){
4 |
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/assets/javascripts/discourse/templates/discovery.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{discourse-banner user=currentUser banner=site.banner}}
3 |
4 |
5 |
6 |
7 | {{outlet "navigation-bar"}}
8 |
9 |
10 |
11 |
12 |
33 |
34 | {{#if siteSettings.sidebar_enable}}
35 |
38 | {{/if}}
39 |
40 | {{plugin-outlet name="discovery-below" tagName="span" connectorTagName="div"}}
41 |
--------------------------------------------------------------------------------
/assets/javascripts/discourse/widgets/sidebar-category-posts.js.es6:
--------------------------------------------------------------------------------
1 | import { createWidget } from "discourse/widgets/widget";
2 | import { getLatestPosts } from "discourse/plugins/discourse-sidebar-blocks/discourse/helpers/latest-posts-category";
3 | import { categoryBadgeHTML } from "discourse/helpers/category-link";
4 | import RawHtml from "discourse/widgets/raw-html";
5 | import { h } from "virtual-dom";
6 | import Category from "discourse/models/category";
7 |
8 | export default createWidget("sidebar-category-posts", {
9 | tagName: "div.sidebar-category-posts",
10 | buildKey: (attrs) => `sidebar-category-posts-${attrs.category}`,
11 | defaultState() {
12 | return { loading: false };
13 | },
14 | buildClasses(attrs) {
15 | const result = [];
16 | if (attrs.category) {
17 | result.push(`sidebar-c-${attrs.category}`);
18 | }
19 | return result;
20 | },
21 |
22 | refreshTopics() {
23 | if (this.state.loading) {
24 | return;
25 | }
26 | this.state.loading = true;
27 | this.state.topics = "empty";
28 | getLatestPosts(this).then((result) => {
29 | for (var i = result.length - 1; i >= 0; i--) {
30 | // remove archived posts
31 | if (result[i].archived) {
32 | result.splice(i, 1);
33 | }
34 | }
35 |
36 | if (result.length) {
37 | var max = parseInt(this.siteSettings.sidebar_num_results) - 1;
38 | for (var i = result.length - 1; i >= 0; i--) {
39 | if (i > max) {
40 | result.splice(i, 1);
41 | }
42 | }
43 | this.state.topics = result;
44 | } else {
45 | this.state.topics = "empty";
46 | }
47 | this.state.loading = false;
48 | this.scheduleRerender();
49 | });
50 | },
51 |
52 | html(attrs, state) {
53 | if (!state.topics) {
54 | this.refreshTopics();
55 | }
56 | const result = [];
57 | if (state.loading) {
58 | result.push(h("div.spinner-container", h("div.spinner")));
59 | } else if (state.topics !== "empty") {
60 | var category = Category.findBySlug(attrs.category);
61 | result.push(h("div", { innerHTML: categoryBadgeHTML(category) }));
62 | const topicItems = state.topics.map((t) =>
63 | this.attach("sidebar-post-item", t)
64 | );
65 | result.push(h("div", [topicItems]));
66 | } else {
67 | var category = Category.findBySlug(attrs.category);
68 | result.push(h("div", { innerHTML: categoryBadgeHTML(category) }));
69 | result.push(h("div.no-messages", "No posts in this category."));
70 | }
71 |
72 | return result;
73 | },
74 | });
75 |
--------------------------------------------------------------------------------
/assets/javascripts/discourse/widgets/sidebar-custom-content.js.es6:
--------------------------------------------------------------------------------
1 | import { createWidget } from 'discourse/widgets/widget';
2 | import { h } from 'virtual-dom';
3 |
4 | createWidget('sidebar-custom-content', {
5 | tagName: 'div.sidebar-custom-content',
6 |
7 | html(attrs) {
8 | return h('div', {innerHTML: this.siteSettings.sidebar_custom_content});
9 | },
10 | });
11 |
--------------------------------------------------------------------------------
/assets/javascripts/discourse/widgets/sidebar-items.js.es6:
--------------------------------------------------------------------------------
1 | import { createWidget } from 'discourse/widgets/widget';
2 | import { h } from 'virtual-dom';
3 |
4 | export default createWidget('sidebar-items', {
5 | tagName: 'div.sidebar-items',
6 | buildKey: () => 'sidebar-items',
7 |
8 | html(attrs, state) {
9 | if (!this.siteSettings.sidebar_enable || this.site.mobileView)
10 | return;
11 |
12 | var sidebarBlocks = this.siteSettings.sidebar_block_order.split("|");
13 |
14 | const result = [];
15 | var self = this;
16 |
17 | sidebarBlocks.map(function(item) {
18 | if (item == 'latest_replies') {
19 | result.push(self.attach('sidebar-latest-replies'));
20 | } else if (item == 'custom_html') {
21 | result.push(self.attach('sidebar-custom-content'));
22 | } else if (item == 'leaderboard') {
23 | result.push(self.attach('sidebar-leaderboard'));
24 | } else {
25 | result.push(self.attach('sidebar-category-posts', {category: item}));
26 | }
27 | });
28 |
29 | return result;
30 | },
31 |
32 | });
33 |
--------------------------------------------------------------------------------
/assets/javascripts/discourse/widgets/sidebar-latest-replies.js.es6:
--------------------------------------------------------------------------------
1 | import { createWidget } from 'discourse/widgets/widget';
2 | import { getLatestReplies } from 'discourse/plugins/discourse-sidebar-blocks/discourse/helpers/recent-replies';
3 | import { h } from 'virtual-dom';
4 |
5 | export default createWidget('sidebar-latest-replies', {
6 | tagName: 'div.sidebar-latest-replies',
7 | buildKey: attrs => 'sidebar-latest-replies',
8 | defaultState() {
9 | return { loading: false };
10 | },
11 |
12 | refreshPosts() {
13 | if (this.state.loading) { return; }
14 | this.state.loading = true
15 | this.state.posts = 'empty'
16 | getLatestReplies(this).then((result) => {
17 | if (result.length) {
18 | for (var i = result.length - 1; i >= 0; i--) {
19 | // remove first post in a topic (not a reply)
20 | // remove any "post" that is merely an action
21 | // remove hidden posts
22 | if (result[i].post_number < 2 || result[i].action_code != undefined || result[i].hidden) {
23 | result.splice(i, 1);
24 | }
25 | }
26 |
27 | for (var i = result.length - 1; i >= 0; i--) {
28 | // limit to 5 max
29 | if (i > 4) {
30 | result.splice(i, 1);
31 | }
32 | }
33 | this.state.posts = result;
34 | } else {
35 | this.state.posts = 'empty'
36 | }
37 | this.state.loading = false
38 | this.scheduleRerender()
39 | })
40 | },
41 |
42 | html(attrs, state) {
43 | const messageBus = this.register.lookup('message-bus:main');
44 | messageBus.subscribe("/latest", data => {
45 | this.refreshPosts();
46 | });
47 |
48 | if (!state.posts) {
49 | this.refreshPosts();
50 | }
51 | const result = [];
52 | if (state.loading) {
53 | result.push(h('div.spinner-container', h('div.spinner')));
54 | } else if (state.posts !== 'empty') {
55 | result.push(h('h3.sidebar-heading', I18n.t('sidebar_blocks.recent_replies')));
56 | const replies = state.posts.map(t => this.attach('sidebar-reply-item', t));
57 | result.push(replies);
58 | } else {
59 | result.push(h('div.no-messages', 'No recent replies.'))
60 | }
61 |
62 | return result;
63 | },
64 |
65 | });
66 |
--------------------------------------------------------------------------------
/assets/javascripts/discourse/widgets/sidebar-leaderboard.js.es6:
--------------------------------------------------------------------------------
1 | import { createWidget } from 'discourse/widgets/widget';
2 | import { h } from 'virtual-dom';
3 | import { getLeaderboardList } from 'discourse/plugins/discourse-sidebar-blocks/discourse/helpers/leaderboard-list';
4 | import { iconNode } from "discourse-common/lib/icon-library";
5 |
6 | createWidget('sidebar-leaderboard', {
7 | tagName: 'div.sidebar-leaderboard',
8 | buildKey: attrs => 'sidebar-leaderboard',
9 | defaultState() {
10 | return { loading: false };
11 | },
12 | refreshUsers() {
13 | if (this.state.loading) { return; }
14 | this.state.loading = true
15 | this.state.users = 'empty'
16 | getLeaderboardList(this).then((result) => {
17 | // console.log("refreshUsers List : ", result);
18 | if (result.length) {
19 | this.state.users = result;
20 | } else {
21 | this.state.users = 'empty'
22 | }
23 | this.state.loading = false
24 | this.scheduleRerender()
25 | })
26 | },
27 |
28 | leaderboardRow(user){
29 | // console.log("user : ", user);
30 | return h("tr",
31 | {
32 | "attributes": {
33 | "data-user-card": user.user.username
34 | }
35 | },
36 | [
37 | h('td', [
38 | h('div.useravatar', this.attach('topic-participant', user.user)),
39 | h('div.username.trigger-data-card', user.user.username)
40 | ]),
41 | h('td', h('span.points', user.likes_received+''))
42 | ]);
43 | },
44 |
45 | leaderboardHeader(){
46 | return h(
47 | 'h3.sidebar-heading', h(
48 | 'a', {
49 | 'attributes':{
50 | 'href':'/u',
51 | 'title':'Leaderboard'
52 | }
53 | },
54 | 'Leaderboard'
55 | )
56 | );
57 | },
58 |
59 | html(attrs, state) {
60 | if (!state.users) {
61 | this.refreshUsers();
62 | }
63 | const result = [];
64 | if (state.loading) {
65 | result.push(h('div.spinner-container', h('div.spinner')));
66 | } else if (state.users !== 'empty') {
67 | result.push(this.leaderboardHeader());
68 |
69 | const users = state.users.map( user => this.leaderboardRow(user))
70 |
71 | result.push(
72 | h("div.directory",
73 | h("table", [
74 | h("tbody", [
75 | h("tr", [
76 | h("th", 'User'),
77 | h("th", [
78 | iconNode('heart'),
79 | h('span','Received')
80 | ])
81 | ]),
82 | users
83 | ])
84 | ])
85 | )
86 | );
87 | } else {
88 | result.push(h('div.no-messages', 'No users loaded.'))
89 | }
90 |
91 | return result;
92 | },
93 |
94 | });
95 |
--------------------------------------------------------------------------------
/assets/javascripts/discourse/widgets/sidebar-post-item.js.es6:
--------------------------------------------------------------------------------
1 | import { createWidget } from 'discourse/widgets/widget';
2 | import { h } from 'virtual-dom';
3 | import getURL from "discourse-common/lib/get-url";
4 |
5 | createWidget('sidebar-post-item', {
6 | tagName: 'div.sidebar-post-item',
7 |
8 | html(attrs) {
9 | var url = getURL("/t/") + attrs.slug + "/" + attrs.id;
10 | return [
11 | h('a.item-title', {
12 | attributes: { href: url}
13 | }, attrs.title),
14 | h('span.comment_count', {}, attrs.posts_count - 1),
15 | // h('span', this.attach('featured-link', {topic: attrs}))
16 | ]
17 | },
18 | });
19 |
--------------------------------------------------------------------------------
/assets/javascripts/discourse/widgets/sidebar-reply-item.js.es6:
--------------------------------------------------------------------------------
1 | import { createWidget } from 'discourse/widgets/widget';
2 | import { h } from 'virtual-dom';
3 | import { dateNode } from 'discourse/helpers/node';
4 | import getURL from "discourse-common/lib/get-url";
5 |
6 | createWidget('sidebar-reply-item', {
7 | tagName: 'div.sidebar-reply-item',
8 |
9 | html(attrs) {
10 | var url = getURL("/t/") + attrs.topic_slug + "/" + attrs.topic_id + "/" + attrs.id;
11 |
12 | var limit = 125;
13 | var excerpt = $(attrs.cooked).text();
14 | if (excerpt.length > limit) {
15 | excerpt = $('').html(attrs.cooked);
16 | excerpt.find("img,aside").remove();
17 | excerpt = excerpt.text().substring(0, limit).trim(this) + "...";
18 | }
19 |
20 | const createdAt = new Date(attrs.created_at);
21 |
22 | return [
23 | h('span.avatar', this.attach('post-avatar', attrs)),
24 | h('span.reply-date', {}, dateNode(createdAt)),
25 | h('span.excerpt', excerpt),
26 | h('a.item-title', {
27 | attributes: { href: url}
28 | }, attrs.topic_title)
29 | ]
30 | },
31 | });
32 |
--------------------------------------------------------------------------------
/assets/stylesheets/sidebar.scss:
--------------------------------------------------------------------------------
1 | html:not(.mobile-view) {
2 | .has-sidebar + .sidebar {
3 | width: 30%;
4 | float: right;
5 | min-height: 100px;
6 | }
7 |
8 | .list-container.has-sidebar {
9 | width: 65%;
10 | float: left;
11 | }
12 |
13 | .sidebar-category-posts {
14 | margin-bottom: 30px;
15 |
16 | h3 {
17 | text-transform: capitalize;
18 | padding-bottom: 8px;
19 | }
20 |
21 | .sidebar-post-item {
22 | padding-bottom: 6px;
23 | border-bottom: 1px solid #F2F2F2;
24 | margin-bottom: 6px;
25 | }
26 |
27 | span.comment_count {
28 | margin-left: 6px;
29 | opacity: 0.5;
30 | }
31 |
32 | a.item-title {
33 | color: #222;
34 | font-size: 1.143em;
35 | }
36 | }
37 |
38 | .sidebar-latest-replies {
39 | margin-bottom: 30px;
40 |
41 | h3 {
42 | padding-bottom: 8px;
43 | }
44 | }
45 |
46 | .sidebar-reply-item {
47 | clear: both;
48 | padding: 6px 0px;
49 | border-bottom: 1px solid #F2F2F2;
50 | margin-bottom: 6px;
51 | font-size: 0.94em;
52 | line-height: 16px;
53 |
54 | > .avatar {
55 | display: block;
56 | float: left;
57 | width: 30px;
58 | margin-right: 10px;
59 | margin-bottom: 5px;
60 |
61 | .topic-avatar {
62 | width: 100%;
63 | border-top: none;
64 | padding-top: 2px;
65 | float: none;
66 | }
67 |
68 | img {
69 | width: 30px;
70 | height: 30px;
71 | }
72 | }
73 |
74 | > span.reply-date {
75 | float: right;
76 | display: block;
77 | opacity: 0.5;
78 | width: 35px;
79 | text-align: right;
80 | }
81 |
82 | > span.excerpt {
83 | margin-right: 35px;
84 | display: block;
85 | }
86 |
87 | a.item-title {
88 | margin-left: 40px;
89 | padding: 5px 0px;
90 | display: block;
91 | }
92 | }
93 |
94 | .sidebar-custom-content {
95 | margin-bottom: 30px;
96 | }
97 |
98 | .sidebar-leaderboard {
99 | td {
100 | .username {
101 | padding-left: 40px;
102 | padding-top: 3%;
103 | }
104 |
105 | .useravatar {
106 | float: left;
107 | }
108 | }
109 |
110 | .points {
111 | margin-left: 30px;
112 | }
113 |
114 | .fa-heart, .d-icon-heart {
115 | color: red;
116 | padding-right: 0.5em;
117 | }
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/config/locales/client.en.yml:
--------------------------------------------------------------------------------
1 | en:
2 | js:
3 | sidebar_blocks:
4 | recent_replies: "Recent replies"
--------------------------------------------------------------------------------
/config/locales/client.es.yml:
--------------------------------------------------------------------------------
1 | es:
2 | js:
3 | sidebar_blocks:
4 | recent_replies: "Respuestas Recientes"
5 |
--------------------------------------------------------------------------------
/config/locales/client.sq.yml:
--------------------------------------------------------------------------------
1 | sq:
2 | js:
3 | sidebar_blocks:
4 | recent_replies: "Komentet e fundit"
--------------------------------------------------------------------------------
/config/locales/server.en.yml:
--------------------------------------------------------------------------------
1 | en:
2 | site_settings:
3 | sidebar_enable: "Show sidebar on the right list screens."
4 | sidebar_block_order: "Include blocks for the sidebar. Enter category slugs, 'latest_replies' for the latest replies block and/or 'custom_html' for the custom HTML from the field below."
5 | sidebar_custom_content: "Custom HTML content. To show this, you must include 'custom_html' in block list above."
6 | sidebar_num_results: "Number of category posts to display on each sidebar block."
7 | sidebar_leaderboard_count: "Number of users to show in leaderboard."
8 | sidebar_leaderboard_period: "Period for leaderboard - Possible values - daily,weekly,monthly,quarterly,yearly,all"
9 |
--------------------------------------------------------------------------------
/config/locales/server.es.yml:
--------------------------------------------------------------------------------
1 | es:
2 | site_settings:
3 | sidebar_enable: "Mostrar sidebar en la parte derecha de la pantalla."
4 | sidebar_block_order: "incluir bloques para el sidebar. Ingrese 'latest_replies' para el bloque de últimas respuestas y/o 'custom_html' para el bloque de HTML personalizado."
5 | sidebar_custom_content: "Contenido HTML personalizado. Para mostrar esto, debes incluir 'custom_html' en el bloque listado arriba."
6 | sidebar_num_results: "Número de temas por categoría a mostrar en cada bloque del sidebar."
7 | sidebar_leaderboard_count: "Número de usuarios a mostrar en el leaderboard."
8 | sidebar_leaderboard_period: "Periodos para el leaderboard - Valores posibles: daily,weekly,monthly,quarterly,yearly,all"
9 |
--------------------------------------------------------------------------------
/config/settings.yml:
--------------------------------------------------------------------------------
1 | plugins:
2 | sidebar_enable:
3 | default: false
4 | client: true
5 | sidebar_block_order:
6 | client: true
7 | type: list
8 | default: "latest_replies"
9 | sidebar_custom_content:
10 | type: text
11 | default: ''
12 | client: true
13 | sidebar_num_results:
14 | default: '6'
15 | client: true
16 | sidebar_leaderboard_count:
17 | default: '5'
18 | client: true
19 | sidebar_leaderboard_period:
20 | default: 'daily'
21 | client: true
22 |
--------------------------------------------------------------------------------
/plugin.rb:
--------------------------------------------------------------------------------
1 | # name: discourse-sidebar-blocks
2 | # about: add a sidebar to topic lists (discovery) with several blocks available: posts from a category, recent replies
3 | # version: 0.1
4 | # authors: pmusaraj
5 | # url: https://github.com/pmusaraj/discourse-home-sidebar
6 |
7 | register_asset "stylesheets/sidebar.scss"
--------------------------------------------------------------------------------