Merge pull request #381 from warpc/263-refactoring
[Refs #263] Fix tests. Fix comments. Global refactoring. * Improve docs, design source and config examples; * Fix and refactor build lists, comments and other specs; * Refactor comments controller, models and views; * Code cleanup.
|
@ -45,7 +45,7 @@ class BuildListsController < ApplicationController
|
|||
@build_list = @project.build_lists.build(params[:build_list])
|
||||
@build_list.commit_hash = @project.git_repository.commits(@build_list.project_version.match(/^latest_(.+)/).to_a.last || @build_list.project_version).first.id if @build_list.project_version
|
||||
@build_list.bpl = bpl; @build_list.arch = arch; @build_list.user = current_user
|
||||
@build_list.include_repos = @build_list.include_repos.select { |ir| @build_list.bpl.repository_ids.include? ir.to_i }
|
||||
@build_list.include_repos = @build_list.include_repos.select {|ir| @build_list.bpl.repository_ids.include? ir.to_i}
|
||||
@build_list.priority = 100 # User builds more priority than mass rebuild with zero priority
|
||||
flash_options = {:project_version => @build_list.project_version, :arch => arch.name, :bpl => bpl.name, :pl => @build_list.pl}
|
||||
if @build_list.save
|
||||
|
|
|
@ -1,26 +1,17 @@
|
|||
# -*- encoding : utf-8 -*-
|
||||
class CommentsController < ApplicationController
|
||||
before_filter :authenticate_user!
|
||||
load_and_authorize_resource :project
|
||||
before_filter :find_commentable
|
||||
before_filter :find_or_build_comment
|
||||
load_and_authorize_resource
|
||||
|
||||
load_resource :project
|
||||
before_filter :set_commentable
|
||||
before_filter :find_comment, :only => [:edit, :update, :destroy]
|
||||
authorize_resource
|
||||
|
||||
def index
|
||||
@comments = @commentable.comments
|
||||
end
|
||||
include CommentsHelper
|
||||
|
||||
def create
|
||||
@comment = @commentable.comments.build(params[:comment]) if @commentable.class == Issue
|
||||
if @commentable.class == Grit::Commit
|
||||
@comment = Comment.new(params[:comment].merge(:commentable_id => @commentable.id.hex, :commentable_type => @commentable.class.name))
|
||||
end
|
||||
@comment.project = @project
|
||||
@comment.user_id = current_user.id
|
||||
if @comment.save
|
||||
flash[:notice] = I18n.t("flash.comment.saved")
|
||||
redirect_to commentable_path
|
||||
redirect_to project_commentable_path(@project, @commentable)
|
||||
else
|
||||
flash[:error] = I18n.t("flash.comment.save_error")
|
||||
render :action => 'new'
|
||||
|
@ -28,19 +19,12 @@ class CommentsController < ApplicationController
|
|||
end
|
||||
|
||||
def edit
|
||||
@update_url = case @commentable.class.name
|
||||
when "Issue"
|
||||
project_issue_comment_path(@project, @commentable, @comment)
|
||||
when "Grit::Commit"
|
||||
project_commit_comment_path(@project, @commentable, @comment)
|
||||
end
|
||||
@commentable_path = commentable_path
|
||||
end
|
||||
|
||||
def update
|
||||
if @comment.update_attributes(params[:comment])
|
||||
flash[:notice] = I18n.t("flash.comment.saved")
|
||||
redirect_to commentable_path
|
||||
redirect_to project_commentable_path(@project, @commentable)
|
||||
else
|
||||
flash[:error] = I18n.t("flash.comment.save_error")
|
||||
render :action => 'new'
|
||||
|
@ -49,30 +33,19 @@ class CommentsController < ApplicationController
|
|||
|
||||
def destroy
|
||||
@comment.destroy
|
||||
|
||||
flash[:notice] = t("flash.comment.destroyed")
|
||||
redirect_to commentable_path
|
||||
redirect_to project_commentable_path(@project, @commentable)
|
||||
end
|
||||
|
||||
private
|
||||
protected
|
||||
|
||||
def set_commentable
|
||||
@commentable = if params[:issue_id].present?
|
||||
@project.issues.find_by_serial_id params[:issue_id]
|
||||
elsif params[:commit_id].present?
|
||||
@project.git_repository.commit params[:commit_id]
|
||||
end
|
||||
def find_commentable
|
||||
@commentable = params[:issue_id].present? && @project.issues.find_by_serial_id(params[:issue_id]) ||
|
||||
params[:commit_id].present? && @project.git_repository.commit(params[:commit_id])
|
||||
end
|
||||
|
||||
def find_comment
|
||||
@comment = Comment.find(params[:id])
|
||||
if @comment.commit_comment?
|
||||
@comment.project = @project
|
||||
def find_or_build_comment
|
||||
@comment = params[:id].present? && Comment.find(params[:id]) ||
|
||||
current_user.comments.build(params[:comment]) {|c| c.commentable = @commentable; c.project = @project}
|
||||
end
|
||||
end
|
||||
|
||||
def commentable_path
|
||||
@commentable.class == Issue ? [@project, @commentable] : commit_path(@project, @commentable.id)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
# -*- encoding : utf-8 -*-
|
||||
module CommentsHelper
|
||||
def project_commentable_comment_path(project, commentable, comment)
|
||||
case
|
||||
when Comment.issue_comment?(commentable.class)
|
||||
project_issue_comment_path(project, commentable, comment)
|
||||
when Comment.commit_comment?(commentable.class)
|
||||
project_commit_comment_path(project, commentable, comment)
|
||||
end
|
||||
end
|
||||
|
||||
def project_commentable_path(project, commentable)
|
||||
case
|
||||
when Comment.issue_comment?(commentable.class)
|
||||
polymorphic_path [project, commentable]
|
||||
when Comment.commit_comment?(commentable.class)
|
||||
commit_path project, commentable.id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -35,7 +35,7 @@ class ActivityFeedObserver < ActiveRecord::Observer
|
|||
end
|
||||
|
||||
when 'Comment'
|
||||
if record.commentable.class == Issue
|
||||
if record.issue_comment?
|
||||
subscribes = record.commentable.subscribes
|
||||
subscribes.each do |subscribe|
|
||||
if record.user_id != subscribe.user_id
|
||||
|
|
|
@ -6,23 +6,45 @@ class Comment < ActiveRecord::Base
|
|||
|
||||
validates :body, :user_id, :commentable_id, :commentable_type, :project_id, :presence => true
|
||||
|
||||
scope :for_commit, lambda {|c| where(:commentable_id => c.id.hex, :commentable_type => c.class)}
|
||||
default_scope order('created_at')
|
||||
|
||||
after_create :subscribe_on_reply, :unless => lambda {|c| c.commit_comment?}
|
||||
after_create :subscribe_users
|
||||
after_initialize do |comment|
|
||||
class_eval { def commentable; project.git_repository.commit(commentable_id.to_s(16)); end } if commit_comment?
|
||||
|
||||
attr_accessible :body
|
||||
|
||||
def commentable
|
||||
commit_comment? ? project.git_repository.commit(commentable_id.to_s(16)) : super
|
||||
end
|
||||
|
||||
attr_accessible :body, :commentable_id, :commentable_type
|
||||
attr_readonly :commentable_id, :commentable_type
|
||||
def commentable=(c)
|
||||
if self.class.commit_comment?(c.class)
|
||||
self.commentable_id = c.id.hex
|
||||
self.commentable_type = c.class.name
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def own_comment?(user)
|
||||
user_id == user.id
|
||||
def self.commit_comment?(class_name)
|
||||
class_name.to_s == 'Grit::Commit'
|
||||
end
|
||||
|
||||
def commit_comment?
|
||||
commentable_type == 'Grit::Commit'
|
||||
self.class.commit_comment?(commentable_type)
|
||||
end
|
||||
|
||||
def self.issue_comment?(class_name)
|
||||
class_name.to_s == 'Issue'
|
||||
end
|
||||
|
||||
def issue_comment?
|
||||
self.class.issue_comment?(commentable_type)
|
||||
end
|
||||
|
||||
def own_comment?(user)
|
||||
user_id == user.id
|
||||
end
|
||||
|
||||
def can_notify_on_new_comment?(subscribe)
|
||||
|
@ -32,19 +54,19 @@ class Comment < ActiveRecord::Base
|
|||
protected
|
||||
|
||||
def subscribe_on_reply
|
||||
self.commentable.subscribes.create(:user_id => self.user_id) if !self.commentable.subscribes.exists?(:user_id => self.user_id)
|
||||
commentable.subscribes.create(:user_id => user_id) if !commentable.subscribes.exists?(:user_id => user_id)
|
||||
end
|
||||
|
||||
def subscribe_users
|
||||
if self.commentable.class == Issue
|
||||
self.commentable.subscribes.create(:user => self.user) if !self.commentable.subscribes.exists?(:user_id => self.user.id)
|
||||
elsif self.commit_comment?
|
||||
recipients = self.project.relations.by_role('admin').where(:object_type => 'User').map &:object # admins
|
||||
recipients << self.user << User.where(:email => self.commentable.committer.email).first # commentor and committer
|
||||
recipients << self.project.owner if self.project.owner_type == 'User' # project owner
|
||||
if issue_comment?
|
||||
commentable.subscribes.create(:user => user) if !commentable.subscribes.exists?(:user_id => user.id)
|
||||
elsif commit_comment?
|
||||
recipients = project.relations.by_role('admin').where(:object_type => 'User').map &:object # admins
|
||||
recipients << user << User.where(:email => commentable.committer.email).first # commentor and committer
|
||||
recipients << project.owner if project.owner_type == 'User' # project owner
|
||||
recipients.compact.uniq.each do |user|
|
||||
options = {:project_id => self.project.id, :subscribeable_id => self.commentable_id, :subscribeable_type => self.commentable.class.name, :user_id => user.id}
|
||||
Subscribe.subscribe_to_commit(options) if Subscribe.subscribed_to_commit?(self.project, user, self.commentable)
|
||||
options = {:project_id => project.id, :subscribeable_id => commentable_id, :subscribeable_type => commentable.class.name, :user_id => user.id}
|
||||
Subscribe.subscribe_to_commit(options) if Subscribe.subscribed_to_commit?(project, user, commentable)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,7 +18,7 @@ class Group < ActiveRecord::Base
|
|||
|
||||
scope :search_order, order("CHAR_LENGTH(uname) ASC")
|
||||
scope :without, lambda {|a| where("groups.id NOT IN (?)", a)}
|
||||
scope :search, lambda {|q| where("uname ILIKE ?", "%#{q.strip}%")}
|
||||
scope :search, lambda {|q| where("uname ILIKE ?", "%#{q.to_s.strip}%")}
|
||||
scope :opened, where('1=1')
|
||||
scope :by_owner, lambda {|owner| where(:owner_id => owner.id)}
|
||||
scope :by_admin, lambda {|admin| joins(:objects).where(:'relations.role' => 'admin', :'relations.object_id' => admin.id, :'relations.object_type' => 'User')}
|
||||
|
|
|
@ -29,7 +29,7 @@ class Platform < ActiveRecord::Base
|
|||
after_update :update_owner_relation
|
||||
|
||||
scope :search_order, order("CHAR_LENGTH(name) ASC")
|
||||
scope :search, lambda {|q| where("name ILIKE ?", "%#{q.strip}%")}
|
||||
scope :search, lambda {|q| where("name ILIKE ?", "%#{q.to_s.strip}%")}
|
||||
scope :by_visibilities, lambda {|v| where(:visibility => v)}
|
||||
scope :opened, where(:visibility => 'open')
|
||||
scope :hidden, where(:visibility => 'hidden')
|
||||
|
|
|
@ -29,7 +29,7 @@ class Project < ActiveRecord::Base
|
|||
|
||||
scope :recent, order("name ASC")
|
||||
scope :search_order, order("CHAR_LENGTH(name) ASC")
|
||||
scope :search, lambda {|q| by_name("%#{q.strip}%")}
|
||||
scope :search, lambda {|q| by_name("%#{q.to_s.strip}%")}
|
||||
scope :by_name, lambda {|name| where('projects.name ILIKE ?', name)}
|
||||
scope :by_visibilities, lambda {|v| where(:visibility => v)}
|
||||
scope :opened, where(:visibility => 'open')
|
||||
|
@ -181,10 +181,6 @@ class Project < ActiveRecord::Base
|
|||
system("#{Rails.root.join('bin', 'import_srpm.sh')} #{srpm_path} #{path} #{branch_name} >> /dev/null 2>&1")
|
||||
end
|
||||
|
||||
def self.commit_comments(commit, project)
|
||||
comments = Comment.where(:commentable_id => commit.id.hex, :commentable_type => 'Grit::Commit')
|
||||
end
|
||||
|
||||
def owner?(user)
|
||||
owner == user
|
||||
end
|
||||
|
|
|
@ -49,7 +49,7 @@ class User < ActiveRecord::Base
|
|||
|
||||
scope :search_order, order("CHAR_LENGTH(uname) ASC")
|
||||
scope :without, lambda {|a| where("users.id NOT IN (?)", a)}
|
||||
scope :search, lambda {|q| where("uname ILIKE ?", "%#{q.strip}%")}
|
||||
scope :search, lambda {|q| where("uname ILIKE ?", "%#{q.to_s.strip}%")}
|
||||
scope :opened, where('1=1')
|
||||
scope :banned, where(:role => 'banned')
|
||||
scope :admin, where(:role => 'admin')
|
||||
|
|
|
@ -30,10 +30,10 @@ class CommentPresenter < ApplicationPresenter
|
|||
def buttons
|
||||
project = options[:project]
|
||||
commentable = options[:commentable]
|
||||
(ep, dp) = if commentable.class == Issue
|
||||
(ep, dp) = if Comment.issue_comment?(commentable.class)
|
||||
[edit_project_issue_comment_path(project, commentable, comment),
|
||||
project_issue_comment_path(project, commentable, comment)]
|
||||
elsif commentable.class == Grit::Commit
|
||||
elsif Comment.commit_comment?(commentable.class)
|
||||
[edit_project_commit_comment_path(project, commentable, comment),
|
||||
project_commit_comment_path(project, commentable, comment)]
|
||||
end
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
#open-comment.comment.view
|
||||
%h3.tmargin0= t("layout.comments.new_header")
|
||||
- if commentable.class == Issue
|
||||
- if Comment.issue_comment?(commentable.class)
|
||||
- new_path = project_issue_comments_path(project, commentable)
|
||||
- is_subscribed = commentable.subscribes.exists?(:user_id => current_user.id)
|
||||
- subscribe_path = is_subscribed ? project_issue_subscribe_path(project, commentable, current_user.id) : project_issue_subscribes_path(project, commentable)
|
||||
- else commentable.class == Grit::Commit
|
||||
- else Comment.commit_comment?(commentable.class)
|
||||
- new_path = project_commit_comments_path(project, commentable)
|
||||
- is_subscribed = Subscribe.subscribed_to_commit?(project, current_user, commentable)
|
||||
- subscribe_path = is_subscribed ? unsubscribe_commit_path(project, commentable) : subscribe_commit_path(project, commentable)
|
||||
|
|
|
@ -1,14 +1,2 @@
|
|||
.wrapper
|
||||
= f.text_area :body, :cols => 80
|
||||
.comment-right
|
||||
= submit_tag t("layout.save")
|
||||
-#.group
|
||||
= f.label :body, t("activerecord.attributes.comment.body"), :class => :label
|
||||
= f.text_area :body, :class => 'text_field', :cols => 80
|
||||
|
||||
-#.group.navform.wat-cf
|
||||
%button.button{:type => "submit"}
|
||||
= image_tag("choose.png", :alt => t("layout.save"))
|
||||
= t("layout.save")
|
||||
%span.text_button_padding= t("layout.or")
|
||||
= link_to t("layout.cancel"), @commentable_path , :class => "text_button_padding link_button"
|
||||
.wrapper= f.text_area :body, :cols => 80
|
||||
.comment-right= submit_tag t("layout.save")
|
|
@ -4,36 +4,3 @@
|
|||
- list.each do |comment|
|
||||
- CommentPresenter.present(comment, :project => project, :commentable => commentable) do |presenter|
|
||||
= render :partial => 'shared/feed_message', :locals => {:presenter => presenter}
|
||||
-#.block#block-list
|
||||
.content
|
||||
%h2.title
|
||||
= t("layout.issues.comments_header")
|
||||
.inner
|
||||
%ul.list
|
||||
- list.each do |comment|
|
||||
%li
|
||||
.left
|
||||
= link_to comment.user.uname, user_path(comment.user.uname)
|
||||
.item
|
||||
= comment.body
|
||||
%br
|
||||
%br
|
||||
- if commentable.class == Issue
|
||||
- edit_path = edit_project_issue_comment_path(project, commentable, comment)
|
||||
- delete_path = project_issue_comment_path(project, commentable, comment)
|
||||
- elsif commentable.class == Grit::Commit
|
||||
- edit_path = edit_project_commit_comment_path(project, commentable, comment)
|
||||
- delete_path = project_commit_comment_path(project, commentable, comment)
|
||||
= link_to t("layout.edit"), edit_path if can? :update, comment
|
||||
=# link_to image_tag("x.png", :alt => t("layout.delete")) + " " + t("layout.delete"), delete_path, :method => "delete", :class => "button", :confirm => t("layout.comments.confirm_delete") if can? :delete, comment
|
||||
= link_to t("layout.delete"), delete_path, :method => "delete", :confirm => t("layout.comments.confirm_delete") if can? :delete, comment
|
||||
|
||||
-#.block
|
||||
.content
|
||||
%h2.title
|
||||
= t("layout.comments.new_header")
|
||||
.inner
|
||||
- new_path = project_issue_comments_path(project, commentable) if commentable.class == Issue
|
||||
- new_path = project_commit_comments_path(project, commentable) if commentable.class == Grit::Commit
|
||||
= form_for :comment, :url => new_path, :method => :post, :html => { :class => :form } do |f|
|
||||
= render :partial => "comments/form", :locals => {:f => f}
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
.block
|
||||
.secondary-navigation
|
||||
%ul.wat-cf
|
||||
%li.first= link_to t("layout.comments.back"), @commentable_path
|
||||
%li.first= link_to t("layout.comments.back"), project_commentable_path(@project, @commentable)
|
||||
.content
|
||||
%h2.title
|
||||
= t("layout.comments.edit_header")
|
||||
.inner
|
||||
= form_for @comment, :url => @update_url, :html => { :class => :form } do |f|
|
||||
= form_for @comment, :url => project_commentable_comment_path(@project, @commentable, @comment), :html => {:class => :form} do |f|
|
||||
= render :partial => "form", :locals => {:f => f}
|
||||
|
|
|
@ -16,5 +16,5 @@
|
|||
#repo-wrapper
|
||||
= render :partial => 'show'
|
||||
|
||||
= render :partial => "comments/list", :locals => {:list => Project.commit_comments(@commit, @project), :project => @project, :commentable => @commit}
|
||||
= render :partial => "comments/list", :locals => {:list => Comment.for_commit(@commit), :project => @project, :commentable => @commit}
|
||||
= render :partial => "comments/add", :locals => {:project => @project, :commentable => @commit} if current_user
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
%p== Hello, #{@user.user_appeal}.
|
||||
|
||||
- if @comment.commentable.class == Issue
|
||||
- if @comment.issue_comment?
|
||||
- link = link_to @comment.commentable.title, project_issue_url(@comment.commentable.project, @comment.commentable)
|
||||
- object = 'issue'
|
||||
- elsif @comment.commit_comment?
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
%p== Здравствуйте, #{@user.user_appeal}.
|
||||
|
||||
- if @comment.commentable.class == Issue
|
||||
- if @comment.issue_comment?
|
||||
- link = link_to @comment.commentable.title, project_issue_url(@comment.commentable.project, @comment.commentable)
|
||||
- object = 'задаче'
|
||||
- elsif @comment.commit_comment?
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- encoding : utf-8 -*-
|
||||
class RemoveContainersAndRpms < ActiveRecord::Migration
|
||||
def up
|
||||
drop_table :containers
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- encoding : utf-8 -*-
|
||||
class CustomizePlatform < ActiveRecord::Migration
|
||||
def self.up
|
||||
change_column_null :platforms, :name, false
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- encoding : utf-8 -*-
|
||||
class ClearProduct < ActiveRecord::Migration
|
||||
def self.up
|
||||
remove_column :products, :build_status
|
||||
|
|
12
db/schema.rb
|
@ -11,14 +11,14 @@
|
|||
#
|
||||
# It's strongly recommended to check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(:version => 20120403110931) do
|
||||
ActiveRecord::Schema.define(:version => 20120404134602) do
|
||||
|
||||
create_table "activity_feeds", :force => true do |t|
|
||||
t.integer "user_id", :null => false
|
||||
t.string "kind"
|
||||
t.text "data"
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.datetime "created_at", :null => false
|
||||
t.datetime "updated_at", :null => false
|
||||
end
|
||||
|
||||
create_table "arches", :force => true do |t|
|
||||
|
@ -102,7 +102,7 @@ ActiveRecord::Schema.define(:version => 20120403110931) do
|
|||
t.string "locked_by"
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.string "queue"
|
||||
t.string "queue", :default => "default"
|
||||
end
|
||||
|
||||
add_index "delayed_jobs", ["priority", "run_at"], :name => "delayed_jobs_priority"
|
||||
|
@ -331,16 +331,16 @@ ActiveRecord::Schema.define(:version => 20120403110931) do
|
|||
t.string "name"
|
||||
t.string "email", :default => "", :null => false
|
||||
t.string "encrypted_password", :limit => 128, :default => "", :null => false
|
||||
t.string "password_salt", :default => "", :null => false
|
||||
t.string "reset_password_token"
|
||||
t.datetime "remember_created_at"
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.text "ssh_key"
|
||||
t.string "uname"
|
||||
t.string "role"
|
||||
t.string "language", :default => "en"
|
||||
t.integer "own_projects_count", :default => 0, :null => false
|
||||
t.datetime "reset_password_sent_at"
|
||||
t.integer "own_projects_count", :default => 0, :null => false
|
||||
t.text "professional_experience"
|
||||
t.string "site"
|
||||
t.string "company"
|
||||
|
|
|
@ -1,323 +0,0 @@
|
|||
КРАТКОЕ ОПИСАНИЕ ACL
|
||||
====================
|
||||
|
||||
Предназначение
|
||||
--------------
|
||||
|
||||
ACL предназначена для контроля прав пользователя на выполнение действий в
|
||||
системе и доступа к моделям по областям видимости.
|
||||
|
||||
Решаемые задачи
|
||||
---------------
|
||||
|
||||
* Проверка наличия у пользователя прав для выполнения метода контроллера;
|
||||
* Прозрачная фильтрация моделей для исключения невидимых для пользователя
|
||||
записей.
|
||||
|
||||
Возможности
|
||||
-----------
|
||||
|
||||
* Неограниченное количество моделей, над которыми могут выполняться
|
||||
действия (`target`);
|
||||
* Неограниченное количество моделей, которые могут выполнять действия над
|
||||
другими (`acter`);
|
||||
* Геренатор прав основывающийся на структуре приложения (см. далее);
|
||||
* Неограниченное количество ролей, которые могут назначаться для `acter` и
|
||||
содержать любую комбинацию прав и доступных видимостей;
|
||||
* Объединение прав `acter`-ов на глубину одной модели (см. далее);
|
||||
* Разграничение назначения ролей по классам (не завершено, на данный
|
||||
момент не критично);
|
||||
* Разграничение ролей на глобальные и локальные (см. далее).
|
||||
|
||||
Типы моделей, с которыми взаимодействует ACL
|
||||
--------------------------------------------
|
||||
|
||||
* __ActerModel__ -- модель, которая может выполнять действия, разрешенные
|
||||
ролями, над другими моделями;
|
||||
* __TargetTarget__ -- модель, над которой могут выполняться действия,
|
||||
разрешенные ролями.
|
||||
|
||||
__ActerModel__ может иметь глобальную роль, которая определяет возможность
|
||||
выполнения действий без привязки к конкретному экземпляру __TargetModel__ и
|
||||
неограниченное количество прав по отношению к конкретному экземпляру
|
||||
__TargetModel__.
|
||||
|
||||
__TODO__: *Реализовать дополнение необходимым функционалом моделей, выбранных
|
||||
в качестве __ActerModel__ или __TargetModel__ при декларировании их роли в
|
||||
системе*
|
||||
|
||||
Схема взаимодействия объектов ACL
|
||||
---------------------------------
|
||||
|
||||
Функционал ACL реализуется путем взаимодействия моделей `Right, Role, Relation`,
|
||||
реализующих основной функционал и особых моделей проекта, обозначенных на схеме
|
||||
как `ActerModel` и `TargetModel`.
|
||||
|
||||
Экземпляры __ActerModel__ и __TargetModel__ связываются посредством модели
|
||||
`Relation`, через которую экземпляр __ActerModel__ получает неограниченное
|
||||
количество ролей по отношению к экземпляру __TargetModel__.
|
||||
|
||||
### Схема связей моделей:
|
||||
|
||||
--------------
|
||||
| ActerModel |
|
||||
/ --------------
|
||||
--------- -------- |
|
||||
| Right | | Role | V
|
||||
--------- -------- ------------
|
||||
... <= ... <= | Relation |
|
||||
--------- -------- ------------
|
||||
| Right | | Role | |
|
||||
--------- -------- V
|
||||
---------------
|
||||
| TargetModel |
|
||||
---------------
|
||||
|
||||
* Обозначения: <= -- Связь с несколькими моделями
|
||||
<-,/,| -- Связь с одной моделью
|
||||
|
||||
|
||||
Генератор прав
|
||||
--------------
|
||||
|
||||
Генератор ролей является Rake-task-ом и запускается командой
|
||||
`rake rights:generate`.
|
||||
|
||||
Желательно запускать после добавления нового метода в контроллер для того,
|
||||
чтобы на этот метод в системе появилось право.
|
||||
|
||||
Загрузка ролей из дампа
|
||||
-----------------------
|
||||
|
||||
Загрузку ролей из заранее подготовленного дампа можно произвести двумя
|
||||
способами:
|
||||
|
||||
* В консоли, используя Rake task `rake roles:load`, который загрузит в базу
|
||||
роли, описанные в `config/roles.yml`
|
||||
* Через Web-интерфейс, на странице `/roles`, если у пользователя есть
|
||||
соответствующие права. Для загрузки через Web-интерфейс необходимо выбрать
|
||||
файл в поле выбора вверху страницы и нажать __Загрузить__.
|
||||
|
||||
Получение дампа ролей
|
||||
---------------------
|
||||
|
||||
Дамп ролей может получить пользователь, имеющий на это права, зайдя на страницу
|
||||
`roles` и нажав кнопку `Скачать в YML`.
|
||||
|
||||
Задание областей видимости моделей
|
||||
----------------------------------
|
||||
*Этот функционал скорее всего будет изменяться*
|
||||
|
||||
Если модель должна иметь несколько областей видимости, нужно сделать следующее:
|
||||
|
||||
* Добавить в модель константу `VISIBILITIES`, в которой задать названия областей
|
||||
видимости;
|
||||
* Добавить к таблице моделей поле `visibility:text`;
|
||||
* Добавить `attr_accessible :visibility` в модель;
|
||||
* Создать `scope :by_visibility`, принимающий аргументом массив областей
|
||||
видимости.
|
||||
|
||||
После выполнения этих действий на странице редактирования роли появится поле
|
||||
выбора областей видимости для этой модели.
|
||||
|
||||
### Пример:
|
||||
|
||||
model VisibilitiesExample < ActiveRecord::Base
|
||||
VISIBILITIES = ['open', 'hidden', 'open_for_admins']
|
||||
attr_accessible :visibility
|
||||
|
||||
scope :by_visibility, lambda {|v| {:conditions => ['visibility in (?)', v]}}
|
||||
end
|
||||
|
||||
*Назначение методов описано в API*
|
||||
|
||||
Задание типа модели
|
||||
-------------------
|
||||
*Этот функционал скорее всего будет изменяться*
|
||||
|
||||
Если модель должна иметь возможность быть связанной с другими с использованием
|
||||
ролей, необходимо произвести следующие действия:
|
||||
|
||||
* Добавить в модель декларацию `relationable`, с аргументом `:as`, который
|
||||
может принимать заначения из `[:object, :target]`. Если модель будет
|
||||
__acter__-ом, передается `:object`, иначе `:target`
|
||||
Пример: `relationable :as => :object`
|
||||
* Добавить в модель связь `belongs_to :global_role, :class_name => 'Role'`
|
||||
* Добавить в модель связь с моделью `Relation`
|
||||
* Если модель -- __acter__ и она должна использовать как свои роли, так и
|
||||
роли из другой модели, необходимо добавить декларацию `inherit_rights_from`
|
||||
которой аргументом присвоить имя/имена связей с моделями, из которых должны
|
||||
браться роли.
|
||||
|
||||
### Примеры:
|
||||
|
||||
* Модель, являющаяся __acter__:
|
||||
|
||||
class ActerModel < ActiveRecord::Base
|
||||
relationable :as => :object
|
||||
|
||||
belongs_to :global_role, :class_name => 'Role'
|
||||
has_many :targets, :as => :object, :class_name => 'Relation'
|
||||
end
|
||||
* Модель, являющаяся __acter__ и наследующая права другой модели:
|
||||
|
||||
class ActerWithInheritableRolesModel < ActiveRecord::Base
|
||||
relationable :as => :object
|
||||
ingerit_rights_from :another_acter_model
|
||||
|
||||
has_many :another_acters_models
|
||||
|
||||
belongs_to :global_role, :class_name => 'Role'
|
||||
has_many :targets, :as => :object, :class_name => 'Relation'
|
||||
end
|
||||
* Модель, являющаяся __target__:
|
||||
|
||||
class TargetModel < ActiveRecord::Base
|
||||
relationable :as => :target
|
||||
|
||||
has_many :objects, :as => :target, :class_name => 'Relation'
|
||||
end
|
||||
* Модель, являющаяся и __acter__, и __target__:
|
||||
|
||||
class ActerAndTargetModel < ActiveRecord::Base
|
||||
relationable :as => :object
|
||||
relationable :as => :target
|
||||
|
||||
belongs_to :global_role, :class_name => 'Role'
|
||||
has_many :targets, :as => :object, :class_name => 'Relation'
|
||||
has_many :objects, :as => :target, :class_name => 'Relation'
|
||||
end
|
||||
|
||||
*Назначение методов описано в API*
|
||||
|
||||
Использование ACL в контроллере
|
||||
-------------------------------
|
||||
|
||||
Если необходимо ограничить доступ ко всем методам контроллера по глобальной
|
||||
роли пользователя вне зависимости от текущей модели, необходимо установить
|
||||
`before_filter :check_global_rights`.
|
||||
В случае, если у пользователя нет прав для выполнения текущего действия, он
|
||||
будет переотправлен на предыдущую страницу.
|
||||
|
||||
Если необходимо проверить, может ли пользователь выполнить конкретное действие,
|
||||
необходимо в начале этого метода вызвать метод `can_perform?`. Если методу
|
||||
передан параметр, являющийся экземпляром класса __TargetModel__, метод возвратит
|
||||
`true`, если одна или несколько ролей пользователя над этой моделью позволяет
|
||||
ему выполнить может выполнить действие и `false` в противном случае. Если
|
||||
необязательный параметр опущен, или в качестве параметра передано `:system`,
|
||||
учитываются только глобальные роли.
|
||||
|
||||
### Примеры
|
||||
|
||||
* Контроллер, некоторые методы которого доступны для всех:
|
||||
|
||||
class StuffController < ApplicationController
|
||||
def index # доступ у всех
|
||||
...
|
||||
end
|
||||
|
||||
def show # 'Что-то полезное' выполнится только у тех, чьи роли над
|
||||
# @data позволяют выполнить конкретное действие.
|
||||
@data = Stuff.find(params[:id])
|
||||
if can_perform? @data
|
||||
#что-то полезное
|
||||
else
|
||||
# сообщаем пользователю, что он не может выполнить действие
|
||||
end
|
||||
end
|
||||
|
||||
def create # 'Что-то полезное' выполнится только у тех, чьи
|
||||
# глобальные роли позволяют выполнить метод
|
||||
if can_perform?
|
||||
# что-то полезное
|
||||
else
|
||||
# сообщаем пользователю, что он не может выполнить действие
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
* Контроллер, доступ к методам которого возможен только при наличии необходимых
|
||||
прав в глобальных ролях:
|
||||
|
||||
class StuffController < ApplicationController
|
||||
before_filter :check_global_rights # разрешаем доступ только тем,
|
||||
# чьи роли это позволяют.
|
||||
|
||||
def index # доступ только у тех, кому это позволяет глобальная роль
|
||||
...
|
||||
end
|
||||
|
||||
def show # 'Что-то полезное' выполнится только у тех, чьи роли
|
||||
# над @data это позволяют
|
||||
@data = Stuff.find(params[:id])
|
||||
if can_perform? @data
|
||||
#что-то полезное
|
||||
else
|
||||
# сообщаем пользователю, что он не может выполнить действие
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Использование ACL во view
|
||||
-------------------------
|
||||
|
||||
Используется метод `can_perform?` модели, для которой нужно проверить права
|
||||
доступа. Обычно этой моделью является `current_user`.
|
||||
|
||||
### Примеры:
|
||||
|
||||
* Проверка на возможность выполнения глобального действия:
|
||||
|
||||
-if current_user.can_perform?('some_controller', 'some_aciton')
|
||||
%a{:href => controller_action_path}= Some description
|
||||
|
||||
* Проверка на возможность выполнения действия над текущей моделью:
|
||||
|
||||
-if current_user.can_perform?('some_controller', 'some_aciton', @data)
|
||||
%a{:href => controller_action_path(@data)}= Some description
|
||||
|
||||
API для работы с ACL
|
||||
--------------------
|
||||
*Этот функционал скорее всего будет изменяться*
|
||||
|
||||
### Методы потомков `ActiveRecord::Base`
|
||||
|
||||
* Методы классов:
|
||||
- `relationable` -- устанавливает, кем является модель (acter/target)
|
||||
- `relationable?` -- может ли иметь связь с ролью/ролями с другими
|
||||
- `relation_acters` -- список моделей, которые могут иметь роли
|
||||
по отношению к другим (след. метод)
|
||||
- `relation_targets` -- список моделей, над которыми могут совершаться
|
||||
действия
|
||||
- `relation_acter? (model)`, `relation_target? (model)` -- является ли
|
||||
тем или другим
|
||||
- `inherit_rights_from (:relation_name | [:relation_names])` -- права из
|
||||
каких связанных моделей наследовать
|
||||
- `visible_to (model)` -- все видимые для модели записи, может
|
||||
включаться в цепочку (например, для paginate)
|
||||
|
||||
* Методы инстансов:
|
||||
- `add_role_to(acter, role)` -- привязать acter-а с ролью к текущей записи
|
||||
- `add_role_on(target, role)` -- привязать текущую модель с ролью
|
||||
- `roles_to(object)` -- если object == :system, возвращает глобальные роли
|
||||
текущей записи, если передана запись -- то роли текущей модели над записью
|
||||
- `rights_to(object)` -- аргументы те же, но возвращается список прав,
|
||||
собранный из всех ролей
|
||||
- `right_to(controller_name, action)` -- возвращает запись с правом на
|
||||
выполнение действия action в контроллере c именем `controller_name`
|
||||
- `can_perform? (controller_name, action, target = :system)` -- показывает,
|
||||
может ли текущая модель выполнить действие контроллера над целью
|
||||
|
||||
### Методы потомков `ActiveController::Base`
|
||||
*Возможно, будут вынесены в хелпер для универсализации системы*
|
||||
|
||||
- `can_perform? (target = :system)` -- может ли `current_user` выполнить
|
||||
текущее действие
|
||||
- `check_global_access` -- делает редирект назад или на главную, если
|
||||
пользователь вообще не может совершить текущее действие
|
||||
- `roles_to(object)` -- возвращает список ролей `current_user`-а по отношению
|
||||
к объекту
|
||||
- `rights_to(object)` -- возвращает список прав `current_user`-а по отношению
|
||||
к объекту
|
||||
|
|
@ -23,9 +23,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="active">Проекты</a>
|
||||
|
@ -43,9 +46,7 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -22,9 +22,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="active">Проекты</a>
|
||||
|
@ -42,9 +45,6 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -24,9 +24,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="active">Проекты</a>
|
||||
|
@ -44,9 +47,7 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -18,9 +18,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="active">Проекты</a>
|
||||
|
@ -38,9 +41,7 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -20,9 +20,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="active">Проекты</a>
|
||||
|
@ -40,9 +43,6 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -23,9 +23,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Проекты</a>
|
||||
|
@ -43,9 +46,7 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -18,9 +18,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="active">Проекты</a>
|
||||
|
@ -38,9 +41,6 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -17,9 +17,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Проекты</a>
|
||||
|
@ -37,9 +40,6 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -22,9 +22,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="active">Проекты</a>
|
||||
|
@ -42,9 +45,6 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -17,9 +17,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Проекты</a>
|
||||
|
@ -37,9 +40,6 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first active">Главная</a>
|
||||
|
@ -37,9 +40,6 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -22,9 +22,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="active">Проекты</a>
|
||||
|
@ -42,9 +45,6 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -24,9 +24,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="active">Проекты</a>
|
||||
|
@ -44,9 +47,7 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -21,9 +21,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Проекты</a>
|
||||
|
@ -41,9 +44,6 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -18,9 +18,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="active">Проекты</a>
|
||||
|
@ -38,9 +41,6 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -27,9 +27,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Проекты</a>
|
||||
|
@ -47,9 +50,6 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -18,9 +18,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Проекты</a>
|
||||
|
@ -38,9 +41,6 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -27,9 +27,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Проекты</a>
|
||||
|
@ -47,9 +50,7 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -21,9 +21,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="active">Проекты</a>
|
||||
|
@ -41,9 +44,6 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
@ -100,8 +100,14 @@
|
|||
<th class="th3">Роль в проекте</th>
|
||||
<th class="th4">Покинуть проект</th>
|
||||
</tr>
|
||||
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="search">
|
||||
<td colspan="4">
|
||||
<input type="text" value="Найти проект..." class="gray" onClick="if(this.value=='Найти проект...'){this.value='';this.className='black';}" onblur="if(this.value==''){this.value='Найти проект...';this.className='gray';}" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="Row1">
|
||||
<td><a href="#"><div class="table-sort-left"><img src="pics/unlock.png" alt="unlock"></div><div class="table-sort-right">gsapronov/ROSA-Jabber</div></a></td>
|
||||
<td class="td2">Проект создан для разработки интернет-пейджера, с простым
|
||||
|
|
|
@ -18,9 +18,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Проекты</a>
|
||||
|
@ -38,9 +41,6 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -20,9 +20,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="active">Проекты</a>
|
||||
|
@ -40,9 +43,6 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -20,9 +20,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Проекты</a>
|
||||
|
@ -40,9 +43,6 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -20,9 +20,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Проекты</a>
|
||||
|
@ -40,9 +43,6 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -17,9 +17,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Проекты</a>
|
||||
|
@ -37,9 +40,7 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -17,9 +17,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Проекты</a>
|
||||
|
@ -37,9 +40,6 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -20,9 +20,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Проекты</a>
|
||||
|
@ -40,9 +43,6 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -17,9 +17,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Проекты</a>
|
||||
|
@ -37,9 +40,6 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -18,9 +18,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="active">Проекты</a>
|
||||
|
@ -38,9 +41,6 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -18,9 +18,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Проекты</a>
|
||||
|
@ -38,9 +41,6 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -37,9 +37,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="active">Проекты</a>
|
||||
|
@ -57,9 +60,6 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
@ -137,7 +137,7 @@
|
|||
<article>
|
||||
<div class="all">
|
||||
|
||||
<div class="desription-top">
|
||||
<div class="description-top">
|
||||
<div class="img">
|
||||
<img src="pics/code.png" alt="pic" />
|
||||
</div>
|
||||
|
|
|
@ -37,9 +37,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="active">Проекты</a>
|
||||
|
@ -57,9 +60,6 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
@ -137,7 +137,7 @@
|
|||
<article>
|
||||
<div class="all">
|
||||
|
||||
<div class="desription-top">
|
||||
<div class="description-top">
|
||||
<input class="name" value="git@git.com:snigipini/wacomdrivers.git" />
|
||||
<div class="role">
|
||||
чтение и запись
|
||||
|
|
|
@ -17,9 +17,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first active">Главная</a>
|
||||
<a href="#" class="active">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Проекты</a>
|
||||
|
@ -37,9 +40,7 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -23,9 +23,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Проекты</a>
|
||||
|
@ -43,9 +46,6 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -137,7 +137,7 @@
|
|||
</aside>
|
||||
<!--Right part of page-->
|
||||
<div class="right">
|
||||
<div class="desription-top">
|
||||
<div class="description-top">
|
||||
<div class="img">
|
||||
<img src="pics/code.png" alt="pic" />
|
||||
</div>
|
||||
|
|
|
@ -18,9 +18,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="active">Проекты</a>
|
||||
|
@ -38,9 +41,7 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -18,9 +18,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="active">Проекты</a>
|
||||
|
@ -38,9 +41,6 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
|
@ -0,0 +1,217 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>Сборочная среда</title>
|
||||
<script type="text/javascript" src="js/html5shiv.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="styles/main.css" />
|
||||
<script type="text/javascript" src="js/jquery-1.7.1.min.js"></script>
|
||||
<script type="text/javascript" src="js/all.js"></script>
|
||||
<script type='text/javascript' src='js/checkbox-main.js'></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap columns">
|
||||
<!--Top block-->
|
||||
<header>
|
||||
<div class="left">
|
||||
</div>
|
||||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="active">Проекты</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Группы</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Мониторинг</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Платформа</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
<div class="search">
|
||||
<div class="pic">
|
||||
</div>
|
||||
<div class="field">
|
||||
<input type="text" value="Поиск" class="gray" onClick="if(this.value=='Поиск'){this.value='';this.className='black';}" onblur="if(this.value==''){this.value='Поиск';this.className='gray';}" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="user">
|
||||
<div class="avatar" onclick="droplist();">
|
||||
<img src="pics/ava.png" alt="avatar" height="30" />
|
||||
</div>
|
||||
<div class="profile" onclick="droplist();">
|
||||
<a href="#" onclick="droplist();">mikimaus <img src="pics/expand-white.png" alt="ex" /></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="both">
|
||||
</div>
|
||||
|
||||
<div class="droplist-wrap">
|
||||
<div class="droplist" id="droplist">
|
||||
<div class="a"><a href="#">Публичный профиль</a></div>
|
||||
<div class="a"><a href="#">Настройки</a></div>
|
||||
<div class="a"><a href="#">Выйти</a></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="right">
|
||||
</div>
|
||||
<div class="both">
|
||||
</div>
|
||||
</header>
|
||||
<!--Submenu-->
|
||||
<div class="sub-menu">
|
||||
<div class="left">
|
||||
WacomDrivers
|
||||
</div>
|
||||
<nav>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#">Проект</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Коммиты</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Сборки</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Трекер</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="active">Wiki</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Readme</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Настройки</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="both">
|
||||
</div>
|
||||
|
||||
<!--Page-->
|
||||
<article>
|
||||
<!--Left part of page-->
|
||||
<aside>
|
||||
<div class="admin-preferences">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#">Home</a>
|
||||
</li>
|
||||
<li class="active">
|
||||
<a href="#">Pages</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Wiki history</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Git access</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
<!--Right part of page-->
|
||||
<div class="right">
|
||||
<div class="left">
|
||||
<h3>Home</h3>
|
||||
</div>
|
||||
<div class="r">
|
||||
<a class="button" href="#">История</a>
|
||||
</div>
|
||||
<div class="both"></div>
|
||||
|
||||
<div id='wiki-content'>
|
||||
<div class='has-footer has-rightbar wrap'>
|
||||
<div class='gollum-markdown-content' id='wiki-body'>
|
||||
<div id='template'><p>Lorem ipsum.</p>
|
||||
|
||||
<p>Lorem ipsum dolor sit amet.</p>
|
||||
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse sit amet ullamcorper arcu. Cras congue, velit sed sollicitudin dignissim, turpis erat ultrices risus, vitae gravida ligula elit vel urna. Curabitur mattis dapibus rhoncus. Aenean lorem nisi, lacinia sed elementum nec, consectetur ac augue. Quisque vulputate sodales nunc, et imperdiet ante tincidunt eu. Donec velit enim, dapibus at pharetra a, scelerisque ut sapien. Nullam luctus pulvinar dui nec convallis. Nullam viverra nisi nec orci fringilla varius.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class='gollum-markdown-content' id='wiki-rightbar'>
|
||||
<p>Это сайдбар.
|
||||
Это сайдбар.
|
||||
Это сайдбар.
|
||||
Это сайдбар.
|
||||
Это сайдбар.
|
||||
Это сайдбар.
|
||||
Это сайдбар.</p>
|
||||
</div>
|
||||
<div class='gollum-markdown-content' id='wiki-footer'>
|
||||
<div id='footer-content'><p>Футер Футер Футер Футер</p></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id='gollum-footer'>
|
||||
<p id='last-edit'>
|
||||
Последним редактировал
|
||||
<b><a href="/users/14">ironsnake</a></b>
|
||||
около 19 часов
|
||||
назад
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class='both'></div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="both"></div>
|
||||
</article>
|
||||
|
||||
</div>
|
||||
<!--Footer-->
|
||||
<footer>
|
||||
<ul>
|
||||
<li>
|
||||
ROSA Лаб. © 2012 <img src="pics/square.png" alt="_" />
|
||||
</li>
|
||||
<li>
|
||||
<img src="pics/flag.png" alt="rosa" /> <img src="pics/square.png" alt="_" />
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">О компании</a> <img src="pics/square.png" alt="_" />
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Контакты</a> <img src="pics/square.png" alt="_" />
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Условия использования</a> <img src="pics/square.png" alt="_" />
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Конфиденциальность</a> <img src="pics/square.png" alt="_" />
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Безопасность</a>
|
||||
</li>
|
||||
</ul>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
|
@ -18,9 +18,12 @@
|
|||
<div class="middle">
|
||||
<!--Main menu-->
|
||||
<menu>
|
||||
<div class="logo">
|
||||
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#" class="first">Главная</a>
|
||||
<a href="#">Главная</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="active">Проекты</a>
|
||||
|
@ -38,9 +41,6 @@
|
|||
<a href="#">Документация</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="logo">
|
||||
<img src="pics/logo-mini.png" alt="logo" />
|
||||
</div>
|
||||
</menu>
|
||||
<div class="information">
|
||||
<!--Search-->
|
||||
|
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 941 B |
After Width: | Height: | Size: 939 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 667 B |
After Width: | Height: | Size: 667 B |
After Width: | Height: | Size: 818 B |
After Width: | Height: | Size: 818 B |
|
@ -0,0 +1,126 @@
|
|||
user rosa;
|
||||
worker_processes 2;
|
||||
|
||||
#error_log logs/error.log;
|
||||
#error_log logs/error.log notice;
|
||||
#error_log logs/error.log info;
|
||||
|
||||
#pid logs/nginx.pid;
|
||||
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
accept_mutex on;
|
||||
use epoll;
|
||||
}
|
||||
|
||||
|
||||
http {
|
||||
include mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
#log_format ma§in '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
# '$status $body_bytes_sent "$http_referer" '
|
||||
# '"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
#access_log logs/access.log main;
|
||||
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay off;
|
||||
|
||||
#keepalive_timeout 0;
|
||||
keepalive_timeout 65;
|
||||
|
||||
#gzip on;
|
||||
|
||||
client_max_body_size 1G;
|
||||
|
||||
server_names_hash_bucket_size 64;
|
||||
include conf.d/rosa_build.conf; # force default ip access
|
||||
include conf.d/rosa_build_*;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
#charset koi8-r;
|
||||
|
||||
#access_log logs/host.access.log main;
|
||||
|
||||
location / {
|
||||
root html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
|
||||
#error_page 404 /404.html;
|
||||
|
||||
# redirect server error pages to the static page /50x.html
|
||||
#
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root html;
|
||||
}
|
||||
|
||||
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
|
||||
#
|
||||
#location ~ \.php$ {
|
||||
# proxy_pass http://127.0.0.1;
|
||||
#}
|
||||
|
||||
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
|
||||
#
|
||||
#location ~ \.php$ {
|
||||
# root html;
|
||||
# fastcgi_pass 127.0.0.1:9000;
|
||||
# fastcgi_index index.php;
|
||||
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
|
||||
# include fastcgi_params;
|
||||
#}
|
||||
|
||||
# deny access to .htaccess files, if Apache's document root
|
||||
# concurs with nginx's one
|
||||
#
|
||||
#location ~ /\.ht {
|
||||
# deny all;
|
||||
#}
|
||||
}
|
||||
|
||||
|
||||
# another virtual host using mix of IP-, name-, and port-based configuration
|
||||
#
|
||||
#server {
|
||||
# listen 8000;
|
||||
# listen somename:8080;
|
||||
# server_name somename alias another.alias;
|
||||
|
||||
# location / {
|
||||
# root html;
|
||||
# index index.html index.htm;
|
||||
# }
|
||||
#}
|
||||
|
||||
|
||||
# HTTPS server
|
||||
#
|
||||
#server {
|
||||
# listen 443;
|
||||
# server_name localhost;
|
||||
|
||||
# ssl on;
|
||||
# ssl_certificate cert.pem;
|
||||
# ssl_certificate_key cert.key;
|
||||
|
||||
# ssl_session_timeout 5m;
|
||||
|
||||
# ssl_protocols SSLv2 SSLv3 TLSv1;
|
||||
# ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
# ssl_prefer_server_ciphers on;
|
||||
|
||||
# location / {
|
||||
# root html;
|
||||
# index index.html index.htm;
|
||||
# }
|
||||
#}
|
||||
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
upstream rosa_build_backend {
|
||||
# server 127.0.0.1:8080;
|
||||
server unix:/tmp/rosa_build_unicorn.sock;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name rosa-build.rosalab.ru;
|
||||
|
||||
root /srv/rosa_build/current/public;
|
||||
|
||||
if ($uri !~ downloads) {
|
||||
rewrite ^(.*) https://$host$1 permanent;
|
||||
}
|
||||
|
||||
location /downloads {
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
access_log /srv/rosa_build/shared/log/nginx.access.log;
|
||||
error_log /srv/rosa_build/shared/log/nginx.error.log;
|
||||
|
||||
#rewrite ^/downloads/(.*) http://$host/downloads/$1 break;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 default ssl;
|
||||
server_name 195.19.77.242;
|
||||
root /srv/rosa_build/current/public;
|
||||
|
||||
ssl on;
|
||||
ssl_certificate /etc/ssl/rosalinux.crt;
|
||||
ssl_certificate_key /etc/ssl/rosalinux.key;
|
||||
#ssl_verify_depth 3;
|
||||
|
||||
#location /downloads {
|
||||
#autoindex on;
|
||||
#}
|
||||
|
||||
try_files $uri/index.html $uri.html $uri @myapp;
|
||||
|
||||
location @myapp {
|
||||
proxy_pass http://rosa_build_backend;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_read_timeout 300;
|
||||
proxy_send_timeout 180;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
rewrite ^/downloads/(.*) http://$host/downloads/$1 break;
|
||||
|
||||
#access_log /srv/rosa_build/shared/log/nginx.access.log;
|
||||
#error_log /srv/rosa_build/shared/log/nginx.error.log;
|
||||
|
||||
error_page 500 502 503 504 /500.html;
|
||||
location = /500.html {
|
||||
root /srv/rosa_build/current/public;
|
||||
}
|
||||
}
|
|
@ -83,11 +83,18 @@ describe BuildListsController do
|
|||
end
|
||||
|
||||
context 'for guest' do
|
||||
if APP_CONFIG['anonymous_access']
|
||||
it 'should be able to perform index action' do
|
||||
get :index
|
||||
response.should be_success
|
||||
end
|
||||
else
|
||||
it 'should not be able to perform index action' do
|
||||
get :index
|
||||
response.should redirect_to(new_user_session_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'for user' do
|
||||
before(:each) do
|
||||
|
@ -305,13 +312,16 @@ describe BuildListsController do
|
|||
assigns[:build_lists].should_not include(@build_list2)
|
||||
assigns[:build_lists].should include(@build_list3)
|
||||
assigns[:build_lists].should_not include(@build_list4)
|
||||
# response.should be_success
|
||||
end
|
||||
end
|
||||
|
||||
context 'callbacks' do
|
||||
let(:build_list) { FactoryGirl.create(:build_list_core) }
|
||||
|
||||
before(:each) do
|
||||
mock(controller).authenticate_build_service! {true}
|
||||
end
|
||||
|
||||
describe 'publish_build' do
|
||||
before { test_git_commit(build_list.project); build_list.update_attribute :commit_hash, build_list.project.git_repository.commits('master').last.id }
|
||||
|
||||
|
|
|
@ -2,9 +2,7 @@
|
|||
require 'spec_helper'
|
||||
|
||||
def create_comment user
|
||||
comment = user.comments.create(:commentable_id => @commit.id.hex, :commentable_type => @commit.class.name,
|
||||
:body => 'test', :project_id => @project.id)
|
||||
comment
|
||||
FactoryGirl.create(:comment, :user => user, :commentable => @commit, :project => @project)
|
||||
end
|
||||
|
||||
shared_examples_for 'user with create comment rights for commits' do
|
||||
|
|
|
@ -39,7 +39,7 @@ end
|
|||
shared_examples_for 'user without issue update rights' do
|
||||
it 'should not be able to perform update action' do
|
||||
put :update, {:id => @issue.serial_id}.merge(@update_params)
|
||||
response.should redirect_to(forbidden_path)
|
||||
response.should redirect_to(controller.current_user ? forbidden_path : new_user_session_path)
|
||||
end
|
||||
|
||||
it 'should not update issue title' do
|
||||
|
@ -51,11 +51,11 @@ end
|
|||
shared_examples_for 'user without issue destroy rights' do
|
||||
it 'should not be able to perform destroy action' do
|
||||
delete :destroy, :id => @issue.serial_id, :project_id => @project.id
|
||||
response.should redirect_to(forbidden_path)
|
||||
response.should redirect_to(controller.current_user ? forbidden_path : new_user_session_path)
|
||||
end
|
||||
|
||||
it 'should not reduce issues count' do
|
||||
lambda{ delete :destroy, :id => @issue.serial_id, :project_id => @project.id }.should change{ Issue.count }.by(0)
|
||||
lambda{ delete :destroy, :id => @issue.serial_id, :project_id => @project.id }.should_not change{ Issue.count }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -185,4 +185,32 @@ describe IssuesController do
|
|||
it_should_behave_like 'user without issue destroy rights'
|
||||
it_should_behave_like 'project with issues turned off'
|
||||
end
|
||||
|
||||
context 'for guest' do
|
||||
if APP_CONFIG['anonymous_access']
|
||||
it_should_behave_like 'issue user with project reader rights'
|
||||
else
|
||||
it 'should not be able to perform index action' do
|
||||
get :index, :project_id => @project.id
|
||||
response.should redirect_to(new_user_session_path)
|
||||
end
|
||||
|
||||
it 'should not be able to perform show action' do
|
||||
get :show, :project_id => @project.id, :id => @issue.serial_id
|
||||
response.should redirect_to(new_user_session_path)
|
||||
end
|
||||
end
|
||||
|
||||
it 'should not be able to perform create action' do
|
||||
post :create, @create_params
|
||||
response.should redirect_to(new_user_session_path)
|
||||
end
|
||||
|
||||
it 'should not create issue object into db' do
|
||||
lambda{ post :create, @create_params }.should_not change{ Issue.count }
|
||||
end
|
||||
|
||||
it_should_behave_like 'user without issue update rights'
|
||||
it_should_behave_like 'user without issue destroy rights'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -55,11 +55,18 @@ describe ProductBuildListsController do
|
|||
response.should redirect_to(new_user_session_path)
|
||||
end
|
||||
|
||||
if APP_CONFIG['anonymous_access']
|
||||
it 'should be able to view ProductBuildLists' do
|
||||
get :index
|
||||
response.should be_success
|
||||
end
|
||||
else
|
||||
it 'should not be able to view ProductBuildLists' do
|
||||
get :index
|
||||
response.should redirect_to(new_user_session_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'for user' do
|
||||
before(:each) { set_session_for FactoryGirl.create(:user) }
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
# -*- encoding : utf-8 -*-
|
||||
require 'spec_helper'
|
||||
|
||||
shared_examples_for 'able search' do
|
||||
it 'should be able to search' do
|
||||
get :index
|
||||
response.should be_success
|
||||
response.should render_template(:index)
|
||||
end
|
||||
end
|
||||
shared_examples_for 'not able search' do
|
||||
it 'should not be able to search' do
|
||||
get :index
|
||||
response.should redirect_to(controller.current_user ? forbidden_path : new_user_session_path)
|
||||
end
|
||||
end
|
||||
|
||||
describe SearchController do
|
||||
before { stub_rsync_methods }
|
||||
|
||||
context 'as guest' do
|
||||
if APP_CONFIG['anonymous_access']
|
||||
it_should_behave_like 'able search'
|
||||
else
|
||||
it_should_behave_like 'not able search'
|
||||
end
|
||||
end
|
||||
|
||||
context 'as user' do
|
||||
before {set_session_for FactoryGirl.create(:user)}
|
||||
|
||||
it_should_behave_like 'able search'
|
||||
end
|
||||
end
|
|
@ -38,10 +38,6 @@ describe UsersController do
|
|||
end
|
||||
|
||||
context 'with mass assignment' do
|
||||
it 'should not be able to update uname' do
|
||||
@simple_user.should_not allow_mass_assignment_of :uname
|
||||
end
|
||||
|
||||
it 'should not be able to update role' do
|
||||
@simple_user.should_not allow_mass_assignment_of :role
|
||||
end
|
||||
|
|
|
@ -1,19 +1,18 @@
|
|||
# -*- encoding : utf-8 -*-
|
||||
FactoryGirl.define do
|
||||
factory :platform do
|
||||
description { FactoryGirl.generate(:string) }
|
||||
name { FactoryGirl.generate(:unixname) }
|
||||
description { FactoryGirl.generate(:string) }
|
||||
platform_type 'main'
|
||||
distrib_type APP_CONFIG['distr_types'].first
|
||||
association :owner, :factory => :user
|
||||
|
||||
factory :platform_with_repos do
|
||||
after_create {|p| FactoryGirl.create_list(:repository, 1, platform: p)}
|
||||
end
|
||||
|
||||
factory :platform_with_repos, :parent => :platform do
|
||||
repositories {|r| [r.association(:repository)]}
|
||||
end
|
||||
|
||||
factory :personal_platform, :parent => :platform do
|
||||
factory :personal_platform do
|
||||
platform_type 'personal'
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -64,10 +64,6 @@ describe CanCan do
|
|||
@ability.should_not be_able_to(:read, hidden_platform)
|
||||
end
|
||||
|
||||
it 'should be able to auto build projects' do
|
||||
@ability.should be_able_to(:auto_build, Project)
|
||||
end
|
||||
|
||||
[:publish_build, :status_build, :pre_build, :post_build, :circle_build, :new_bbdt].each do |action|
|
||||
it "should be able to #{ action } build list" do
|
||||
@ability.should be_able_to(action, BuildList)
|
||||
|
@ -90,7 +86,7 @@ describe CanCan do
|
|||
@ability.should_not be_able_to(:destroy, register_request)
|
||||
end
|
||||
|
||||
it 'should be able to register new user' do
|
||||
pending 'should be able to register new user' do # while self registration is closed
|
||||
@ability.should be_able_to(:create, User)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,9 +3,7 @@ require 'spec_helper'
|
|||
require "cancan/matchers"
|
||||
|
||||
def create_comment user
|
||||
comment = user.comments.create(:commentable_id => @commit.id.hex, :commentable_type => @commit.class.name,
|
||||
:body => 'test', :project_id => @project.id)
|
||||
comment
|
||||
FactoryGirl.create(:comment, :user => user, :commentable => @commit, :project => @project)
|
||||
end
|
||||
|
||||
def set_comments_data_for_commit
|
||||
|
|