Skip to content

Commit 9c1fa88

Browse files
author
Joshua Villagomez
committed
KB is now project-based. New view permissions added. Adjusted README.
1 parent a22d191 commit 9c1fa88

32 files changed

+305
-177
lines changed

Diff for: AUTHORS

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Contributors:
1010
* shoorick
1111
* fabiob
1212
* Martin Denizet
13+
* whqcd
1314

1415
Documentation:
1516
* Alan Bowman

Diff for: CHANGES

+6
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,9 @@
1010
the setting in the configuration panel (default => true)
1111
* cleanup category list on the main page
1212
* fixed comment creation popup not being displayed
13+
14+
0.3.2 (11-JUL-23)
15+
-----------------
16+
* Wiki macros for articles and categories.
17+
* Knowledgebase is now project-based, fully configurable through permissions.
18+

Diff for: README.markdown

+35-17
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
# Redmine Knowledgebase plugin
22

3-
This plugin adds generic knowledgebase funcationlity to the redmine project management application.
3+
This plugin adds a professional knowledgebase functionality to the Redmine project management application.
44

55
## Introduction
66

7-
Redmine (www.redmine.org) is just plain awesome, and has proven to provide 90% of the functionality I need. The one feature that was missing was a usable knowledgebase component. I've looked at some of the open source solutions available, but couldn't find anything that fit my needs exactly. Seeing as redmine is so easily extended, I figured why not create it for this platform instead of starting yet another project from scratch :P
7+
Redmine (www.redmine.org) is just plain awesome, and has proven to provide 90% of the functionality I need. The one feature that was missing was a usable knowledgebase component. I've looked at some of the open source solutions available, but couldn't find anything that fit my needs exactly. Seeing as Redmine is so easily extended, I figured why not create it for this platform instead of starting yet another project from scratch :P
88

99
## Features
1010

11+
* Knowledgebase articles per project
1112
* Categorization of Articles
1213
* Article ratings
1314
* File attachments
1415
* Article Comments
1516
* Article Tags
17+
* Wiki macros
1618
* Permissions
1719

1820
## Requirements
@@ -63,22 +65,13 @@ need to restart Redmine for the plugin to be available.
6365

6466
## Overview
6567

66-
Once the Redmine Knowledgebase Plugin is installed, there will be a
67-
Knowledgebase link in the Redmine navigation header. Unlike the Redmine
68-
Wiki, which is only available at the per-project or subproject level,
69-
the Knowledgebase is available at the root level of the Redmine
70-
application.
68+
Once the Redmine Knowledgebase Plugin is installed, Knowledgebase articles are managed and controlled at the project level. After enabling and setting the right roles/permissions, each project will have its own categories and articles. Knowledgebase will appear as a tab view per project.
7169

7270
## Configuring the Knowledgebase
7371

74-
To start using the Knowledgebase plugin, click on the Knowledgebase link
75-
in the Redmine navigation header.
72+
To start using the Knowledgebase plugin, go to your project Settings -> Modules and enable Knowledgebase. Then, go to Administration -> Roles and Permissions. Select a role (e.g. Non member). Under Knowledgebase, add at least Manage Articles and Manage Article Categories permission (to test functionality; later apply real permissions for your users). Select any other that apply and Save. See Permissions section for more information.
7673

77-
This takes you to the default page for
78-
the Knowledgebase, with this text: *"No articles have been added to the
79-
knowledgebase yet. Maybe you want to get the ball rolling ..."*. To the
80-
right is a green circle with a plus (+) sign, and the link to add a New
81-
Category.
74+
This takes you to the default page for the Knowledgebase, with this text: *"There have been no articles added"*. To the right is a green circle with a plus (+) sign, and the link to add a New Category.
8275

8376
Click on **New Category**. This takes you to the Create Category page.
8477

@@ -110,11 +103,28 @@ use that to navigate to any category or sub-category. You can also
110103
navigate to categories (not sub-categories) by clicking on the category
111104
name in the right hand *Browse by Category* side bar.
112105

106+
### Knowledgebase Permissions
107+
108+
The following permissions are available:
109+
110+
* View articles
111+
* Create articles
112+
* Edit articles
113+
* Manage articles
114+
* Create article categories
115+
* Comment and rate articles
116+
* Manage articles comments
117+
* Manage article categories
118+
* View newest articles
119+
* View most popular articles
120+
* View top rated articles
121+
* View recently updated articles
122+
123+
For granular permissions, create specific Knowledgebase roles, associate the permissions and add them to a project. The Knowledebase home page statistic quadrants will appear or dissapear, based on the View permissions.
113124

