| |   |
| 1 | class Article < ActiveRecord::Base |
| 2 | |
| 3 | belongs_to :user |
| 4 | belongs_to :category |
| 5 | belongs_to :project |
| 6 | |
| 7 | has_many :comments |
| 8 | |
| 9 | # |
| 10 | validates_columns :article_type, :status, :comment_status, :audience |
| 11 | validates_presence_of :title, :permalink, :category_id, :user_id |
| 12 | validates_length_of :title, :within => 2..128 |
| 13 | validates_length_of :permalink, :within => 2..128 |
| 14 | validates_uniqueness_of :title, :permalink, :case_sensitive => false |
| 15 | |
| 16 | # must pass the enum as string |
| 17 | def self.format_article_type (at) |
| 18 | case at |
| 19 | when "release" |
| 20 | return "Releases" |
| 21 | when "issue" |
| 22 | return "Issues" |
| 23 | when "howto" |
| 24 | return "How To" #TODO this is spare now |
| 25 | when "other" |
| 26 | return "Others" |
| 27 | else |
| 28 | return at.to_s.capitalize |
| 29 | end |
| 30 | end |
| 31 | |
| 32 | protected |
| 33 | |
| 34 | |
| 35 | #FIXME it's possible this could be returning too many results |
| 36 | def self.time_delta(year, month = nil, day = nil) |
| 37 | from = Time.mktime(year, month || 1, day || 1) |
| 38 | to = from + 1.year |
| 39 | to = from + 1.month unless month.blank? |
| 40 | to = from + 1.day unless day.blank? |
| 41 | to = to.tomorrow unless month.blank? |
| 42 | return [from, to] |
| 43 | end |
| 44 | |
| 45 | #FIXME Audience Issue |
| 46 | #there is some conflict between the security system using project audience and articles |
| 47 | #poss solution is either to do enforce that articles can't be public if they belong to a private project |
| 48 | #or to do the security tests below by joining project and testing project.audience as well as testing |
| 49 | #article.audience |
| 50 | |
| 51 | |
| 52 | #public version of .count to filter articles you are not authorised to see |
| 53 | def self.public_count (curruser) |
| 54 | @projects = Project.list_for_user(curruser) |
| 55 | #FIXME is there a faster way of doing this? |
| 56 | @articles = find(:all, :conditions => ["status = '#{:published}' AND ( audience = '#{:public}' OR project_id IN (" << @projects << ") )"], :order => "created_at DESC") |
| 57 | return @articles.size |
| 58 | end |
| 59 | |
| 60 | # |
| 61 | # finds articles for \2008 or \2008\11 or \2008\11\1 |
| 62 | # |
| 63 | def self.find_by_date (curruser, year, month = nil, day = nil) |
| 64 | from, to = self.time_delta(year, month, day) |
| 65 | @projects = Project.list_for_user(curruser) |
| 66 | find(:all, :conditions => [ "status = '#{:published}' AND (created_at BETWEEN ? AND ?) AND ( audience = '#{:public}' OR project_id IN (" << @projects << ") )", from, to ]) |
| 67 | end |
| 68 | |
| 69 | # |
| 70 | def self.find_all_articles (curruser) |
| 71 | @projects = Project.list_for_user(curruser) |
| 72 | find(:all, :conditions => ["status = '#{:published}' AND ( audience = '#{:public}' OR project_id IN (" << @projects << ") )"], :order => "created_at DESC") |
| 73 | end |
| 74 | |
| 75 | # |
| 76 | def self.find_home (curruser) |
| 77 | @projects = Project.list_for_user(curruser) |
| 78 | find(:all, :conditions => ["status = '#{:published}' AND ( audience = '#{:public}' OR project_id IN (" << @projects << ") )"], :order => "created_at DESC", :limit => 3) |
| 79 | end |
| 80 | |
| 81 | # finds articles for a user based on the user slug e.g. /profiles/test-user/articles |
| 82 | def self.find_by_user (user_id, curruser) |
| 83 | @projects = Project.list_for_user(curruser) |
| 84 | find(:all, :conditions => ["status = '#{:published}' AND user_id = '#{user_id}' AND ( audience = '#{:public}' OR project_id IN (" << @projects << ") )"], :order => "created_at DESC") |
| 85 | end |
| 86 | |
| 87 | # finds articles for a user/project based on the user slug e.g. /profiles/test-user/project/project_id/articles |
| 88 | def self.find_by_user_project (user_id, project_id, curruser) |
| 89 | @projects = Project.list_for_user(curruser) |
| 90 | find(:all, :conditions => ["status = '#{:published}' AND user_id = '#{user_id}' AND project_id = '#{project_id}' AND ( audience = '#{:public}' OR project_id IN (" << @projects << ") )"], :order => "created_at DESC") |
| 91 | end |
| 92 | |
| 93 | # |
| 94 | def self.find_by_permalink (permalink, curruser) |
| 95 | @projects = Project.list_for_user(curruser) |
| 96 | find(:first, :conditions => ["status = '#{:published}' AND permalink = '#{permalink}' AND ( audience = '#{:public}' OR project_id IN (" << @projects << ") )"]) |
| 97 | end |
| 98 | |
| 99 | # |
| 100 | def self.find_by_type (article_type, curruser) |
| 101 | @projects = Project.list_for_user(curruser) |
| 102 | find(:all, :conditions => ["status = '#{:published}' AND article_type = '#{article_type}' AND ( audience = '#{:public}' OR project_id IN (" << @projects << ") )"], :order => "created_at DESC") |
| 103 | end |
| 104 | |
| 105 | # |
| 106 | def self.find_by_category (category_id, curruser) |
| 107 | @projects = Project.list_for_user(curruser) |
| 108 | find(:all, :conditions => ["status = '#{:published}' AND category_id = '#{category_id}' AND ( audience = '#{:public}' OR project_id IN (" << @projects << ") )"], :order => "created_at DESC") |
| 109 | end |
| 110 | |
| 111 | # |
| 112 | def self.find_by_sitename |
| 113 | find(:all, :conditions => ["status = '#{:published}' AND project_id = '1'"], :order => "created_at DESC") |
| 114 | end |
| 115 | |
| 116 | # |
| 117 | def self.find_by_project (project_id, curruser) |
| 118 | projects_allowed = Project.list_for_user(curruser) |
| 119 | find(:all, :conditions => ["status = '#{:published}' AND project_id = '#{project_id}' AND ( audience = '#{:public}' OR project_id IN (" << projects_allowed << ") )"], :order => "created_at DESC") |
| 120 | end |
| 121 | |
| 122 | # |
| 123 | def self.find_by_sitename_and_type (article_type) |
| 124 | find(:all, :conditions => ["status = '#{:published}' AND project_id = '1' AND article_type = '#{article_type}'"], :order => "created_at DESC") |
| 125 | end |
| 126 | |
| 127 | # |
| 128 | def self.find_by_project_and_type (article_type, project_id, curruser) |
| 129 | projects_allowed = Project.list_for_user(curruser) |
| 130 | find(:all, :conditions => ["status = '#{:published}' AND project_id = '#{project_id}' AND article_type = '#{article_type}' AND ( audience = '#{:public}' OR project_id IN (" << projects_allowed << ") )"], :order => "created_at DESC") |
| 131 | end |
| 132 | |
| 133 | # |
| 134 | def self.find_sitename_type_counts |
| 135 | find_by_sql ["SELECT article_types.article_type, IFNULL(COUNT(articles.id),0) AS type_count |
| 136 | FROM article_types |
| 137 | LEFT JOIN articles ON article_types.article_type = articles.article_type AND (articles.project_id = 1 AND articles.status = '#{:published}') |
| 138 | GROUP BY article_types.article_type |
| 139 | ORDER BY article_types.id"] |
| 140 | end |
| 141 | |
| 142 | # |
| 143 | def self.find_project_type_counts (project_id) |
| 144 | find_by_sql ["SELECT article_types.article_type, IFNULL(COUNT(articles.id),0) AS type_count |
| 145 | FROM article_types |
| 146 | LEFT JOIN articles ON article_types.article_type = articles.article_type AND (articles.project_id = '#{project_id}' AND articles.status = '#{:published}') |
| 147 | GROUP BY article_types.article_type |
| 148 | ORDER BY article_types.id"] |
| 149 | end |
| 150 | |
| 151 | # only show public articles on the main feed |
| 152 | # FIXME do a seperate feed on the private user page with project member articles included |
| 153 | def self.find_rss |
| 154 | find(:all, :conditions => ["status = '#{:published}' AND audience = '#{:public}'"], :order => "created_at DESC", :limit => 10) |
| 155 | end |
| 156 | |
| 157 | # |
| 158 | def self.admin_find_all_articles |
| 159 | find(:all, :order => "created_at DESC") |
| 160 | end |
| 161 | |
| 162 | # finds articles for a user based on the user slug e.g. /profiles/test-user/articles |
| 163 | def self.admin_find_by_user (user_id) |
| 164 | find(:all, :conditions => ["user_id = '#{user_id}'"], :order => "created_at DESC") |
| 165 | end |
| 166 | |
| 167 | # |
| 168 | def self.admin_find_by_category(category_id) |
| 169 | find(:all, :conditions => ["category_id = '#{category_id}'"], :order => "created_at DESC") |
| 170 | end |
| 171 | |
| 172 | # |
| 173 | def self.admin_find_by_search(phrase) |
| 174 | find(:all, :conditions => ["title like ?", "%" << phrase << "%"], :order => "created_at DESC") |
| 175 | end |
| 176 | |
| 177 | end |
| 178 | |
| 179 | |
| 180 | |
| 181 | |
| 182 | |
| 183 | |
| 184 | |
| 185 | |
| 186 | |
| 187 | |
| toggle raw diff |
--- /dev/null
+++ b/app/models/article.rb
@@ -0,0 +1,187 @@
+class Article < ActiveRecord::Base
+
+ belongs_to :user
+ belongs_to :category
+ belongs_to :project
+
+ has_many :comments
+
+ #
+ validates_columns :article_type, :status, :comment_status, :audience
+ validates_presence_of :title, :permalink, :category_id, :user_id
+ validates_length_of :title, :within => 2..128
+ validates_length_of :permalink, :within => 2..128
+ validates_uniqueness_of :title, :permalink, :case_sensitive => false
+
+ # must pass the enum as string
+ def self.format_article_type (at)
+ case at
+ when "release"
+ return "Releases"
+ when "issue"
+ return "Issues"
+ when "howto"
+ return "How To" #TODO this is spare now
+ when "other"
+ return "Others"
+ else
+ return at.to_s.capitalize
+ end
+ end
+
+protected
+
+
+ #FIXME it's possible this could be returning too many results
+ def self.time_delta(year, month = nil, day = nil)
+ from = Time.mktime(year, month || 1, day || 1)
+ to = from + 1.year
+ to = from + 1.month unless month.blank?
+ to = from + 1.day unless day.blank?
+ to = to.tomorrow unless month.blank?
+ return [from, to]
+ end
+
+ #FIXME Audience Issue
+ #there is some conflict between the security system using project audience and articles
+ #poss solution is either to do enforce that articles can't be public if they belong to a private project
+ #or to do the security tests below by joining project and testing project.audience as well as testing
+ #article.audience
+
+
+ #public version of .count to filter articles you are not authorised to see
+ def self.public_count (curruser)
+ @projects = Project.list_for_user(curruser)
+ #FIXME is there a faster way of doing this?
+ @articles = find(:all, :conditions => ["status = '#{:published}' AND ( audience = '#{:public}' OR project_id IN (" << @projects << ") )"], :order => "created_at DESC")
+ return @articles.size
+ end
+
+ #
+ # finds articles for \2008 or \2008\11 or \2008\11\1
+ #
+ def self.find_by_date (curruser, year, month = nil, day = nil)
+ from, to = self.time_delta(year, month, day)
+ @projects = Project.list_for_user(curruser)
+ find(:all, :conditions => [ "status = '#{:published}' AND (created_at BETWEEN ? AND ?) AND ( audience = '#{:public}' OR project_id IN (" << @projects << ") )", from, to ])
+ end
+
+ #
+ def self.find_all_articles (curruser)
+ @projects = Project.list_for_user(curruser)
+ find(:all, :conditions => ["status = '#{:published}' AND ( audience = '#{:public}' OR project_id IN (" << @projects << ") )"], :order => "created_at DESC")
+ end
+
+ #
+ def self.find_home (curruser)
+ @projects = Project.list_for_user(curruser)
+ find(:all, :conditions => ["status = '#{:published}' AND ( audience = '#{:public}' OR project_id IN (" << @projects << ") )"], :order => "created_at DESC", :limit => 3)
+ end
+
+ # finds articles for a user based on the user slug e.g. /profiles/test-user/articles
+ def self.find_by_user (user_id, curruser)
+ @projects = Project.list_for_user(curruser)
+ find(:all, :conditions => ["status = '#{:published}' AND user_id = '#{user_id}' AND ( audience = '#{:public}' OR project_id IN (" << @projects << ") )"], :order => "created_at DESC")
+ end
+
+ # finds articles for a user/project based on the user slug e.g. /profiles/test-user/project/project_id/articles
+ def self.find_by_user_project (user_id, project_id, curruser)
+ @projects = Project.list_for_user(curruser)
+ find(:all, :conditions => ["status = '#{:published}' AND user_id = '#{user_id}' AND project_id = '#{project_id}' AND ( audience = '#{:public}' OR project_id IN (" << @projects << ") )"], :order => "created_at DESC")
+ end
+
+ #
+ def self.find_by_permalink (permalink, curruser)
+ @projects = Project.list_for_user(curruser)
+ find(:first, :conditions => ["status = '#{:published}' AND permalink = '#{permalink}' AND ( audience = '#{:public}' OR project_id IN (" << @projects << ") )"])
+ end
+
+ #
+ def self.find_by_type (article_type, curruser)
+ @projects = Project.list_for_user(curruser)
+ find(:all, :conditions => ["status = '#{:published}' AND article_type = '#{article_type}' AND ( audience = '#{:public}' OR project_id IN (" << @projects << ") )"], :order => "created_at DESC")
+ end
+
+ #
+ def self.find_by_category (category_id, curruser)
+ @projects = Project.list_for_user(curruser)
+ find(:all, :conditions => ["status = '#{:published}' AND category_id = '#{category_id}' AND ( audience = '#{:public}' OR project_id IN (" << @projects << ") )"], :order => "created_at DESC")
+ end
+
+ #
+ def self.find_by_sitename
+ find(:all, :conditions => ["status = '#{:published}' AND project_id = '1'"], :order => "created_at DESC")
+ end
+
+ #
+ def self.find_by_project (project_id, curruser)
+ projects_allowed = Project.list_for_user(curruser)
+ find(:all, :conditions => ["status = '#{:published}' AND project_id = '#{project_id}' AND ( audience = '#{:public}' OR project_id IN (" << projects_allowed << ") )"], :order => "created_at DESC")
+ end
+
+ #
+ def self.find_by_sitename_and_type (article_type)
+ find(:all, :conditions => ["status = '#{:published}' AND project_id = '1' AND article_type = '#{article_type}'"], :order => "created_at DESC")
+ end
+
+ #
+ def self.find_by_project_and_type (article_type, project_id, curruser)
+ projects_allowed = Project.list_for_user(curruser)
+ find(:all, :conditions => ["status = '#{:published}' AND project_id = '#{project_id}' AND article_type = '#{article_type}' AND ( audience = '#{:public}' OR project_id IN (" << projects_allowed << ") )"], :order => "created_at DESC")
+ end
+
+ #
+ def self.find_sitename_type_counts
+ find_by_sql ["SELECT article_types.article_type, IFNULL(COUNT(articles.id),0) AS type_count
+ FROM article_types
+ LEFT JOIN articles ON article_types.article_type = articles.article_type AND (articles.project_id = 1 AND articles.status = '#{:published}')
+ GROUP BY article_types.article_type
+ ORDER BY article_types.id"]
+ end
+
+ #
+ def self.find_project_type_counts (project_id)
+ find_by_sql ["SELECT article_types.article_type, IFNULL(COUNT(articles.id),0) AS type_count
+ FROM article_types
+ LEFT JOIN articles ON article_types.article_type = articles.article_type AND (articles.project_id = '#{project_id}' AND articles.status = '#{:published}')
+ GROUP BY article_types.article_type
+ ORDER BY article_types.id"]
+ end
+
+ # only show public articles on the main feed
+ # FIXME do a seperate feed on the private user page with project member articles included
+ def self.find_rss
+ find(:all, :conditions => ["status = '#{:published}' AND audience = '#{:public}'"], :order => "created_at DESC", :limit => 10)
+ end
+
+ #
+ def self.admin_find_all_articles
+ find(:all, :order => "created_at DESC")
+ end
+
+ # finds articles for a user based on the user slug e.g. /profiles/test-user/articles
+ def self.admin_find_by_user (user_id)
+ find(:all, :conditions => ["user_id = '#{user_id}'"], :order => "created_at DESC")
+ end
+
+ #
+ def self.admin_find_by_category(category_id)
+ find(:all, :conditions => ["category_id = '#{category_id}'"], :order => "created_at DESC")
+ end
+
+ #
+ def self.admin_find_by_search(phrase)
+ find(:all, :conditions => ["title like ?", "%" << phrase << "%"], :order => "created_at DESC")
+ end
+
+end
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file |
| |   |
| 1 | class Repository < ActiveRecord::Base |
| 2 | belongs_to :user |
| 3 | belongs_to :project |
| 4 | belongs_to :parent, :class_name => "Repository" |
| 5 | has_many :committerships, :dependent => :destroy |
| 6 | has_many :committers, :through => :committerships, :source => :user |
| 7 | has_many :comments, :dependent => :destroy |
| 8 | has_many :merge_requests, :foreign_key => 'target_repository_id', |
| 9 | :order => "status, id desc", :dependent => :destroy |
| 10 | has_many :proposed_merge_requests, :foreign_key => 'source_repository_id', |
| 11 | :class_name => 'MergeRequest', :order => "id desc", :dependent => :destroy |
| 12 | |
| 13 | validates_presence_of :user_id, :project_id, :name |
| 14 | validates_format_of :name, :with => /^[a-z0-9_\-]+$/i, |
| 15 | :message => "is invalid, must match something like /[a-z0-9_\\-]+/" |
| 16 | validates_uniqueness_of :name, :scope => :project_id, :case_sensitive => false |
| 17 | |
| 18 | before_save :set_as_mainline_if_first |
| 19 | after_create :add_user_as_committer, :create_new_repos_task |
| 20 | after_destroy :create_delete_repos_task |
| 21 | |
| 22 | def self.new_by_cloning(other, username=nil) |
| 23 | suggested_name = username ? "#{username}s-clone" : nil |
| 24 | new(:parent => other, :project => other.project, :name => suggested_name) |
| 25 | end |
| 26 | |
| 27 | def self.find_by_name!(name) |
| 28 | find_by_name(name) || raise(ActiveRecord::RecordNotFound) |
| 29 | end |
| 30 | |
| 31 | def self.create_git_repository(path) |
| 32 | git_backend.create(full_path_from_partial_path(path)) |
| 33 | end |
| 34 | |
| 35 | def self.clone_git_repository(target_path, source_path) |
| 36 | git_backend.clone(full_path_from_partial_path(target_path), |
| 37 | full_path_from_partial_path(source_path)) |
| 38 | end |
| 39 | |
| 40 | def self.delete_git_repository(path) |
| 41 | git_backend.delete!(full_path_from_partial_path(path)) |
| 42 | end |
| 43 | |
| 44 | def gitdir |
| 45 | File.join(project.slug, "#{name}.git") |
| 46 | end |
| 47 | |
| 48 | def clone_url |
| 49 | "git://#{GitoriousConfig['gitorious_host']}/#{gitdir}" |
| 50 | end |
| 51 | |
| 52 | def http_clone_url |
| 53 | "http://git.#{GitoriousConfig['gitorious_host']}/#{gitdir}" |
| 54 | end |
| 55 | |
| 56 | def push_url |
| 57 | "git@#{GitoriousConfig['gitorious_host']}:#{gitdir}" |
| 58 | end |
| 59 | |
| 60 | def full_repository_path |
| 61 | self.class.full_path_from_partial_path(gitdir) |
| 62 | end |
| 63 | |
| 64 | def git |
| 65 | Grit::Repo.new(full_repository_path) |
| 66 | end |
| 67 | |
| 68 | def has_commits? |
| 69 | return false if new_record? || !ready? |
| 70 | !git.heads.empty? |
| 71 | end |
| 72 | |
| 73 | def self.git_backend |
| 74 | RAILS_ENV == "test" ? MockGitBackend : GitBackend |
| 75 | end |
| 76 | |
| 77 | def git_backend |
| 78 | RAILS_ENV == "test" ? MockGitBackend : GitBackend |
| 79 | end |
| 80 | |
| 81 | def to_param |
| 82 | name |
| 83 | end |
| 84 | |
| 85 | def to_xml(opts = {}) |
| 86 | super({:methods => [:gitdir, :clone_url, :push_url]}.merge(opts)) |
| 87 | end |
| 88 | |
| 89 | def add_committer(user) |
| 90 | unless user.can_write_to?(self) |
| 91 | committers << user |
| 92 | end |
| 93 | end |
| 94 | |
| 95 | def head_candidate |
| 96 | return nil unless has_commits? |
| 97 | @head_candidate ||= git.heads.find{|h| h.name == "master"} || git.heads.first |
| 98 | end |
| 99 | |
| 100 | def last_commit |
| 101 | if has_commits? |
| 102 | @last_commit ||= git.commits(head_candidate.name, 1).first |
| 103 | end |
| 104 | @last_commit |
| 105 | end |
| 106 | |
| 107 | def can_be_deleted_by?(candidate) |
| 108 | !mainline? && (candidate == user) |
| 109 | end |
| 110 | |
| 111 | def create_new_repos_task |
| 112 | Task.create!(:target_class => self.class.name, |
| 113 | :command => parent ? "clone_git_repository" : "create_git_repository", |
| 114 | :arguments => parent ? [gitdir, parent.gitdir] : [gitdir], |
| 115 | :target_id => self.id) |
| 116 | end |
| 117 | |
| 118 | def create_delete_repos_task |
| 119 | Task.create!(:target_class => self.class.name, |
| 120 | :command => "delete_git_repository", :arguments => [gitdir]) |
| 121 | end |
| 122 | |
| 123 | def paginated_commits(tree_name, page, per_page = 30) |
| 124 | per_page = GitoriousConfig["number_of_displayed_commits"] |
| 125 | page = (page || 1).to_i |
| 126 | total = git.commit_count(tree_name) |
| 127 | offset = (page - 1) * per_page |
| 128 | commits = WillPaginate::Collection.new(page, per_page, total) |
| 129 | commits.replace git.commits(tree_name, per_page, offset) |
| 130 | end |
| 131 | |
| 132 | |
| 133 | def count_commits_from_last_week_by_user(user) |
| 134 | return 0 unless has_commits? |
| 135 | |
| 136 | commits_by_email = git.commits_since("master", "last week").collect do |commit| |
| 137 | commit.committer.email == user.email |
| 138 | end |
| 139 | commits_by_email.size |
| 140 | end |
| 141 | |
| 142 | # TODO: cache |
| 143 | def commit_graph_data(head = "master") |
| 144 | commits = git.commits_since(head, "24 weeks ago") |
| 145 | commits_by_week = commits.group_by{|c| c.committed_date.strftime("%W") } |
| 146 | |
| 147 | # build an initial empty set of 24 week commit data |
| 148 | weeks = [1.day.from_now-1.week] |
| 149 | 23.times{|w| weeks << weeks.last-1.week } |
| 150 | week_numbers = weeks.map{|d| d.strftime("%W") } |
| 151 | commits = (0...24).to_a.map{|i| 0 } |
| 152 | |
| 153 | commits_by_week.each do |week, commits_in_week| |
| 154 | if week_pos = week_numbers.index(week) |
| 155 | commits[week_pos+1] = commits_in_week.size |
| 156 | end |
| 157 | end |
| 158 | commits = [] if commits.max == 0 |
| 159 | [week_numbers.reverse, commits.reverse] |
| 160 | end |
| 161 | |
| 162 | # TODO: refactor into simpler approach |
| 163 | # TODO: caching |
| 164 | def commit_graph_data_by_author(head = "master") |
| 165 | h = Hash.new |
| 166 | |
| 167 | data = self.git.git.rev_list({:pretty => "format:name:%cn", :since => "1 years ago" }, head) |
| 168 | data.each_line do |line| |
| 169 | if line =~ /^name:(.*)$/ then |
| 170 | author = $1 |
| 171 | |
| 172 | if h[author] |
|