insight_rails 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/README.md +43 -0
  2. data/Rakefile +15 -0
  3. data/VERSION +1 -0
  4. data/app/controllers/help/categories_controller.rb +11 -0
  5. data/app/controllers/help/comments_controller.rb +22 -0
  6. data/app/controllers/help/issues_controller.rb +53 -0
  7. data/app/controllers/help/sessions_controller.rb +40 -0
  8. data/app/controllers/help_controller.rb +10 -0
  9. data/app/controllers/insight_controller.rb +32 -0
  10. data/app/controllers/knowledge/article_categories_controller.rb +13 -0
  11. data/app/controllers/knowledge/articles_controller.rb +11 -0
  12. data/app/helpers/help/issues_helper.rb +15 -0
  13. data/app/models/article.rb +11 -0
  14. data/app/models/article_category.rb +11 -0
  15. data/app/models/category.rb +11 -0
  16. data/app/models/category_issue.rb +10 -0
  17. data/app/models/comment.rb +26 -0
  18. data/app/models/insight_user.rb +19 -0
  19. data/app/models/issue.rb +30 -0
  20. data/app/models/lead.rb +15 -0
  21. data/app/views/help/categories/index.html.erb +29 -0
  22. data/app/views/help/comments/_form.html.erb +25 -0
  23. data/app/views/help/index.html.erb +0 -0
  24. data/app/views/help/issues/_form.html.erb +43 -0
  25. data/app/views/help/issues/index.html.erb +42 -0
  26. data/app/views/help/issues/new.html.erb +3 -0
  27. data/app/views/help/issues/show.html.erb +39 -0
  28. data/app/views/help/sessions/new.html.erb +20 -0
  29. data/app/views/knowledge/article_categories/index.html.erb +16 -0
  30. data/app/views/knowledge/articles/show.html.erb +3 -0
  31. data/app/views/layouts/_insight_footer.html.erb +25 -0
  32. data/app/views/layouts/insight_layout.html.erb +56 -0
  33. data/config/routes.rb +42 -0
  34. data/generators/insight/insight_generator.rb +26 -0
  35. data/generators/insight/lib/insert_commands.rb +33 -0
  36. data/generators/insight/templates/README +0 -0
  37. data/generators/insight/templates/insight.rake +26 -0
  38. data/generators/insight/templates/insight.rb +7 -0
  39. data/generators/insight/templates/insight.sass +284 -0
  40. data/generators/insight/templates/migrations/link_users_to_crm_contacts.rb +13 -0
  41. data/lib/insight/configuration.rb +28 -0
  42. data/lib/insight/crm/callbacks/account.rb +39 -0
  43. data/lib/insight/crm/callbacks/user.rb +53 -0
  44. data/lib/insight/crm/models/account.rb +17 -0
  45. data/lib/insight/crm/models/contact.rb +17 -0
  46. data/lib/insight/multi_pass_attributes.rb +32 -0
  47. data/lib/insight.rb +3 -0
  48. metadata +140 -0