114125
## Using the Knowledgebase
115126

116-
Once you have created categories, you can then add articles and
117-
sub-categories.
127+
Once you have enabled the module, set proper permissions and created categories, you can then add articles and sub-categories.
118128

119129
### Creating Sub-categories
120130

@@ -190,7 +200,15 @@ You can easily create links to articles and categories from issues or Wiki pages
190200
* {{article(<article_id>)}} will render a link to the "kb#<article_id>: <article_title>" format.
191201
* {{category(<category_id>)}} will render a link to the "<category_title>" format, only renders the title of the category.
192202

203+
For example: {{kb(71)}} will render a link: KB#71
204+
193205
## Knowledgebase Home Page
194206

195-
Now that categories and articles have been created, the Home page of the Knowledgebase will show the *Newest Articles*, *Recently Updated Articles*, *Most Popular Articles*, and *Top Rated Articles*. You can use this page to help navigate the Knowledgebase, as well as using the *Jump to Category* drop down menu or the *Browse by Category* menu on the right of the screen. You can reach this page from anywhere inside the Knowledgebase by clicking on the Home link.
207+
Now that categories and articles have been created, the Home page of the Knowledgebase will show the *Newest Articles*, *Recently Updated Articles*, *Most Popular Articles*, and *Top Rated Articles* base on view permissions you have granted. You can use this page to help navigate the Knowledgebase, as well as using the *Jump to Category* drop down menu or the *Browse by Category* menu on the right of the screen. You can reach this page from anywhere inside the Knowledgebase by clicking on the Home link.
208+
209+
**Note:** URL Links to categories or articles will appear as follows:
210+
211+
* Categories: http://redmine-server.foo.bar/categories/[number]
212+
213+
* Articles: http://redmine-server.foo.bar/articles/[number]
196214

Diff for: app/controllers/articles_controller.rb

+24-5
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,27 @@ class ArticlesController < KnowledgebaseController
55
include AttachmentsHelper
66
include FaceboxRender
77

8-
#Authorize against global permissions defined in init.rb
9-
before_filter :authorize_global
8+
before_filter :find_project, :authorize
109

10+
def find_project
11+
if !params[:project_id].nil?
12+
@project=Project.find(params[:project_id])
13+
elsif !params[:category_id].nil?
14+
@project=Category.find(params[:category_id]).project
15+
elsif !params[:id].nil?
16+
@project=Article.find(params[:id]).category.project
17+
elsif !params[:article_id].nil?
18+
@project=Article.find(params[:article_id]).category.project
19+
end
20+
end
21+
1122
def new
1223
@article = Article.new
24+
25+
# only get root categories
26+
# which will then be fed into the function
27+
# this will make it render correctly
28+
@categories=@project.categories.find(:all, :conditions=>["parent_id IS Null"])
1329
@default_category = params[:category_id]
1430
@article.category_id = params[:category_id]
1531
end
@@ -20,14 +36,15 @@ def rate
2036
render :partial => "rating", :locals => {:article => @article}
2137
end
2238

23-
def create
39+
def create
2440
@article = Article.new(params[:article])
2541
@article.category_id = params[:category_id]
2642
@article.author_id = User.current.id
43+
@article.project_id=Category.find(params[:category_id]).project_id
2744
if @article.save
2845
attachments = attach(@article, params[:attachments])
2946
flash[:notice] = "Created Article " + @article.title
30-
redirect_to({ :controller => 'knowledgebase', :action => 'index' })
47+
redirect_to({ :controller=>'categories', :action=>'show', :id=>Category.find(params[:category_id])})
3148
else
3249
render(:action => 'new')
3350
end
@@ -41,6 +58,7 @@ def show
4158
end
4259

4360
def edit
61+
@categories=@project.categories.find(:all, :conditions=>["parent_id IS Null"])
4462
@article = Article.find(params[:id])
4563
end
4664

@@ -77,9 +95,10 @@ def destroy_comment
7795

7896
def destroy
7997
@article = Article.find(params[:id])
98+
category_id=@article.category.id
8099
@article.destroy
81100
flash[:notice] = "Article Removed"
82-
redirect_to({ :controller => 'knowledgebase', :action => 'index' })
101+
redirect_to({ :controller => 'categories', :action=>'show', :id=>category_id })
83102
end
84103

85104
def add_attachment

Diff for: app/controllers/categories_controller.rb

+21-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,19 @@
11
class CategoriesController < KnowledgebaseController
2-
unloadable
2+
unloadable
33

4-
#Authorize against global permissions defined in init.rb
5-
before_filter :authorize_global
4+
before_filter :find_project, :authorize
5+
6+
def find_project
7+
if !params[:project_id].nil?
8+
@project=Project.find(params[:project_id])
9+
elsif !params[:category_id].nil?
10+
@project=Category.find(params[:category_id]).project
11+
elsif !params[:parent_id].nil?
12+
@project=Category.find(params[:parent_id]).project
13+
elsif !params[:id].nil?
14+
@project=Category.find(params[:id]).project
15+
end
16+
end
617

718
def show
819
@category = Category.find(params[:id])
@@ -12,15 +23,18 @@ def show
1223
def new
1324
@category = Category.new
1425
@parent_id = params[:parent_id]
26+
@categories=@project.categories.find(:all, :conditions=>["parent_id Is Null"])
1527
end
1628

1729
def create
1830
@category = Category.new(params[:category])
31+
@category.project_id=@project.id
32+
1933
if @category.save
2034
# Test if the new category is a root category, and if more categories exist.
2135
# We check for a value > 1 because if this is the first entry, the category
2236
# count would be 1 (since the create operation already succeeded)
23-
if !params[:root_category] and Category.count > 1
37+
if !params[:root_category] and @project.categories.count > 1
2438
@category.move_to_child_of(Category.find(params[:parent_id]))
2539
end
2640

@@ -33,14 +47,16 @@ def create
3347

3448
def edit
3549
@category = Category.find(params[:id])
50+
@categories=@project.categories.find(:all, :conditions=>["parent_id Is Null"])
3651
end
3752

3853
def delete
3954
@category = Category.find(params[:id])
4055
if @category.articles.size == 0
56+
project_identifier=@category.project.identifier
4157
@category.destroy
4258
flash[:notice] = "Category deleted"
43-
redirect_to({ :controller => :knowledgebase, :action => 'index' })
59+
redirect_to({ :controller=>'knowledgebase', :action => 'index', :id=>project_identifier})
4460
else
4561
@articles = @category.articles.find(:all)
4662
flash[:error] = "Category is assigned to articles and could not be deleted."

Diff for: app/controllers/knowledgebase_controller.rb

+12-14
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
class KnowledgebaseController < ApplicationController
22
unloadable
33

4-
#Authorize against global permissions defined in init.rb
5-
before_filter :authorize_global, :unless => :allow_anonymous_access?
4+
before_filter :find_project, :authorize
65

76
rescue_from ActionView::MissingTemplate, :with => :force_404
87
rescue_from ActiveRecord::RecordNotFound, :with => :force_404
@@ -13,17 +12,18 @@ def index
1312
rescue
1413
summary_limit = 5
1514
end
15+
16+
article_table=Article.table_name
17+
@categories=@project.categories.find(:all, :conditions=>["parent_id IS NULL"])
18+
19+
@articles_newest = @project.articles.find(:all, :limit => summary_limit, :order => "#{article_table}.created_at DESC")
20+
@articles_updated = @project.articles.find(:all, :limit => summary_limit, :conditions => ["#{article_table}.created_at <> #{article_table}.updated_at"], :order => "#{article_table}.updated_at DESC")
1621

17-
@categories = Category.find(:all)
18-
@articles_newest = Article.find(:all, :limit => summary_limit, :order => 'created_at DESC')
19-
@articles_updated = Article.find(:all, :limit => summary_limit, :conditions => ['created_at <> updated_at'], :order => 'updated_at DESC')
20-
21-
#FIXME the following method still requires ALL records to be loaded before being filtered.
22-
23-
a = Article.find(:all, :include => :viewings).sort_by(&:view_count)
22+
a = @project.articles.find(:all, :include => :viewings).sort_by(&:view_count)
2423
a = a.drop(a.count - summary_limit) if a.count > summary_limit
2524
@articles_popular = a.reverse
26-
a = Article.find(:all, :include => :ratings).sort_by(&:rated_count)
25+
26+
a = @project.articles.find(:all, :include => :ratings).sort_by(&:rated_count)
2727
a = a.drop(a.count - summary_limit) if a.count > summary_limit
2828
@articles_toprated = a.reverse
2929
end
@@ -38,10 +38,6 @@ def is_user_logged_in
3838
end
3939
end
4040

41-
def allow_anonymous_access?
42-
Setting['plugin_redmine_knowledgebase']['knowledgebase_anonymous_access'].to_i == 1
43-
end
44-
4541
#######
4642
private
4743
#######
@@ -50,5 +46,7 @@ def force_404
5046
render_404
5147
end
5248