data/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # Insight
2
+
3
+ Rails engine to access support requests and knowledge base articles stored in FatFree CRM.
4
+
5
+ ## Installation
6
+
7
+ Add insight to your applications dependencies.
8
+
9
+ For Rails 2
10
+
11
+ gem.config "insight_rails", :lib => "insight"
12
+
13
+ For those using bundler
14
+
15
+ gem "insight_rails", :require => "insight"
16
+
17
+ Make sure your development database exists and run the generator:
18
+
19
+ script/generate insight
20
+
21
+ Firstly, run the migration to add the crm_id field to your users table.
22
+
23
+ Then update the insight initializer with details of your api_key, fatfree's url and your recaptcha keys.
24
+
25
+ ## Usage
26
+
27
+ You will now have a knowledge base of articles from your fat free installation available at:
28
+
29
+ /knowledge
30
+
31
+ You will also have a support page available at:
32
+
33
+ /help
34
+
35
+ Discussions can be viewed at:
36
+
37
+ /help/browse
38
+
39
+ This will pull through the issue_categories you setup in Fat Free.
40
+
41
+ ## Copyright
42
+
43
+ Copyright (c) 2010 Kieran Johnson. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require 'jeweler'
2
+
3
+ Jeweler::Tasks.new do |gem|
4
+ gem.name = "insight_rails"
5
+ gem.summary = "Customer Support and Knowledge Base"
6
+ gem.description = "Customer Support and Knowledge Base"
7
+ gem.email = "support@invisiblelines.com"
8
+ gem.homepage = "http://github.com/kieranj/insight"
9
+ gem.authors = ["Kieran Johnson"]
10
+ gem.files = FileList["[A-Z]*", "{app,config,generators,lib,rails}/**/*"]
11
+ gem.add_dependency "recaptcha"
12
+ gem.add_dependency "gravtastic"
13
+ end
14
+
15
+ Jeweler::GemcutterTasks.new
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.1
@@ -0,0 +1,11 @@
1
+ class Help::CategoriesController < InsightController
2
+
3
+ skip_before_filter :login_required
4
+
5
+ layout Insight.configuration.layout
6
+
7
+ def index
8
+ @categories = Category.find(:all)
9
+ end
10
+
11
+ end
@@ -0,0 +1,22 @@
1
+ class Help::CommentsController < InsightController
2
+
3
+ skip_before_filter :login_required
4
+
5
+ def create
6
+ @comment = Comment.new(params[:comment].merge(defaults))
7
+ if verify_recaptcha(:model => @comment, :private_key => Insight.configuration.recaptcha_private_key) && @comment.save
8
+ redirect_to(help_issue_path(@comment.issue.to_param))
9
+ else
10
+ redirect_to(help_issue_path(@comment.issue.to_param))
11
+ end
12
+ end
13
+
14
+ protected
15
+
16
+ def defaults
17
+ {
18
+ :issue_id => params[:issue_id]
19
+ }
20
+ end
21
+
22
+ end
@@ -0,0 +1,53 @@
1
+ class Help::IssuesController < InsightController
2
+
3
+ layout Insight.configuration.layout
4
+
5
+ skip_before_filter :login_required
6
+
7
+ def my
8
+ @issues = Issue.get(:my, :crm_id => current_user.crm_id)
9
+ render :action => "index"
10
+ end
11
+
12
+ def index
13
+ if params[:category_id]
14
+ @category = Category.find(params[:category_id])
15
+ @issues = @category.issues
16
+ else
17
+ @issues = Issue.find(:all)
18
+ end
19
+ end
20
+
21
+ def show
22
+ @issue = Issue.find(params[:id])
23
+ end
24
+
25
+ def new
26
+ @issue = Issue.new
27
+ end
28
+
29
+ def create
30
+ @issue = Issue.new(params[:issue])
31
+ @issue.contact_id = current_user.crm_id if logged_in? && !current_user.crm_id.nil?
32
+
33
+ if verify_recaptcha(:model => @issue, :private_key => Insight.configuration.recaptcha_private_key) && @issue.save
34
+ redirect_to(help_issue_path(@issue))
35
+ else
36
+ render :action => "new"
37
+ end
38
+ end
39
+
40
+ def edit
41
+ @issue = Issue.find(params[:id])
42
+ end
43
+
44
+ def update
45
+ @issue = Issue.find(params[:id])
46
+ if verify_recaptcha(:model => @issue, :private_key => Insight.configuration.recaptcha_private_key) && @issue.update_attributes(params[:issue].merge(:contact_id => current_user.crm_id))
47
+ redirect_to(help_issues_path)
48
+ else
49
+ render :action => "edit"
50
+ end
51
+ end
52
+
53
+ end
@@ -0,0 +1,40 @@
1
+ class Help::SessionsController < ApplicationController
2
+
3
+ layout Insight.configuration.layout
4
+
5
+ skip_before_filter :login_required
6
+
7
+ before_filter :check_existing_login, :only => [ :new ]
8
+
9
+ # def new
10
+ # end
11
+ #
12
+ # def create
13
+ # json = { :username => params[:username], :password => params[:password] }.to_json
14
+ # auth_details = MultiPass.encode64(json)
15
+ #
16
+ # session = InsightSession.new(:auth => auth_details)
17
+ # if session.save
18
+ # self.current_user = session.multipass
19
+ # else
20
+ # render :action => "new"
21
+ # end
22
+ # end
23
+
24
+ def destroy
25
+ session[:multipass] = nil
26
+ redirect_to("/help")
27
+ end
28
+
29
+ protected
30
+
31
+ def check_existing_login
32
+ if logged_in?
33
+ session[:multipass] = current_user.multipass
34
+ redirect_to("/help")
35
+ else
36
+ redirect_to(login_path(:to => "/help"))
37
+ end
38
+ end
39
+
40
+ end
@@ -0,0 +1,10 @@
1
+ class HelpController < InsightController
2
+
3
+ skip_before_filter :login_required
4
+
5
+ layout Insight.configuration.layout
6
+
7
+ def index
8
+ end
9
+
10
+ end
@@ -0,0 +1,32 @@
1
+ class InsightController < ApplicationController
2
+
3
+ before_filter :login_required
4
+
5
+ def current_user
6
+ @insight_user ||= login_from_multipass
7
+ end
8
+
9
+ def current_user=(multipass)
10
+ session[:multipass] = multipass
11
+ @insight_user = login_from_multipass
12
+ end
13
+
14
+ def authorized?
15
+ logged_in?
16
+ end
17
+
18
+ def logged_in?
19
+ !!current_user
20
+ end
21
+
22
+ protected
23
+
24
+ def login_required
25
+ authorized?
26
+ end
27
+
28
+ def login_from_multipass
29
+ session[:multipass].nil? ? nil : InsightUser.new(session[:multipass])
30
+ end
31
+
32
+ end
@@ -0,0 +1,13 @@
1
+ class Knowledge::ArticleCategoriesController < ApplicationController
2
+
3
+ skip_before_filter :login_required
4
+
5
+ layout Insight.configuration.layout
6
+
7
+ def index
8
+ @categories = ArticleCategory.find(:all)
9
+ @latest_articles = Article.get(:latest)
10
+ @latest_issues = Issue.get(:latest)
11
+ end
12
+
13
+ end
@@ -0,0 +1,11 @@
1
+ class Knowledge::ArticlesController < ApplicationController
2
+
3
+ skip_before_filter :login_required
4
+
5
+ layout Insight.configuration.layout
6
+
7
+ def show
8
+ @article = Article.find(params[:id])
9
+ end
10
+
11
+ end
@@ -0,0 +1,15 @@
1
+ module Help::IssuesHelper
2
+
3
+ def posted_on_long(date)
4
+ date.strftime("%B #{date.day.ordinalize} %Y") + " at " + date.strftime("%H:%M%p").downcase
5
+ end
6
+
7
+ def posted_on_short(date)
8
+ date.strftime("%B #{date.day.ordinalize}, %Y %H:%M") + date.strftime("%p").downcase
9
+ end
10
+
11
+ def comment_by(comment)
12
+ "staff" if comment.commenter_type == "User"
13
+ end
14
+
15
+ end
@@ -0,0 +1,11 @@
1
+ class Article < ActiveResource::Base
2
+
3
+ headers["X-ApiKey"] = Insight.configuration.api_key
4
+
5
+ self.site = Insight.configuration.fat_free_url
6
+
7
+ def to_param
8
+ slug
9
+ end
10
+
11
+ end
@@ -0,0 +1,11 @@
1
+ class ArticleCategory < ActiveResource::Base
2
+
3
+ headers["X-ApiKey"] = Insight.configuration.api_key
4
+
5
+ self.site = Insight.configuration.fat_free_url
6
+
7
+ def to_param
8
+ slug
9
+ end
10
+
11
+ end
@@ -0,0 +1,11 @@
1
+ class Category < ActiveResource::Base
2
+
3
+ headers["X-ApiKey"] = Insight.configuration.api_key
4
+
5
+ self.site = Insight.configuration.fat_free_url
6
+
7
+ def to_param
8
+ slug
9
+ end
10
+
11
+ end
@@ -0,0 +1,10 @@
1
+ class CategoryIssue < ActiveResource::Base
2
+
3
+ self.site = Insight.configuration.fat_free_url + "/categories/:category_id"
4
+ self.element_name = "issue"
5
+
6
+ def to_param
7
+ slug
8
+ end
9
+
10
+ end
@@ -0,0 +1,26 @@
1
+ class Comment < ActiveResource::Base
2
+
3
+ headers["X-ApiKey"] = Insight.configuration.api_key
4
+
5
+ self.site = Insight.configuration.fat_free_url + "/issues/:issue_id"
6
+
7
+ include Gravtastic
8
+ gravtastic :user_email
9
+
10
+ def user_name
11
+ if commenter_id
12
+ commenter.username
13
+ else
14
+ super
15
+ end
16
+ end
17
+
18
+ def user_email
19
+ if commenter_id
20
+ commenter.email
21
+ else
22
+ super
23
+ end
24
+ end
25
+
26
+ end
@@ -0,0 +1,19 @@
1
+ class InsightUser
2
+
3
+ def initialize(multipass)
4
+ @multipass_attributes = self.class.multipass.decode(multipass)
5
+ end
6
+
7
+ def method_missing(method, *args, &block)
8
+ if @multipass_attributes.keys.include?(method)
9
+ @multipass_attributes[method]
10
+ else
11
+ super
12
+ end
13
+ end
14
+
15
+ def self.multipass
16
+ @multipass ||= MultiPass.new('fatfree', 'abc')
17
+ end
18
+
19
+ end
@@ -0,0 +1,30 @@
1
+ class Issue < ActiveResource::Base
2
+
3
+ headers["X-ApiKey"] = Insight.configuration.api_key
4
+
5
+ self.site = Insight.configuration.fat_free_url
6
+
7
+ include Gravtastic
8
+ gravtastic :user_email
9
+
10
+ def to_param
11
+ "#{slug}-#{id}"
12
+ end
13
+
14
+ def user_name
15
+ if contact_id
16
+ contact.username
17
+ else
18
+ super
19
+ end
20
+ end
21
+
22
+ def user_email
23
+ if contact_id
24
+ contact.email
25
+ else
26
+ super
27
+ end
28
+ end
29
+
30
+ end
@@ -0,0 +1,15 @@
1
+ class Lead < ActiveResource::Base
2
+
3
+ headers["X-ApiKey"] = Insight.configuration.api_key
4
+
5
+ self.site = Insight.configuration.fat_free_url
6
+
7
+ attr_accessor :password, :password_confirmation
8
+
9
+ def validate
10
+ self.errors.add(:email, "Is required") if email.nil?
11
+ self.errors.add(:password, "Is required") if password.nil?
12
+ self.errors.add(:password_confirmation, "Must match password") if password != password_confirmation
13
+ end
14
+
15
+ end
@@ -0,0 +1,29 @@
1
+ <h1>Public Discussions</h1>
2
+
3
+ <h2>Welcome to the discussion area. You can browse public discussions or start your own discussion in a category of your choosing.</h2>
4
+
5
+ <table>
6
+ <thead>
7
+ <tr>
8
+ <th width="790">Category</th>
9
+ <th width="150">Latest Post</th>
10
+ </tr>
11
+ </thead>
12
+ <tbody>
13
+ <% @categories.each do |category| -%>
14
+ <tr class="<%= cycle(:odd, :even) %>">
15
+ <td>
16
+ <h2><%= link_to(category.name, help_category_issues_path(category)) %></h2>
17
+ <%= simple_format(category.description) %>
18
+ </td>
19
+ <td>
20
+ <% if !category.issues.empty? -%>
21
+ <%= link_to(category.issues.first.updated_at.strftime("%b %d, %Y %H:%M"),
22
+ help_category_issue_path(category, category.issues.first)) %>
23
+ by <%= category.issues.first.user_name %>
24
+ <% end -%>
25
+ </td>
26
+ </tr>
27
+ <% end -%>
28
+ </tbody>
29
+ </table>
@@ -0,0 +1,25 @@
1
+ <% form_tag(help_issue_comments_path(@issue), :class => "formtastic") do %>
2
+ <fieldset class="inputs">
3
+ <ol>
4
+ <% if !logged_in? -%>
5
+ <li class="string">
6
+ <%= label_tag "comment[user_name]", "Name" %>
7
+ <%= text_field_tag "comment[user_name]" %>
8
+ </li>
9
+ <li class="string">
10
+ <%= label_tag "comment[user_email]", "Email" %>
11
+ <%= text_field_tag "comment[user_email]" %>
12
+ </li>
13
+ <% end -%>
14
+ <li class="text">
15
+ <%= text_area_tag "comment[comment]" %>
16
+ </li>
17
+ </ol>
18
+ </fieldset>
19
+
20
+ <%= recaptcha_tags :public_key => Insight.configuration.recaptcha_public_key %>
21
+
22
+ <fieldset class="buttons">
23
+ <%= submit_tag("Post Comment") %>
24
+ </fieldset>
25
+ <% end %>
File without changes
@@ -0,0 +1,43 @@
1
+ <% form_tag(help_issues_path, :class => "formtastic") do %>
2
+ <fieldset class="inputs">
3
+ <ol>
4
+ <% if !logged_in? -%>
5
+ <li class="string">
6
+ <%= label_tag "issue[user_name]", "Name" %>
7
+ <%= text_field_tag "issue[user_name]" %>
8
+ </li>
9
+ <li class="string">
10
+ <%= label_tag "issue[user_email]", "Email" %>
11
+ <%= text_field_tag "issue[user_email]" %>
12
+ </li>
13
+ <% end -%>
14
+ <li class="select">
15
+ <%= label_tag "issue[category_id]", "Category" %>
16
+ <%= select_tag "issue[category_id]",
17
+ options_from_collection_for_select(Category.find(:all), :id, :name),
18
+ :prompt => "Please select" %>
19
+ </li>
20
+ <li class="string">
21
+ <%= label_tag "issue[subject]", "Subject" %>
22
+ <%= text_field_tag "issue[subject]" %>
23
+ </li>
24
+ <li class="text">
25
+ <%= text_area_tag "issue[body]" %>
26
+ </li>
27
+ <% if logged_in? -%>
28
+ <li class="boolean">
29
+ <label>
30
+ <%= check_box_tag "issue[private]" %>
31
+ Mark this issue as private. Only allow support representatives to see it.
32
+ </label>
33
+ </li>
34
+ <% end -%>
35
+ </ol>
36
+ </fieldset>
37
+
38
+ <%= recaptcha_tags :public_key => Insight.configuration.recaptcha_public_key %>
39
+
40
+ <fieldset class="buttons">
41
+ <%= submit_tag("Post Issue") %>
42
+ </fieldset>
43
+ <% end %>
@@ -0,0 +1,42 @@
1
+ <h1><%= @category.name %></h1>
2
+ <h2><%= @category.description %></h2>
3
+
4
+ <%= link_to("Create New Issue", new_help_issue_path, :class => "button") %>
5
+
6
+ <table>
7
+ <thead>
8
+ <tr>
9
+ <th width="590">Topic</th>
10
+ <th width="60">State</th>
11
+ <th width="60">Replies</th>
12
+ <th width="180">Latest Post</th>
13
+ </tr>
14
+ </thead>
15
+ <tbody>
16
+ <% @issues.each do |issue| %>
17
+ <tr class="<%= cycle(:odd, :even) %>">
18
+ <td>
19
+ <h2><%= link_to(issue.subject, help_issue_path(issue)) %></h2>
20
+ <p>Started by <%= issue.user_name %></p>
21
+ </td>
22
+ <td>
23
+ <%= issue.state %>
24
+ </td>
25
+ <td>
26
+ <%= issue.comments.count %>
27
+ </td>
28
+ <td>
29
+ <% if issue.comments.empty? %>
30
+ <%= link_to(posted_on_short(issue.created_at), help_issue_path(issue)) %>
31
+ <br />
32
+ <%= issue.user_name %>
33
+ <% else %>
34
+ <%= link_to(posted_on_short(issue.comments.last.updated_at), help_issue_path(issue)) %>
35
+ <br />
36
+ <%= issue.comments.last.user_name %>
37
+ <% end %>
38
+ </td>
39
+ </tr>
40
+ <% end %>
41
+ </tbody>
42
+ </table>
@@ -0,0 +1,3 @@
1
+ <h1>New Issue</h1>
2
+
3
+ <%= render :partial => "help/issues/form" %>
@@ -0,0 +1,39 @@
1
+ <div class="issue">
2
+ <h2><strong>Subject</strong> : <%= @issue.subject %></h2>
3
+
4
+ <div class="author">
5
+ <%= image_tag(@issue.gravatar_url(:size => 30)) %>
6
+ <p>Posted by <cite><%= @issue.user_name %></cite> on <%= posted_on_long(@issue.updated_at) %></p>
7
+ </div>
8
+
9
+ <div class="body">
10
+ <%= simple_format(@issue.body) %>
11
+ </div>
12
+
13
+ <% if !@issue.comments.empty? %>
14
+ <h3 class="comment_count"><%= @issue.comments.count %> Comments</h3>
15
+
16
+ <ol class="comments">
17
+ <% @issue.comments.each_with_index do |comment, index| %>
18
+ <li class="<%= cycle(:odd, :even) %>">
19
+ <div class="<%= comment_by(comment) %>">
20
+ <p><strong><%= index + 1 %></strong> Posted by <cite><%= comment.user_name %></cite> on <%= posted_on_long(comment.updated_at) %></p>
21
+ </div>
22
+ <div class="gravatar">
23
+ <%= image_tag(comment.gravatar_url(:size => 30)) %>
24
+ </div>
25
+ <div class="said">
26
+ <%= simple_format(comment.comment) %>
27
+ </div>
28
+ </li>
29
+ <% end %>
30
+ </ol>
31
+ <% end %>
32
+
33
+ <h3 class="reply">Reply to this discussion</h3>
34
+ <%= render :partial => "help/comments/form" %>
35
+ </div>
36
+
37
+ <div class="issue-status">
38
+ <p><strong><%= @issue.state %></strong></p>
39
+ </div>
@@ -0,0 +1,20 @@
1
+ <h1>Login</h1>
2
+
3
+ <% form_tag(help_session_path, :class => "formtastic") do %>
4
+ <fieldset class="inputs">
5
+ <ol>
6
+ <li class="string">
7
+ <%= label_tag "user[email]", "Email" %>
8
+ <%= text_field_tag "user[email]" %>
9
+ </li>
10
+ <li class="password">
11
+ <%= label_tag "user[password]", "Password" %>
12
+ <%= password_field_tag "user[password]" %>
13
+ </li>
14
+ </ol>
15
+ </fieldset>
16
+
17
+ <fieldset class="buttons">
18
+ <%= submit_tag("Login") %>
19
+ </fieldset>
20
+ <% end %>
@@ -0,0 +1,16 @@
1
+ <h1>Knowledge Base</h1>
2
+
3
+ <div class="articles">
4
+ <% @categories.each do |category| %>
5
+ <div class="category">
6
+ <h2><%= category.name %></h2>
7
+ <ul>
8
+ <% category.articles[0..5].each do |article| %>
9
+ <li>
10
+ <%= link_to(article.title, knowledge_article_path(article)) %>
11
+ </li>
12
+ <% end %>
13
+ </ul>
14
+ </div>
15
+ <% end %>
16
+ </div>
@@ -0,0 +1,3 @@
1
+ <h1><%= @article.title %></h1>
2
+
3
+ <%= simple_format(@article.body) %>