49+
50+
5351
end
5452

Diff for: app/helpers/knowledgebase_helper.rb

+3-16
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,14 @@
11
module KnowledgebaseHelper
22

3-
# Display a link if the user has a global permission
4-
def link_to_if_authorized_globally(name, options = {}, html_options = nil, *parameters_for_method_reference)
5-
if authorized_globally(options[:controller],options[:action])
6-
link_to(name, options, html_options, *parameters_for_method_reference)
7-
end
8-
end
9-
10-
def link_to_remote_if_authorized_globally(name, options = {}, html_options = nil, *parameters_for_method_reference)
11-
if authorized_globally(options[:controller],options[:action])
12-
link_to_remote(name, options, html_options, *parameters_for_method_reference)
13-
end
3+
def user_allowed(action,scope=nil)
4+
User.current.allowed_to?(action,scope)
145
end
156

16-
def authorized_globally(controller,action)
17-
User.current.allowed_to?({:controller => controller, :action => action}, nil, :global => true)
18-
end
19-
207
def format_article_summary(article, format)
218
output = nil
229
case format
2310
when "normal"
24-
output = textilizable article.summary
11+
output = article.summary
2512
when "newest"
2613
output = "Created " + time_ago_in_words(article.created_at) + " ago in " + link_to(article.category.title, {:controller => 'categories', :action => 'show', :id => article.category.id})
2714
when "updated"

Diff for: app/models/article.rb

+8-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ class Article < ActiveRecord::Base
66
validates_presence_of :title
77
validates_presence_of :category_id
88

9-
belongs_to :project # XXX association added to allow searching to work
9+
belongs_to :project # this will allow the article to be searched through, works along with init.rb
1010
belongs_to :category
1111
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
1212

@@ -18,13 +18,18 @@ class Article < ActiveRecord::Base
1818
acts_as_searchable :columns => [ "kb_articles.title", "kb_articles.content"],
1919
:include => [ :project ],
2020
:order_column => "kb_articles.id",
21-
:permission => nil
21+
:permission => nil,
22+
:date_column => "#{table_name}.created_at"
2223

24+
# this controls what text is displayed
25+
# when results come back as an article
2326
acts_as_event :title => Proc.new { |o| "#{l(:label_title_articles)} ##{o.id}: #{o.title}" },
2427
:description => Proc.new { |o| "#{o.content}" },
2528
:datetime => :created_at,
2629
:type => 'articles',
27-
:url => Proc.new { |o| {:controller => 'articles', :action => 'show', :id => nil, :article_id => o.id} }
30+
:url => Proc.new { |o| {:controller => 'articles', :action => 'show', :id => o.id}
31+
}
32+
2833

2934
has_many :comments, :as => :commented, :dependent => :delete_all, :order => "created_on"
3035

Diff for: app/models/category.rb

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ class Category < ActiveRecord::Base
55

66
validates_presence_of :title
77

8+
belongs_to :project
89
has_many :articles
910

1011
acts_as_nested_set :order => 'title'

Diff for: app/views/articles/_form.html.erb

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<div class="box tabular">
33
<p>
44
<label for="category_id"><%= l(:label_category) %></label>
5-
<%= select_tag :category_id, options_for_select(nested_set_options(Category) {|i| "#{'»' * i.level} #{i.title}" }, @article.category_id) %><br />
5+
<%= select_tag :category_id, options_for_select(nested_set_options(@categories) {|i| "#{'»' * i.level} #{i.title}" }, @article.category_id) %><br />
66
</p>
77
<p><%= f.text_field :title, :size => 60, :label => l(:label_title) %></p>
88
<p><%= f.text_area :summary, :cols => 60, :rows => 5, :class => 'wiki-edit', :label => l(:label_summary) %></p>
@@ -14,4 +14,4 @@
1414
</div>
1515

1616
<%= wikitoolbar_for 'article_summary' %>
17-
<%= wikitoolbar_for 'article_content' %>
17+
<%= wikitoolbar_for 'article_content' %>

Diff for: app/views/articles/comment.html.erb

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<div id="section-title" class="comments">Add Comment</div>
22

3-
<% form_tag({:action => 'add_comment', :id => @article_id}, :id => "add_comment_form") do %>
3+
<% form_tag({:action => 'add_comment', :id => @article_id,:project_id=>params[:project_id]}, :id => "add_comment_form" ) do %>
44
<div class="box">
55
<%= text_area 'comment', 'comments', :cols => 80, :rows => 15, :class => 'wiki-edit' %>
66
</div>

0 commit comments

Comments
 (0)