summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml17
-rw-r--r--CHANGELOG93
-rw-r--r--CONTRIBUTING.md61
-rw-r--r--Gemfile74
-rw-r--r--Gemfile.lock548
-rw-r--r--PROCESS.md44
-rw-r--r--README.md42
-rw-r--r--VERSION2
-rw-r--r--app/assets/images/favicon.icobin1150 -> 32988 bytes
-rw-r--r--app/assets/images/logo-black.pngbin2837 -> 3018 bytes
-rw-r--r--app/assets/images/logo-white.pngbin4040 -> 8337 bytes
-rw-r--r--app/assets/javascripts/admin.js.coffee17
-rw-r--r--app/assets/javascripts/api.js.coffee15
-rw-r--r--app/assets/javascripts/behaviors/toggler_behavior.coffee4
-rw-r--r--app/assets/javascripts/blob.js.coffee70
-rw-r--r--app/assets/javascripts/commits.js.coffee14
-rw-r--r--app/assets/javascripts/issues.js.coffee19
-rw-r--r--app/assets/javascripts/main.js.coffee6
-rw-r--r--app/assets/javascripts/merge_requests.js.coffee8
-rw-r--r--app/assets/javascripts/namespace_select.js.coffee24
-rw-r--r--app/assets/javascripts/notes.js580
-rw-r--r--app/assets/javascripts/notes.js.coffee432
-rw-r--r--app/assets/javascripts/notes_votes.js.coffee22
-rw-r--r--app/assets/javascripts/pager.js.coffee5
-rw-r--r--app/assets/javascripts/project.js.coffee6
-rw-r--r--app/assets/javascripts/stat_graph_contributors_graph.js.coffee13
-rw-r--r--app/assets/javascripts/users_select.js.coffee12
-rw-r--r--app/assets/stylesheets/application.scss56
-rw-r--r--app/assets/stylesheets/common.scss89
-rw-r--r--app/assets/stylesheets/generic.scss12
-rw-r--r--app/assets/stylesheets/generic/avatar.scss (renamed from app/assets/stylesheets/gitlab_bootstrap/avatar.scss)0
-rw-r--r--app/assets/stylesheets/generic/blocks.scss4
-rw-r--r--app/assets/stylesheets/generic/buttons.scss (renamed from app/assets/stylesheets/gitlab_bootstrap/buttons.scss)92
-rw-r--r--app/assets/stylesheets/generic/common.scss (renamed from app/assets/stylesheets/gitlab_bootstrap/common.scss)37
-rw-r--r--app/assets/stylesheets/generic/files.scss (renamed from app/assets/stylesheets/gitlab_bootstrap/files.scss)5
-rw-r--r--app/assets/stylesheets/generic/forms.scss49
-rw-r--r--app/assets/stylesheets/generic/issue_box.scss46
-rw-r--r--app/assets/stylesheets/generic/lists.scss (renamed from app/assets/stylesheets/gitlab_bootstrap/lists.scss)2
-rw-r--r--app/assets/stylesheets/generic/nav.scss (renamed from app/assets/stylesheets/gitlab_bootstrap/nav.scss)48
-rw-r--r--app/assets/stylesheets/generic/selects.scss103
-rw-r--r--app/assets/stylesheets/generic/typography.scss (renamed from app/assets/stylesheets/gitlab_bootstrap/typography.scss)6
-rw-r--r--app/assets/stylesheets/generic/ui_box.scss (renamed from app/assets/stylesheets/gitlab_bootstrap/blocks.scss)157
-rw-r--r--app/assets/stylesheets/gitlab_bootstrap.scss66
-rw-r--r--app/assets/stylesheets/gitlab_bootstrap/forms.scss51
-rw-r--r--app/assets/stylesheets/gl_bootstrap.scss129
-rw-r--r--app/assets/stylesheets/main/fonts.scss (renamed from app/assets/stylesheets/gitlab_bootstrap/fonts.scss)0
-rw-r--r--app/assets/stylesheets/main/mixins.scss (renamed from app/assets/stylesheets/gitlab_bootstrap/mixins.scss)46
-rw-r--r--app/assets/stylesheets/main/variables.scss (renamed from app/assets/stylesheets/gitlab_bootstrap/variables.scss)3
-rw-r--r--app/assets/stylesheets/sections.scss21
-rw-r--r--app/assets/stylesheets/sections/admin.scss15
-rw-r--r--app/assets/stylesheets/sections/commits.scss61
-rw-r--r--app/assets/stylesheets/sections/dashboard.scss56
-rw-r--r--app/assets/stylesheets/sections/editor.scss2
-rw-r--r--app/assets/stylesheets/sections/events.scss3
-rw-r--r--app/assets/stylesheets/sections/header.scss58
-rw-r--r--app/assets/stylesheets/sections/issues.scss28
-rw-r--r--app/assets/stylesheets/sections/login.scss88
-rw-r--r--app/assets/stylesheets/sections/merge_requests.scss41
-rw-r--r--app/assets/stylesheets/sections/nav.scss1
-rw-r--r--app/assets/stylesheets/sections/notes.scss31
-rw-r--r--app/assets/stylesheets/sections/profile.scss12
-rw-r--r--app/assets/stylesheets/sections/projects.scss157
-rw-r--r--app/assets/stylesheets/sections/tree.scss17
-rw-r--r--app/assets/stylesheets/sections/votes.scss5
-rw-r--r--app/assets/stylesheets/sections/wall.scss2
-rw-r--r--app/assets/stylesheets/sections/wiki.scss2
-rw-r--r--app/assets/stylesheets/selects.scss153
-rw-r--r--app/assets/stylesheets/themes/ui_color.scss6
-rw-r--r--app/assets/stylesheets/themes/ui_mars.scss15
-rw-r--r--app/assets/stylesheets/themes/ui_modern.scss6
-rw-r--r--app/contexts/base_context.rb1
-rw-r--r--app/contexts/files/base_context.rb31
-rw-r--r--app/contexts/files/create_context.rb46
-rw-r--r--app/contexts/files/delete_context.rb40
-rw-r--r--app/contexts/files/update_context.rb39
-rw-r--r--app/contexts/filter_context.rb47
-rw-r--r--app/contexts/issues/bulk_update_context.rb2
-rw-r--r--app/contexts/issues/list_context.rb17
-rw-r--r--app/contexts/projects/create_context.rb9
-rw-r--r--app/contexts/projects/update_context.rb12
-rw-r--r--app/contexts/search_context.rb18
-rw-r--r--app/controllers/admin/broadcast_messages_controller.rb32
-rw-r--r--app/controllers/admin/dashboard_controller.rb1
-rw-r--r--app/controllers/admin/projects_controller.rb28
-rw-r--r--app/controllers/admin/users_controller.rb5
-rw-r--r--app/controllers/application_controller.rb38
-rw-r--r--app/controllers/dashboard_controller.rb21
-rw-r--r--app/controllers/groups_controller.rb12
-rw-r--r--app/controllers/profiles/avatars_controller.rb13
-rw-r--r--app/controllers/profiles/keys_controller.rb2
-rw-r--r--app/controllers/profiles_controller.rb2
-rw-r--r--app/controllers/projects/application_controller.rb8
-rw-r--r--app/controllers/projects/base_tree_controller.rb8
-rw-r--r--app/controllers/projects/blob_controller.rb25
-rw-r--r--app/controllers/projects/commit_controller.rb4
-rw-r--r--app/controllers/projects/commits_controller.rb2
-rw-r--r--app/controllers/projects/deploy_keys_controller.rb2
-rw-r--r--app/controllers/projects/edit_tree_controller.rb43
-rw-r--r--app/controllers/projects/hooks_controller.rb4
-rw-r--r--app/controllers/projects/issues_controller.rb23
-rw-r--r--app/controllers/projects/merge_requests_controller.rb49
-rw-r--r--app/controllers/projects/milestones_controller.rb5
-rw-r--r--app/controllers/projects/new_tree_controller.rb19
-rw-r--r--app/controllers/projects/notes_controller.rb103
-rw-r--r--app/controllers/projects/protected_branches_controller.rb2
-rw-r--r--app/controllers/projects/repositories_controller.rb2
-rw-r--r--app/controllers/projects/snippets_controller.rb4
-rw-r--r--app/controllers/projects/team_members_controller.rb11
-rw-r--r--app/controllers/projects/tree_controller.rb9
-rw-r--r--app/controllers/projects_controller.rb28
-rw-r--r--app/controllers/public/projects_controller.rb5
-rw-r--r--app/controllers/search_controller.rb2
-rw-r--r--app/helpers/application_helper.rb49
-rw-r--r--app/helpers/broadcast_messages_helper.rb9
-rw-r--r--app/helpers/commits_helper.rb8
-rw-r--r--app/helpers/compare_helper.rb4
-rw-r--r--app/helpers/dashboard_helper.rb31
-rw-r--r--app/helpers/events_helper.rb18
-rw-r--r--app/helpers/gitlab_markdown_helper.rb13
-rw-r--r--app/helpers/groups_helper.rb19
-rw-r--r--app/helpers/icons_helper.rb21
-rw-r--r--app/helpers/issues_helper.rb16
-rw-r--r--app/helpers/labels_helper.rb4
-rw-r--r--app/helpers/merge_requests_helper.rb2
-rw-r--r--app/helpers/namespaces_helper.rb13
-rw-r--r--app/helpers/notes_helper.rb29
-rw-r--r--app/helpers/projects_helper.rb67
-rw-r--r--app/helpers/search_helper.rb15
-rw-r--r--app/helpers/tab_helper.rb7
-rw-r--r--app/helpers/visibility_level_helper.rb49
-rw-r--r--app/mailers/emails/groups.rb2
-rw-r--r--app/mailers/emails/issues.rb6
-rw-r--r--app/mailers/emails/merge_requests.rb12
-rw-r--r--app/mailers/emails/notes.rb8
-rw-r--r--app/mailers/emails/projects.rb14
-rw-r--r--app/mailers/notify.rb1
-rw-r--r--app/models/ability.rb45
-rw-r--r--app/models/broadcast_message.rb29
-rw-r--r--app/models/concerns/issuable.rb7
-rw-r--r--app/models/event.rb6
-rw-r--r--app/models/gollum_wiki.rb2
-rw-r--r--app/models/group.rb2
-rw-r--r--app/models/issue.rb21
-rw-r--r--app/models/merge_request.rb50
-rw-r--r--app/models/namespace.rb4
-rw-r--r--app/models/note.rb92
-rw-r--r--app/models/project.rb113
-rw-r--r--app/models/project_hook.rb23
-rw-r--r--app/models/project_services/assembla_service.rb45
-rw-r--r--app/models/project_services/campfire_service.rb (renamed from app/models/campfire_service.rb)0
-rw-r--r--app/models/project_services/emails_on_push_service.rb44
-rw-r--r--app/models/project_services/flowdock_service.rb (renamed from app/models/flowdock_service.rb)2
-rw-r--r--app/models/project_services/gitlab_ci_service.rb (renamed from app/models/gitlab_ci_service.rb)0
-rw-r--r--app/models/project_services/hipchat_service.rb (renamed from app/models/hipchat_service.rb)2
-rw-r--r--app/models/project_services/pivotaltracker_service.rb (renamed from app/models/pivotaltracker_service.rb)0
-rw-r--r--app/models/project_team.rb9
-rw-r--r--app/models/repository.rb11
-rw-r--r--app/models/service_hook.rb17
-rw-r--r--app/models/system_hook.rb17
-rw-r--r--app/models/user.rb80
-rw-r--r--app/models/web_hook.rb17
-rw-r--r--app/observers/issue_observer.rb10
-rw-r--r--app/observers/merge_request_observer.rb25
-rw-r--r--app/observers/project_observer.rb6
-rw-r--r--app/observers/users_group_observer.rb2
-rw-r--r--app/observers/users_project_observer.rb4
-rw-r--r--app/services/git_push_service.rb3
-rw-r--r--app/services/notification_service.rb24
-rw-r--r--app/services/project_transfer_service.rb8
-rw-r--r--app/uploaders/attachment_uploader.rb6
-rw-r--r--app/views/admin/background_jobs/show.html.haml4
-rw-r--r--app/views/admin/broadcast_messages/index.html.haml61
-rw-r--r--app/views/admin/dashboard/index.html.haml74
-rw-r--r--app/views/admin/groups/edit.html.haml20
-rw-r--r--app/views/admin/groups/index.html.haml5
-rw-r--r--app/views/admin/groups/new.html.haml14
-rw-r--r--app/views/admin/groups/show.html.haml10
-rw-r--r--app/views/admin/hooks/index.html.haml43
-rw-r--r--app/views/admin/projects/index.html.haml70
-rw-r--r--app/views/admin/projects/show.html.haml37
-rw-r--r--app/views/admin/users/_form.html.haml58
-rw-r--r--app/views/admin/users/index.html.haml16
-rw-r--r--app/views/admin/users/show.html.haml26
-rw-r--r--app/views/dashboard/_groups.html.haml2
-rw-r--r--app/views/dashboard/_project.html.haml5
-rw-r--r--app/views/dashboard/_projects.html.haml2
-rw-r--r--app/views/dashboard/issues.html.haml8
-rw-r--r--app/views/dashboard/merge_requests.html.haml6
-rw-r--r--app/views/dashboard/projects.html.haml102
-rw-r--r--app/views/dashboard/show.html.haml6
-rw-r--r--app/views/dashboard/show.js.haml2
-rw-r--r--app/views/devise/confirmations/new.html.erb12
-rw-r--r--app/views/devise/confirmations/new.html.haml14
-rw-r--r--app/views/devise/mailer/confirmation_instructions.html.erb8
-rw-r--r--app/views/devise/passwords/edit.html.haml7
-rw-r--r--app/views/devise/passwords/new.html.haml19
-rw-r--r--app/views/devise/registrations/edit.html.erb2
-rw-r--r--app/views/devise/registrations/new.html.haml23
-rw-r--r--app/views/devise/sessions/_new_base.html.haml4
-rw-r--r--app/views/devise/sessions/_new_ldap.html.haml4
-rw-r--r--app/views/devise/sessions/new.html.haml14
-rw-r--r--app/views/events/_commit.html.haml2
-rw-r--r--app/views/events/_event.html.haml4
-rw-r--r--app/views/events/_event_last_push.html.haml5
-rw-r--r--app/views/events/_events.html.haml1
-rw-r--r--app/views/groups/_new_group_member.html.haml32
-rw-r--r--app/views/groups/_projects.html.haml5
-rw-r--r--app/views/groups/edit.html.haml35
-rw-r--r--app/views/groups/issues.html.haml6
-rw-r--r--app/views/groups/merge_requests.html.haml8
-rw-r--r--app/views/groups/new.html.haml18
-rw-r--r--app/views/groups/show.html.haml4
-rw-r--r--app/views/groups/show.js.haml2
-rw-r--r--app/views/help/_api_layout.html.haml4
-rw-r--r--app/views/help/_layout.html.haml44
-rw-r--r--app/views/help/_shortcuts.html.haml58
-rw-r--r--app/views/help/index.html.haml10
-rw-r--r--app/views/help/permissions.html.haml2
-rw-r--r--app/views/help/public_access.html.haml47
-rw-r--r--app/views/help/ssh.html.haml7
-rw-r--r--app/views/help/web_hooks.html.haml113
-rw-r--r--app/views/kaminari/gitlab/_paginator.html.haml4
-rw-r--r--app/views/layouts/_broadcast.html.haml4
-rw-r--r--app/views/layouts/_head.html.haml2
-rw-r--r--app/views/layouts/_head_panel.html.haml60
-rw-r--r--app/views/layouts/_init_auto_complete.html.haml2
-rw-r--r--app/views/layouts/application.html.haml1
-rw-r--r--app/views/layouts/devise.html.haml4
-rw-r--r--app/views/layouts/group.html.haml3
-rw-r--r--app/views/layouts/nav/_admin.html.haml2
-rw-r--r--app/views/layouts/nav/_dashboard.html.haml2
-rw-r--r--app/views/layouts/nav/_project.html.haml2
-rw-r--r--app/views/layouts/profile.html.haml1
-rw-r--r--app/views/layouts/project_settings.html.haml5
-rw-r--r--app/views/layouts/projects.html.haml3
-rw-r--r--app/views/layouts/public.html.haml2
-rw-r--r--app/views/layouts/public_projects.html.haml2
-rw-r--r--app/views/notify/_note_message.html.haml2
-rw-r--r--app/views/notify/new_merge_request_email.html.haml2
-rw-r--r--app/views/notify/new_merge_request_email.text.erb2
-rw-r--r--app/views/notify/reassigned_merge_request_email.html.haml2
-rw-r--r--app/views/notify/reassigned_merge_request_email.text.erb2
-rw-r--r--app/views/notify/repository_push_email.html.haml23
-rw-r--r--app/views/notify/repository_push_email.text.haml20
-rw-r--r--app/views/profiles/accounts/show.html.haml9
-rw-r--r--app/views/profiles/groups/index.html.haml2
-rw-r--r--app/views/profiles/keys/_form.html.haml20
-rw-r--r--app/views/profiles/keys/_key.html.haml6
-rw-r--r--app/views/profiles/keys/new.html.haml2
-rw-r--r--app/views/profiles/keys/show.html.haml6
-rw-r--r--app/views/profiles/notifications/_settings.html.haml12
-rw-r--r--app/views/profiles/notifications/show.html.haml10
-rw-r--r--app/views/profiles/passwords/edit.html.haml26
-rw-r--r--app/views/profiles/passwords/new.html.haml14
-rw-r--r--app/views/profiles/show.html.haml72
-rw-r--r--app/views/projects/_dropdown.html.haml15
-rw-r--r--app/views/projects/_errors.html.haml2
-rw-r--r--app/views/projects/_home_panel.html.haml30
-rw-r--r--app/views/projects/_visibility_level.html.haml27
-rw-r--r--app/views/projects/blob/_actions.html.haml9
-rw-r--r--app/views/projects/blob/_blob.html.haml1
-rw-r--r--app/views/projects/blob/_remove.html.haml22
-rw-r--r--app/views/projects/blob/show.html.haml3
-rw-r--r--app/views/projects/branches/_branch.html.haml6
-rw-r--r--app/views/projects/branches/_filter.html.haml14
-rw-r--r--app/views/projects/branches/index.html.haml6
-rw-r--r--app/views/projects/branches/new.html.haml14
-rw-r--r--app/views/projects/branches/recent.html.haml6
-rw-r--r--app/views/projects/commit/_commit_box.html.haml6
-rw-r--r--app/views/projects/commit/huge_commit.html.haml2
-rw-r--r--app/views/projects/commits/_commit.html.haml8
-rw-r--r--app/views/projects/commits/_commits.html.haml4
-rw-r--r--app/views/projects/commits/_diffs.html.haml15
-rw-r--r--app/views/projects/commits/_head.html.haml4
-rw-r--r--app/views/projects/commits/_image.html.haml4
-rw-r--r--app/views/projects/commits/_inline_commit.html.haml5
-rw-r--r--app/views/projects/commits/_parallel_view.html.haml75
-rw-r--r--app/views/projects/commits/_text_file.html.haml1
-rw-r--r--app/views/projects/commits/show.atom.builder2
-rw-r--r--app/views/projects/commits/show.js.haml3
-rw-r--r--app/views/projects/compare/_form.html.haml36
-rw-r--r--app/views/projects/compare/show.html.haml2
-rw-r--r--app/views/projects/deploy_keys/_deploy_key.html.haml7
-rw-r--r--app/views/projects/deploy_keys/_form.html.haml18
-rw-r--r--app/views/projects/deploy_keys/index.html.haml4
-rw-r--r--app/views/projects/deploy_keys/show.html.haml2
-rw-r--r--app/views/projects/edit.html.haml140
-rw-r--r--app/views/projects/edit_tree/show.html.haml10
-rw-r--r--app/views/projects/empty.html.haml11
-rw-r--r--app/views/projects/fork.html.haml6
-rw-r--r--app/views/projects/graphs/show.js.haml2
-rw-r--r--app/views/projects/hooks/_data_ex.html.erb44
-rw-r--r--app/views/projects/hooks/index.html.haml60
-rw-r--r--app/views/projects/issues/_form.html.haml75
-rw-r--r--app/views/projects/issues/_head.html.haml25
-rw-r--r--app/views/projects/issues/_issue.html.haml2
-rw-r--r--app/views/projects/issues/_issue_context.html.haml25
-rw-r--r--app/views/projects/issues/_issues.html.haml29
-rw-r--r--app/views/projects/issues/index.html.haml21
-rw-r--r--app/views/projects/issues/index.js.haml4
-rw-r--r--app/views/projects/issues/show.html.haml55
-rw-r--r--app/views/projects/issues/update.js.haml9
-rw-r--r--app/views/projects/merge_requests/_form.html.haml58
-rw-r--r--app/views/projects/merge_requests/_merge_request.html.haml4
-rw-r--r--app/views/projects/merge_requests/_show.html.haml8
-rw-r--r--app/views/projects/merge_requests/commits.js.haml4
-rw-r--r--app/views/projects/merge_requests/diffs.js.haml2
-rw-r--r--app/views/projects/merge_requests/index.html.haml4
-rw-r--r--app/views/projects/merge_requests/invalid.html.haml26
-rw-r--r--app/views/projects/merge_requests/show.js.haml2
-rw-r--r--app/views/projects/merge_requests/show/_how_to_merge.html.haml78
-rw-r--r--app/views/projects/merge_requests/show/_mr_accept.html.haml6
-rw-r--r--app/views/projects/merge_requests/show/_mr_box.html.haml42
-rw-r--r--app/views/projects/merge_requests/show/_mr_ci.html.haml8
-rw-r--r--app/views/projects/merge_requests/show/_mr_title.html.haml45
-rw-r--r--app/views/projects/merge_requests/show/_no_accept.html.haml12
-rw-r--r--app/views/projects/milestones/_form.html.haml24
-rw-r--r--app/views/projects/milestones/_milestone.html.haml2
-rw-r--r--app/views/projects/milestones/index.html.haml4
-rw-r--r--app/views/projects/milestones/show.html.haml38
-rw-r--r--app/views/projects/network/_head.html.haml34
-rw-r--r--app/views/projects/new.html.haml60
-rw-r--r--app/views/projects/new_tree/show.html.haml47
-rw-r--r--app/views/projects/notes/_diff_notes_with_reply_parallel.html.haml34
-rw-r--r--app/views/projects/notes/_discussion.html.haml3
-rw-r--r--app/views/projects/notes/_form.html.haml13
-rw-r--r--app/views/projects/notes/_note.html.haml10
-rw-r--r--app/views/projects/notes/_notes.html.haml2
-rw-r--r--app/views/projects/notes/_notes_with_form.html.haml5
-rw-r--r--app/views/projects/notes/create.js.haml18
-rw-r--r--app/views/projects/notes/index.js.haml4
-rw-r--r--app/views/projects/protected_branches/index.html.haml14
-rw-r--r--app/views/projects/refs/logs_tree.js.haml2
-rw-r--r--app/views/projects/repositories/_feed.html.haml3
-rw-r--r--app/views/projects/repositories/stats.html.haml4
-rw-r--r--app/views/projects/services/_form.html.haml23
-rw-r--r--app/views/projects/services/index.html.haml14
-rw-r--r--app/views/projects/show.html.haml60
-rw-r--r--app/views/projects/show.js.haml2
-rw-r--r--app/views/projects/snippets/_form.html.haml16
-rw-r--r--app/views/projects/snippets/_snippet.html.haml3
-rw-r--r--app/views/projects/tags/index.html.haml5
-rw-r--r--app/views/projects/tags/new.html.haml14
-rw-r--r--app/views/projects/team_members/_form.html.haml16
-rw-r--r--app/views/projects/team_members/_team_member.html.haml2
-rw-r--r--app/views/projects/team_members/import.html.haml2
-rw-r--r--app/views/projects/tree/_readme.html.haml8
-rw-r--r--app/views/projects/tree/_tree.html.haml23
-rw-r--r--app/views/projects/walls/show.html.haml4
-rw-r--r--app/views/projects/wikis/_form.html.haml48
-rw-r--r--app/views/projects/wikis/_nav.html.haml2
-rw-r--r--app/views/projects/wikis/_new.html.haml24
-rw-r--r--app/views/projects/wikis/edit.html.haml9
-rw-r--r--app/views/projects/wikis/git_access.html.haml2
-rw-r--r--app/views/projects/wikis/history.html.haml5
-rw-r--r--app/views/projects/wikis/pages.html.haml2
-rw-r--r--app/views/projects/wikis/show.html.haml11
-rw-r--r--app/views/public/projects/index.html.haml50
-rw-r--r--app/views/search/_global_results.html.haml5
-rw-r--r--app/views/search/_project_results.html.haml17
-rw-r--r--app/views/search/_result.html.haml62
-rw-r--r--app/views/search/_results.html.haml13
-rw-r--r--app/views/search/results/_blob.html.haml (renamed from app/views/search/_blob.html.haml)2
-rw-r--r--app/views/search/results/_issue.html.haml9
-rw-r--r--app/views/search/results/_merge_request.html.haml12
-rw-r--r--app/views/search/results/_project.html.haml7
-rw-r--r--app/views/search/show.html.haml24
-rw-r--r--app/views/shared/_clone_panel.html.haml8
-rw-r--r--app/views/shared/_filter.html.haml65
-rw-r--r--app/views/shared/_issues.html.haml2
-rw-r--r--app/views/shared/_merge_requests.html.haml3
-rw-r--r--app/views/shared/_no_ssh.html.haml11
-rw-r--r--app/views/shared/_project_filter.html.haml56
-rw-r--r--app/views/shared/_ref_switcher.html.haml2
-rw-r--r--app/views/snippets/_blob.html.haml19
-rw-r--r--app/views/snippets/_form.html.haml42
-rw-r--r--app/views/snippets/_snippet.html.haml4
-rw-r--r--app/views/snippets/current_user_index.html.haml4
-rw-r--r--app/views/users/show.html.haml4
-rw-r--r--app/views/users_groups/_users_group.html.haml2
-rw-r--r--app/views/votes/_votes_block.html.haml4
-rw-r--r--app/workers/emails_on_push_worker.rb25
-rw-r--r--app/workers/repository_import_worker.rb1
-rw-r--r--config.ru9
-rw-r--r--config/application.rb27
-rw-r--r--config/database.yml.mysql2
-rw-r--r--config/environments/development.rb9
-rw-r--r--config/environments/production.rb5
-rw-r--r--config/environments/test.rb5
-rw-r--r--config/gitlab.yml.example22
-rw-r--r--config/initializers/1_settings.rb27
-rw-r--r--config/initializers/carrierwave.rb2
-rw-r--r--config/initializers/devise.rb7
-rw-r--r--config/initializers/gemoji.rb1
-rw-r--r--config/initializers/rack_attack.rb.example20
-rw-r--r--config/initializers/secret_token.rb1
-rw-r--r--config/initializers/session_store.rb3
-rw-r--r--config/routes.rb39
-rw-r--r--config/unicorn.rb.example6
-rw-r--r--db/fixtures/development/05_users.rb2
-rw-r--r--db/fixtures/development/09_issues.rb2
-rw-r--r--db/fixtures/development/10_merge_requests.rb2
-rw-r--r--db/migrate/20131106151520_remove_default_branch.rb9
-rw-r--r--db/migrate/20131112114325_create_broadcast_messages.rb12
-rw-r--r--db/migrate/20131112220935_add_visibility_level_to_projects.rb13
-rw-r--r--db/migrate/20131129154016_add_archived_to_projects.rb5
-rw-r--r--db/migrate/20131130165425_add_color_and_font_to_broadcast_messages.rb6
-rw-r--r--db/migrate/20131202192556_add_event_fields_for_web_hook.rb7
-rw-r--r--db/migrate/20131214224427_add_hide_no_ssh_key_to_users.rb5
-rw-r--r--db/migrate/20131217102743_add_recipients_to_service.rb5
-rw-r--r--db/schema.rb366
-rw-r--r--doc/api/projects.md40
-rw-r--r--doc/api/repositories.md59
-rw-r--r--doc/development/architecture.md23
-rw-r--r--doc/install/databases.md6
-rw-r--r--doc/install/installation.md69
-rw-r--r--doc/install/requirements.md41
-rw-r--r--doc/legal/corporate_contributor_license_agreement.md25
-rw-r--r--doc/legal/individual_contributor_license_agreement.md25
-rw-r--r--doc/markdown/markdown.md2
-rw-r--r--doc/release/monthly.md (renamed from doc/make_release.md)33
-rw-r--r--doc/release/security.md76
-rw-r--r--doc/update/4.2-to-5.0.md3
-rw-r--r--doc/update/5.0-to-5.1.md3
-rw-r--r--doc/update/5.1-to-5.2.md7
-rw-r--r--doc/update/5.1-to-5.4.md103
-rw-r--r--doc/update/5.1-to-6.0.md9
-rw-r--r--doc/update/5.2-to-5.3.md7
-rw-r--r--doc/update/5.3-to-5.4.md11
-rw-r--r--doc/update/5.4-to-6.0.md9
-rw-r--r--doc/update/6.0-to-6.1.md9
-rw-r--r--doc/update/6.0-to-6.4.md128
-rw-r--r--doc/update/6.1-to-6.2.md29
-rw-r--r--doc/update/6.2-to-6.3.md108
-rw-r--r--doc/update/6.3-to-6.4.md80
-rw-r--r--doc/update/patch_versions.md24
-rw-r--r--doc/update/ruby.md54
-rw-r--r--doc/update/upgrader.md30
-rw-r--r--features/admin/active_tab.feature5
-rw-r--r--features/admin/broadcast_messages.feature20
-rw-r--r--features/dashboard/archived_projects.feature16
-rw-r--r--features/dashboard/issues.feature12
-rw-r--r--features/dashboard/merge_requests.feature14
-rw-r--r--features/profile/profile.feature8
-rw-r--r--features/project/archived_projects.feature39
-rw-r--r--features/project/commits/commits.feature6
-rw-r--r--features/project/edit_issuetracker.feature18
-rw-r--r--features/project/network.feature6
-rw-r--r--features/project/project.feature5
-rw-r--r--features/project/redirects.feature26
-rw-r--r--features/project/service.feature12
-rw-r--r--features/project/source/browse_files.feature4
-rw-r--r--features/project/source/multiselect_blob.feature86
-rw-r--r--features/public/public_projects.feature46
-rw-r--r--features/steps/admin/admin_active_tab.rb4
-rw-r--r--features/steps/admin/admin_broadcast_messages.rb41
-rw-r--r--features/steps/dashboard/dashboard_issues.rb74
-rw-r--r--features/steps/dashboard/dashboard_merge_requests.rb79
-rw-r--r--features/steps/dashboard/dashboard_with_archived_projects.rb22
-rw-r--r--features/steps/profile/profile.rb41
-rw-r--r--features/steps/project/project.rb15
-rw-r--r--features/steps/project/project_archived.rb37
-rw-r--r--features/steps/project/project_browse_commits.rb13
-rw-r--r--features/steps/project/project_browse_files.rb25
-rw-r--r--features/steps/project/project_issue_tracker.rb31
-rw-r--r--features/steps/project/project_markdown_render.rb2
-rw-r--r--features/steps/project/project_merge_requests.rb13
-rw-r--r--features/steps/project/project_multiselect_blob.rb58
-rw-r--r--features/steps/project/project_network_graph.rb8
-rw-r--r--features/steps/project/project_services.rb58
-rw-r--r--features/steps/project/project_wiki.rb2
-rw-r--r--features/steps/project/redirects.rb35
-rw-r--r--features/steps/public/projects_feature.rb58
-rw-r--r--features/steps/shared/authentication.rb4
-rw-r--r--features/steps/shared/paths.rb4
-rw-r--r--features/steps/shared/project.rb7
-rw-r--r--features/steps/snippets/snippets.rb4
-rw-r--r--features/support/env.rb1
-rw-r--r--lib/api/api.rb2
-rw-r--r--lib/api/entities.rb14
-rw-r--r--lib/api/files.rb99
-rw-r--r--lib/api/helpers.rb7
-rw-r--r--lib/api/namespaces.rb23
-rw-r--r--lib/api/project_hooks.rb13
-rw-r--r--lib/api/projects.rb36
-rw-r--r--lib/api/repositories.rb34
-rw-r--r--lib/api/users.rb2
-rw-r--r--lib/backup/database.rb10
-rw-r--r--lib/backup/manager.rb10
-rw-r--r--lib/backup/repository.rb14
-rw-r--r--lib/backup/uploads.rb2
-rw-r--r--lib/gitlab/backend/grack_auth.rb36
-rw-r--r--lib/gitlab/backend/grack_helpers.rb2
-rw-r--r--lib/gitlab/backend/shell.rb39
-rw-r--r--lib/gitlab/ldap/user.rb8
-rw-r--r--lib/gitlab/oauth/user.rb2
-rw-r--r--lib/gitlab/regex.rb29
-rw-r--r--lib/gitlab/satellite/files/delete_file_action.rb50
-rw-r--r--lib/gitlab/satellite/files/edit_file_action.rb (renamed from lib/gitlab/satellite/edit_file_action.rb)36
-rw-r--r--lib/gitlab/satellite/files/file_action.rb17
-rw-r--r--lib/gitlab/satellite/files/new_file_action.rb55
-rw-r--r--lib/gitlab/satellite/merge_action.rb2
-rw-r--r--lib/gitlab/satellite/satellite.rb2
-rw-r--r--lib/gitlab/upgrader.rb96
-rw-r--r--lib/gitlab/visibility_level.rb42
-rw-r--r--lib/redcarpet/render/gitlab_html.rb2
-rwxr-xr-xlib/support/deploy/deploy.sh9
-rwxr-xr-xlib/support/init.d/gitlab22
-rwxr-xr-xlib/support/init.d/gitlab.default.example14
-rw-r--r--lib/support/nginx/gitlab8
-rw-r--r--lib/tasks/gitlab/check.rake48
-rw-r--r--lib/tasks/gitlab/task_helpers.rake10
-rw-r--r--public/favicon.icobin1150 -> 32988 bytes
-rw-r--r--script/upgrade.rb3
-rw-r--r--spec/contexts/filter_context_spec.rb58
-rw-r--r--spec/contexts/fork_context_spec.rb2
-rw-r--r--spec/contexts/issues/list_context_spec.rb73
-rw-r--r--spec/contexts/projects_create_context_spec.rb79
-rw-r--r--spec/contexts/projects_update_context_spec.rb111
-rw-r--r--spec/contexts/search_context_spec.rb43
-rw-r--r--spec/controllers/application_controller_spec.rb8
-rw-r--r--spec/factories.rb3
-rw-r--r--spec/factories/broadcast_messages.rb27
-rw-r--r--spec/features/issues_spec.rb165
-rw-r--r--spec/features/notes_on_merge_requests_spec.rb17
-rw-r--r--spec/features/security/project/internal_access_spec.rb251
-rw-r--r--spec/features/security/project/private_access_spec.rb6
-rw-r--r--spec/features/security/project/public_access_spec.rb2
-rw-r--r--spec/helpers/application_helper_spec.rb28
-rw-r--r--spec/helpers/broadcast_messages_helper_spec.rb21
-rw-r--r--spec/helpers/gitlab_markdown_helper_spec.rb11
-rw-r--r--spec/helpers/notifications_helper_spec.rb2
-rw-r--r--spec/helpers/projects_helper_spec.rb12
-rw-r--r--spec/helpers/search_helper_spec.rb6
-rw-r--r--spec/helpers/tab_helper_spec.rb4
-rw-r--r--spec/javascripts/stat_graph_contributors_graph_spec.js27
-rw-r--r--spec/javascripts/stat_graph_contributors_util_spec.js65
-rw-r--r--spec/javascripts/support/jasmine_helper.rb14
-rw-r--r--spec/lib/auth_spec.rb10
-rw-r--r--spec/lib/extracts_path_spec.rb2
-rw-r--r--spec/lib/gitlab/ldap/ldap_user_auth_spec.rb4
-rw-r--r--spec/lib/gitlab/upgrader_spec.rb24
-rw-r--r--spec/lib/oauth_spec.rb8
-rw-r--r--spec/mailers/notify_spec.rb70
-rw-r--r--spec/models/assembla_service_spec.rb50
-rw-r--r--spec/models/broadcast_message_spec.rb39
-rw-r--r--spec/models/concerns/issuable_spec.rb2
-rw-r--r--spec/models/event_spec.rb4
-rw-r--r--spec/models/flowdock_service_spec.rb2
-rw-r--r--spec/models/forked_project_link_spec.rb2
-rw-r--r--spec/models/merge_request_spec.rb12
-rw-r--r--spec/models/note_spec.rb10
-rw-r--r--spec/models/project_spec.rb6
-rw-r--r--spec/models/service_hook_spec.rb17
-rw-r--r--spec/models/system_hook_spec.rb17
-rw-r--r--spec/models/user_spec.rb25
-rw-r--r--spec/models/web_hook_spec.rb17
-rw-r--r--spec/observers/issue_observer_spec.rb2
-rw-r--r--spec/observers/merge_request_observer_spec.rb4
-rw-r--r--spec/observers/note_observer_spec.rb2
-rw-r--r--spec/observers/user_observer_spec.rb2
-rw-r--r--spec/observers/users_group_observer_spec.rb7
-rw-r--r--spec/observers/users_project_observer_spec.rb41
-rw-r--r--spec/requests/api/files_spec.rb115
-rw-r--r--spec/requests/api/groups_spec.rb2
-rw-r--r--spec/requests/api/internal_spec.rb27
-rw-r--r--spec/requests/api/namespaces_spec.rb31
-rw-r--r--spec/requests/api/project_hooks_spec.rb132
-rw-r--r--spec/requests/api/projects_spec.rb222
-rw-r--r--spec/requests/api/repositories_spec.rb30
-rw-r--r--spec/requests/api/session_spec.rb2
-rw-r--r--spec/routing/project_routing_spec.rb8
-rw-r--r--spec/routing/routing_spec.rb7
-rw-r--r--spec/services/git_push_service_spec.rb33
-rw-r--r--spec/support/login_helpers.rb2
-rw-r--r--spec/support/mentionable_shared_examples.rb4
-rw-r--r--spec/support/test_env.rb13
-rw-r--r--vendor/assets/javascripts/ace-src-noconflict/mode-diff.js2
578 files changed, 10385 insertions, 4529 deletions
diff --git a/.travis.yml b/.travis.yml
index f0fc2fb8829..75b4c5c7030 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,9 +1,14 @@
language: ruby
env:
- - DB=mysql TRAVIS=true
+ global:
+ - DB=mysql
+ - TRAVIS=true
+ matrix:
+ - TASK=spinach
+ - TASK=spec
+ - TASK=jasmine:ci
before_install:
- sudo apt-get install libicu-dev -y
- - gem install charlock_holmes -v="0.6.9"
branches:
only:
- 'master'
@@ -11,8 +16,12 @@ rvm:
- 2.0.0
services:
- mysql
- - postgresql
+ - redis-server
before_script:
- "cp config/database.yml.$DB config/database.yml"
- "cp config/gitlab.yml.example config/gitlab.yml"
-script: "bundle exec rake gitlab:test --trace"
+ - "bundle exec rake db:setup"
+ - "bundle exec rake db:seed_fu"
+script: "bundle exec rake $TASK --trace"
+notifications:
+ email: false
diff --git a/CHANGELOG b/CHANGELOG
index c60bbc3c88c..25f10c15f5f 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,9 +1,90 @@
+v 6.5.0
+ - Dropdown menus on issue#show page for assignee and milestone (Jason Blanchard)
+ - Add color custimization and previewing to broadcast messages
+ - Fixed notes anchors
+ - Load new comments in issues dynamically
+ - Added sort options to Public page
+ - Added new filters(assigned/authored/all) to Dashboard#issues, Dashboard#merge_request pages
+ - Add project visibility icons to dashboard
+ - Enable secure cookies if https used
+ - Protect users/confirmation with rack_attack
+ - Default HTTP headers to protect against MIME-sniffing, force https if enabled
+
+v6.4.3
+ - Don't use unicorn worker killer if PhusionPassenger is defined
+
+v6.4.2
+ - Fixed wrong behaviour of script/upgrade.rb
+
+v6.4.1
+ - Fixed bug with repository rename
+ - Fixed bug with project transfer
+
+v 6.4.0
+ - Added sorting to project issues page (Jason Blanchard)
+ - Assembla integration (Carlos Paramio)
+ - Fixed another 500 error with submodules
+ - UI: More compact issues page
+ - Minimal password length increased to 8 symbols
+ - Side-by-side diff view (Steven Thonus)
+ - Internal projects (Jason Hollingsworth)
+ - Allow removal of avatar (Drew Blessing)
+ - Project web hooks now support issues and merge request events
+ - Visiting project page while not logged in will redirect to sign-in instead of 404 (Jason Hollingsworth)
+ - Expire event cache on avatar creation/removal (Drew Blessing)
+ - Archiving old projects (Steven Thonus)
+ - Rails 4
+ - Add time ago tooltips to show actual date/time
+ - UI: Fixed UI for admin system hooks
+ - Ruby script for easier GitLab upgrade
+ - Do not remove Merge requests if fork project was removed
+ - Improve sign-in/signup UX
+ - Add resend confirmation link to sign-in page
+ - Set noreply@HOSTNAME for reply_to field in all emails
+ - Show GitLab API version on Admin#dashboard
+ - API Cross-origin resource sharing
+ - Show READMe link at project home page
+ - Show repo size for projects in Admin area
+
v 6.3.0
- API for adding gitlab-ci service
- Init script now waits for pids to appear after (re)starting before reporting status (Rovanion Luckey)
- Restyle project home page
- Grammar fixes
- Show branches list (which branches contains commit) on commit page (Andrew Kumanyaev)
+ - Security improvements
+ - Added support for GitLab CI 4.0
+ - Fixed issue with 500 error when group did not exist
+ - Ability to leave project
+ - You can create file in repo using UI
+ - You can remove file from repo using UI
+ - API: dropped default_branch attribute from project during creation
+ - Project default_branch is not stored in db any more. It takes from repo now.
+ - Admin broadcast messages
+ - UI improvements
+ - Dont show last push widget if user removed this branch
+ - Fix 500 error for repos with newline in file name
+ - Extended html titles
+ - API: create/update/delete repo files
+ - Admin can transfer project to any namespace
+ - API: projects/all for admin users
+ - Fix recent branches order
+
+v 6.2.4
+ - Security: Cast API private_token to string (CVE-2013-4580)
+ - Security: Require gitlab-shell 1.7.8 (CVE-2013-4581, CVE-2013-4582, CVE-2013-4583)
+ - Fix for Git SSH access for LDAP users
+
+v 6.2.3
+ - Security: More protection against CVE-2013-4489
+ - Security: Require gitlab-shell 1.7.4 (CVE-2013-4490, CVE-2013-4546)
+ - Fix sidekiq rake tasks
+
+v 6.2.2
+ - Security: Update gitlab_git (CVE-2013-4489)
+
+v 6.2.1
+ - Security: Fix issue with generated passwords for new users
v 6.2.0
- Public project pages are now visible to everyone (files, issues, wik, etc.)
@@ -25,7 +106,7 @@ v 6.2.0
- Avatar upload on profile page with a maximum of 100KB (Steven Thonus)
- Store the sessions in Redis instead of the cookie store
- Fixed relative links in markdown
- - User must confirm his email if signup enabled
+ - User must confirm their email if signup enabled
- User must confirm changed email
v 6.1.0
@@ -47,7 +128,7 @@ v 6.1.0
- Add links to create branch/tag from project home page
- Add public-project? checkbox to new-project view
- Improved compare page. Added link to proceed into Merge Request
- - Send email to user when he was added to group
+ - Send an email to a user when they are added to group
- New landing page when you have 0 projects
v 6.0.0
@@ -90,6 +171,14 @@ v 6.0.0
- Improved MR comments logic
- Render readme file for projects in public area
+v 5.4.2
+ - Security: Cast API private_token to string (CVE-2013-4580)
+ - Security: Require gitlab-shell 1.7.8 (CVE-2013-4581, CVE-2013-4582, CVE-2013-4583)
+
+v 5.4.1
+ - Security: Fixes for CVE-2013-4489
+ - Security: Require gitlab-shell 1.7.4 (CVE-2013-4490, CVE-2013-4546)
+
v 5.4.0
- Ability to edit own comments
- Documentation improvements
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d1fdd93850a..50cea84e0ff 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,38 +1,41 @@
# Contribute to GitLab
-This guide details how to use issues and pull requests to improve GitLab.
-
-- [Closing policy for issues and pull requests](#closing-policy-for-issues-and-pull-requests)
-- [Issue tracker](#issue-tracker)
-- [Pull requests](#pull-requests)
-- [Security vulnerabilities](#security-vulnerabilities)
+This guide details how contribute to GitLab.
If you want to know how the GitLab team handles contributions have a look at [the GitLab contributing process](PROCESS.md).
-## Closing policy for issues and pull requests
+## Contributor license agreement
+
+By submitting code as an individual you agree to the [individual contributor license agreement](doc/legal/individual_contributor_license_agreement.md). By submitting code as an entity you agree to the [corporate contributor license agreement](doc/legal/corporate_contributor_license_agreement.md).
+
+## Security vulnerability disclosure
+
+Please report suspected security vulnerabilities in private to support@gitlab.com, also see the [disclosure section on the GitLab.com website](http://www.gitlab.com/disclosure/). Please do NOT create publicly viewable issues for suspected security vulnerabilities.
-GitLab is a popular open source project and the capacity to deal with issues and pull requests is limited. Out of respect for our volunteers, issues and pull requests not in line with the guidelines listed in this document may be closed without notice.
+## Closing policy for issues and merge requests
+
+GitLab is a popular open source project and the capacity to deal with issues and merge requests is limited. Out of respect for our volunteers, issues and merge requests not in line with the guidelines listed in this document may be closed without notice.
Please treat our volunteers with courtesy and respect, it will go a long way towards getting your issue resolved.
-Issues and pull requests should be in English and contain appropriate language for audiences of all ages.
+Issues and merge requests should be in English and contain appropriate language for audiences of all ages.
## Issue tracker
-To get support for your particular problem please use the channels as detailed in [the getting help section of the readme](https://github.com/gitlabhq/gitlabhq#getting-help). Professional [support subscriptions](http://www.gitlab.com/subscription/) and [consulting services](http://www.gitlab.com/consultancy/) are available from [GitLab.com](http://www.gitlab.com/).
+To get support for your particular problem please use the channels as detailed in the getting help section of [the readme](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/README.md). Professional [support subscriptions](http://www.gitlab.com/subscription/) and [consulting services](http://www.gitlab.com/consultancy/) are available from [GitLab.com](http://www.gitlab.com/).
-The [issue tracker](https://github.com/gitlabhq/gitlabhq/issues) is only for obvious bugs or misbehavior in the latest [stable or development release of GitLab](MAINTENANCE.md). When submitting an issue please conform to the issue submission guidelines listed below. Not all issues will be addressed and your issue is more likely to be addressed if you submit a pull request which partially or fully addresses the issue.
+The [issue tracker](https://gitlab.com/gitlab-org/gitlab-ce/issues) is only for obvious bugs or misbehavior in the latest [stable or development release of GitLab](MAINTENANCE.md). When submitting an issue please conform to the issue submission guidelines listed below. Not all issues will be addressed and your issue is more likely to be addressed if you submit a merge request which partially or fully addresses the issue.
Do not use the issue tracker for feature requests. We have a specific [feedback and suggestions forum](http://feedback.gitlab.com) for this purpose.
-Please send a pull request with a tested solution or a pull request with a failing test instead of opening an issue if you can. If you're unsure where to post, post to the [mailing list](https://groups.google.com/forum/#!forum/gitlabhq) or [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) first. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there.
+Please send a merge request with a tested solution or a merge request with a failing test instead of opening an issue if you can. If you're unsure where to post, post to the [mailing list](https://groups.google.com/forum/#!forum/gitlabhq) or [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) first. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there.
### Issue tracker guidelines
-**[Search](https://github.com/gitlabhq/gitlabhq/search?q=&ref=cmdform&type=Issues)** for similar entries before submitting your own, there's a good chance somebody else had the same issue. Show your support with `:+1:` and/or join the discussion. Please submit issues in the following format (as the first post):
+**[Search the issues](https://gitlab.com/gitlab-org/gitlab-ce/issues)** for similar entries before submitting your own, there's a good chance somebody else had the same issue. Show your support with `:+1:` and/or join the discussion. Please submit issues in the following format (as the first post):
1. **Summary:** Summarize your issue in one sentence (what goes wrong, what did you expect to happen)
-2. **Steps to reproduce:** How can we reproduce the issue, preferably on the [GitLab Vagrant virtual machine](https://github.com/gitlabhq/gitlab-vagrant-vm) (start with: `vagrant destroy && vagrant up && vagrant ssh`)
+2. **Steps to reproduce:** How can we reproduce the issue, preferably on the [GitLab development virtual machine with vagrant](https://gitlab.com/gitlab-org/cookbook-gitlab/blob/master/doc/development.md) (start your issue with: `vagrant destroy && vagrant up && vagrant ssh`)
3. **Expected behavior:** Describe your issue in detail
4. **Observed behavior**
5. **Relevant logs and/or screenshots:** Please use code blocks (\`\`\`) to format console output, logs, and code as it's very hard to read otherwise.
@@ -43,37 +46,37 @@ Please send a pull request with a tested solution or a pull request with a faili
* Describe your setup (use relevant parts from `sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`)
7. **Possible fixes**: If you can, link to the line of code that might be responsible for the problem
-## Pull requests
+## Merge requests
-We welcome pull requests with fixes and improvements to GitLab code, tests, and/or documentation. The features we would really like a pull request for are listed with the [status 'accepting merge/pull requests' on our feedback forum](http://feedback.gitlab.com/forums/176466-general/status/796455) but other improvements are also welcome.
+We welcome merge requests with fixes and improvements to GitLab code, tests, and/or documentation. The features we would really like a merge request for are listed with the [status 'accepting merge/merge requests' on our feedback forum](http://feedback.gitlab.com/forums/176466-general/status/796455) but other improvements are also welcome.
-### Pull request guidelines
+### Merge request guidelines
-If you can, please submit a pull request with the fix or improvements including tests. If you don't know how to fix the issue but can write a test that exposes the issue we will accept that as well. In general bug fixes that include a regression test are merged quickly while new features without proper tests are least likely to receive timely feedback. The workflow to make a pull request is as follows:
+If you can, please submit a merge request with the fix or improvements including tests. If you don't know how to fix the issue but can write a test that exposes the issue we will accept that as well. In general bug fixes that include a regression test are merged quickly while new features without proper tests are least likely to receive timely feedback. The workflow to make a merge request is as follows:
-1. Fork the project on GitHub
+1. Fork the project on GitLab Cloud
1. Create a feature branch
1. Write [tests](README.md#run-the-tests) and code
1. Add your changes to the [CHANGELOG](CHANGELOG)
1. If you have multiple commits please combine them into one commit by [squashing them](http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)
1. Push the commit to your fork
-1. Submit a pull request
-2. [Search for issues](https://github.com/gitlabhq/gitlabhq/search?q=&ref=cmdform&type=Issues) related to your pull request and mention them in the pull request description
+1. Submit a merge request (MR)
+1. The MR title should describes the change you want to make
+1. The MR description should give a motive for your change and the method you used to achieve it
+* If the MR changes the UI it should include before and after screenshots
+1. [Search for issues](https://gitlab.com/gitlab-org/gitlab-ce/issues) related to your merge request and mention them in the merge request description
+
+Please keep the change in a single MR as small as possible. If you want to contribute a large feature think very hard what the minimum viable change is. Can you split functionality? Can you only submit the backend/API code? Can you start with a very simple UI? The smaller a MR is the more likely it is it will be merged, after that you can send more MR's to enhance it.
-We will accept pull requests if:
+We will accept merge requests if:
* The code has proper tests and all tests pass (or it is a test exposing a failure in existing code)
* It can be merged without problems (if not please use: `git rebase master`)
* It does not break any existing functionality
* It's quality code that conforms to the [Ruby](https://github.com/bbatsov/ruby-style-guide) and [Rails](https://github.com/bbatsov/rails-style-guide) style guides and best practices
-* The description includes a motive for your change and the method you used to achieve it
-* It is not a catch all pull request but rather fixes a specific issue or implements a specific feature
+* It is not a catch all merge request but rather fixes a specific issue or implements a specific feature
* It keeps the GitLab code base clean and well structured
* We think other users will benefit from the same functionality
-* If it makes changes to the UI the pull request should include screenshots
* It is a single commit (please use `git rebase -i` to squash commits)
-For examples of feedback on pull requests please look at already [closed pull requests](https://github.com/gitlabhq/gitlabhq/pulls?direction=desc&page=1&sort=created&state=closed).
-
-## Security vulnerabilities
-Please report security vulnerabilities in private to support@gitlab.com; also see http://www.gitlab.com/disclosure/. Do NOT create GitHub issues for security vulnerabilities.
+For examples of feedback on merge requests please look at already [closed merge requests](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests?assignee_id=&label_name=&milestone_id=&scope=&sort=&state=closed).
diff --git a/Gemfile b/Gemfile
index 3dbf890e61e..0e5261b27e1 100644
--- a/Gemfile
+++ b/Gemfile
@@ -8,15 +8,21 @@ def linux_only(require_as)
RUBY_PLATFORM.include?('linux') && require_as
end
-gem "rails", "3.2.15"
+gem "rails", "~> 4.0.0"
+
+gem "protected_attributes"
+gem 'rails-observers'
+gem 'actionpack-page_caching'
+gem 'actionpack-action_caching'
+gem 'activerecord-deprecated_finders'
# Supported DBs
gem "mysql2", group: :mysql
gem "pg", group: :postgres
# Auth
-gem "devise", '~> 2.2'
-gem "devise-async"
+gem "devise", '3.0.4'
+gem "devise-async", '0.8.0'
gem 'omniauth', "~> 1.1.3"
gem 'omniauth-google-oauth2'
gem 'omniauth-twitter'
@@ -24,26 +30,27 @@ gem 'omniauth-github'
# Extracting information from a git repository
# Provide access to Gitlab::Git library
-gem "gitlab_git", "~> 3.0.0.rc1"
+gem "gitlab_git", "~> 4.0.0"
# Ruby/Rack Git Smart-HTTP Server Handler
-gem 'gitlab-grack', '~> 1.0.1', require: 'grack'
+gem 'gitlab-grack', '~> 2.0.0.pre', require: 'grack'
# LDAP Auth
gem 'gitlab_omniauth-ldap', '1.0.3', require: "omniauth-ldap"
# Syntax highlighter
-gem "gitlab-pygments.rb", '~> 0.3.2', require: 'pygments.rb'
+gem "gitlab-pygments.rb", '~> 0.5.4', require: 'pygments.rb'
# Git Wiki
-gem "gitlab-gollum-lib", "~> 1.0.1", require: 'gollum-lib'
+gem "gitlab-gollum-lib", "~> 1.0.2", require: 'gollum-lib'
# Language detection
-gem "github-linguist", require: "linguist"
+gem "gitlab-linguist", "~> 2.9.6", require: "linguist"
# API
-gem "grape", "~> 0.4.1"
+gem "grape", "~> 0.6.1"
gem "grape-entity", "~> 0.3.0"
+gem 'rack-cors', require: 'rack/cors'
# Format dates and times
# based on human-friendly examples
@@ -72,13 +79,16 @@ gem "seed-fu"
# Markdown to HTML
gem "redcarpet", "~> 2.2.2"
-gem "github-markup", "~> 0.7.4", require: 'github/markup'
+gem "github-markup", "~> 0.7.4", require: 'github/markup', git: 'https://github.com/gitlabhq/markup.git', ref: '61ade389c1e1c159359338f570d18464a44ddbc4'
# Asciidoc to HTML
gem "asciidoctor"
# Application server
-gem "unicorn", '~> 4.6.3', group: :unicorn
+group :unicorn do
+ gem "unicorn", '~> 4.6.3'
+ gem 'unicorn-worker-killer'
+end
# State machine
gem "state_machine"
@@ -127,26 +137,24 @@ gem "sanitize"
# Protect against bruteforcing
gem "rack-attack"
-group :assets do
- gem "sass-rails"
- gem "coffee-rails"
- gem "uglifier"
- gem "therubyracer"
- gem 'turbolinks'
- gem 'jquery-turbolinks'
-
- gem 'chosen-rails', "1.0.0"
- gem 'select2-rails'
- gem 'jquery-atwho-rails', "0.3.0"
- gem "jquery-rails", "2.1.3"
- gem "jquery-ui-rails", "2.0.2"
- gem "modernizr", "2.6.2"
- gem "raphael-rails", "~> 2.1.2"
- gem 'bootstrap-sass'
- gem "font-awesome-rails"
- gem "gemoji", "~> 1.2.1", require: 'emoji/railtie'
- gem "gon"
-end
+gem "sass-rails"
+gem "coffee-rails"
+gem "uglifier"
+gem "therubyracer"
+gem 'turbolinks'
+gem 'jquery-turbolinks'
+
+gem 'chosen-rails', "1.0.1"
+gem 'select2-rails'
+gem 'jquery-atwho-rails', "~> 0.3.3"
+gem "jquery-rails", "2.1.3"
+gem "jquery-ui-rails", "2.0.2"
+gem "modernizr", "2.6.2"
+gem "raphael-rails", "~> 2.1.2"
+gem 'bootstrap-sass', '~> 3.0'
+gem "font-awesome-rails", '~> 3.2'
+gem "gemoji", "~> 1.3.0"
+gem "gon", '~> 5.0.0'
group :development do
gem "annotate", "~> 2.6.0.beta2"
@@ -169,7 +177,7 @@ end
group :development, :test do
gem 'coveralls', require: false
- gem 'rails-dev-tweaks'
+ # gem 'rails-dev-tweaks'
gem 'spinach-rails'
gem "rspec-rails"
gem "capybara"
@@ -198,7 +206,7 @@ group :development, :test do
gem 'poltergeist', '~> 1.4.1'
gem 'spork', '~> 1.0rc'
- gem 'jasmine'
+ gem 'jasmine', '2.0.0.rc5'
end
group :test do
diff --git a/Gemfile.lock b/Gemfile.lock
index 8c12fadf3d1..75083623894 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,137 +1,150 @@
+GIT
+ remote: https://github.com/gitlabhq/markup.git
+ revision: 61ade389c1e1c159359338f570d18464a44ddbc4
+ ref: 61ade389c1e1c159359338f570d18464a44ddbc4
+ specs:
+ github-markup (0.7.6)
+
GEM
remote: https://rubygems.org/
specs:
- actionmailer (3.2.15)
- actionpack (= 3.2.15)
+ actionmailer (4.0.2)
+ actionpack (= 4.0.2)
mail (~> 2.5.4)
- actionpack (3.2.15)
- activemodel (= 3.2.15)
- activesupport (= 3.2.15)
- builder (~> 3.0.0)
+ actionpack (4.0.2)
+ activesupport (= 4.0.2)
+ builder (~> 3.1.0)
erubis (~> 2.7.0)
- journey (~> 1.0.4)
- rack (~> 1.4.5)
- rack-cache (~> 1.2)
- rack-test (~> 0.6.1)
- sprockets (~> 2.2.1)
- activemodel (3.2.15)
- activesupport (= 3.2.15)
- builder (~> 3.0.0)
- activerecord (3.2.15)
- activemodel (= 3.2.15)
- activesupport (= 3.2.15)
- arel (~> 3.0.2)
- tzinfo (~> 0.3.29)
- activeresource (3.2.15)
- activemodel (= 3.2.15)
- activesupport (= 3.2.15)
- activesupport (3.2.15)
+ rack (~> 1.5.2)
+ rack-test (~> 0.6.2)
+ actionpack-action_caching (1.1.0)
+ actionpack (>= 4.0.0, < 5.0)
+ actionpack-page_caching (1.0.2)
+ actionpack (>= 4.0.0, < 5)
+ activemodel (4.0.2)
+ activesupport (= 4.0.2)
+ builder (~> 3.1.0)
+ activerecord (4.0.2)
+ activemodel (= 4.0.2)
+ activerecord-deprecated_finders (~> 1.0.2)
+ activesupport (= 4.0.2)
+ arel (~> 4.0.0)
+ activerecord-deprecated_finders (1.0.3)
+ activesupport (4.0.2)
i18n (~> 0.6, >= 0.6.4)
- multi_json (~> 1.0)
+ minitest (~> 4.2)
+ multi_json (~> 1.3)
+ thread_safe (~> 0.1)
+ tzinfo (~> 0.3.37)
acts-as-taggable-on (2.4.1)
rails (>= 3, < 5)
- addressable (2.3.4)
- annotate (2.6.0.beta2)
+ addressable (2.3.5)
+ annotate (2.6.0)
activerecord (>= 2.3.0)
rake (>= 0.8.7)
- arel (3.0.2)
- asciidoctor (0.1.3)
+ arel (4.0.1)
+ asciidoctor (0.1.4)
+ atomic (1.1.14)
awesome_print (1.2.0)
- backports (3.3.2)
- bcrypt-ruby (3.1.1)
+ axiom-types (0.0.5)
+ descendants_tracker (~> 0.0.1)
+ ice_nine (~> 0.9)
+ bcrypt-ruby (3.1.2)
better_errors (1.0.1)
coderay (>= 1.0.0)
erubis (>= 2.6.6)
binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1)
- bootstrap-sass (2.3.2.2)
+ bootstrap-sass (3.0.3.0)
sass (~> 3.2)
- builder (3.0.4)
+ builder (3.1.4)
capybara (2.1.0)
mime-types (>= 1.16)
nokogiri (>= 1.3.3)
rack (>= 1.0.0)
rack-test (>= 0.5.4)
xpath (~> 2.0)
- carrierwave (0.8.0)
+ carrierwave (0.9.0)
activemodel (>= 3.2.0)
activesupport (>= 3.2.0)
- celluloid (0.14.1)
- timers (>= 1.0.0)
+ json (>= 1.7)
+ celluloid (0.15.2)
+ timers (~> 1.1.0)
charlock_holmes (0.6.9.4)
- childprocess (0.3.9)
- ffi (~> 1.0, >= 1.0.11)
- chosen-rails (1.0.0)
+ chosen-rails (1.0.1)
coffee-rails (>= 3.2)
compass-rails (>= 1.0)
railties (>= 3.0)
sass-rails (>= 3.2)
- chunky_png (1.2.8)
- cliver (0.2.1)
+ chunky_png (1.2.9)
+ cliver (0.2.2)
code_analyzer (0.4.3)
sexp_processor
- coderay (1.0.9)
- coffee-rails (3.2.2)
+ coderay (1.1.0)
+ coercible (1.0.0)
+ descendants_tracker (~> 0.0.1)
+ coffee-rails (4.0.1)
coffee-script (>= 2.2.0)
- railties (~> 3.2.0)
+ railties (>= 4.0.0, < 5.0)
coffee-script (2.2.0)
coffee-script-source
execjs
- coffee-script-source (1.6.2)
+ coffee-script-source (1.6.3)
colored (1.2)
colorize (0.5.8)
compass (0.12.2)
chunky_png (~> 1.2)
fssm (>= 0.2.7)
sass (~> 3.1)
- compass-rails (1.0.3)
- compass (>= 0.12.2, < 0.14)
- connection_pool (1.1.0)
+ compass-rails (1.1.1)
+ compass (>= 0.12.2)
+ connection_pool (1.2.0)
coveralls (0.7.0)
multi_json (~> 1.3)
rest-client
simplecov (>= 0.7)
term-ansicolor
thor
- crack (0.4.0)
+ crack (0.4.1)
safe_yaml (~> 0.9.0)
d3_rails (3.1.10)
railties (>= 3.1.0)
daemons (1.1.9)
- database_cleaner (1.1.1)
+ database_cleaner (1.2.0)
debug_inspector (0.0.2)
- descendants_tracker (0.0.1)
- devise (2.2.5)
+ descendants_tracker (0.0.3)
+ devise (3.0.4)
bcrypt-ruby (~> 3.0)
orm_adapter (~> 0.1)
- railties (~> 3.1)
- warden (~> 1.2.1)
+ railties (>= 3.2.6, < 5)
+ warden (~> 1.2.3)
devise-async (0.8.0)
devise (>= 2.2, < 3.2)
- diff-lcs (1.2.4)
- dotenv (0.8.0)
- email_spec (1.4.0)
+ diff-lcs (1.2.5)
+ docile (1.1.1)
+ dotenv (0.9.0)
+ email_spec (1.5.0)
launchy (~> 2.1)
mail (~> 2.2)
- enumerize (0.6.1)
+ enumerize (0.7.0)
activesupport (>= 3.2)
+ equalizer (0.0.8)
erubis (2.7.0)
escape_utils (0.2.4)
eventmachine (1.0.3)
excon (0.13.4)
- execjs (1.4.0)
- multi_json (~> 1.0)
- factory_girl (4.2.0)
+ execjs (2.0.2)
+ factory_girl (4.3.0)
activesupport (>= 3.0.0)
- factory_girl_rails (4.2.1)
- factory_girl (~> 4.2.0)
+ factory_girl_rails (4.3.0)
+ factory_girl (~> 4.3.0)
railties (>= 3.0.0)
- faraday (0.8.7)
- multipart-post (~> 1.1)
+ faraday (0.8.8)
+ multipart-post (~> 1.2.0)
faraday_middleware (0.9.0)
faraday (>= 0.7.4, < 0.9)
- ffaker (1.18.0)
- ffi (1.9.0)
+ ffaker (1.22.1)
+ ffi (1.9.3)
fog (1.3.1)
builder
excon (~> 0.13.0)
@@ -149,50 +162,51 @@ GEM
thor (>= 0.13.6)
formatador (0.2.4)
fssm (0.2.10)
- gemoji (1.2.1)
- gherkin-ruby (0.3.0)
- github-linguist (2.3.4)
- charlock_holmes (~> 0.6.6)
- escape_utils (~> 0.2.3)
- mime-types (~> 1.19)
- pygments.rb (>= 0.2.13)
- github-markdown (0.5.3)
- github-markup (0.7.5)
+ gemoji (1.3.1)
+ gherkin-ruby (0.3.1)
+ racc
+ github-markdown (0.5.5)
gitlab-flowdock-git-hook (0.4.2.2)
gitlab-grit (>= 2.4.1)
multi_json
- gitlab-gollum-lib (1.0.1)
+ gitlab-gollum-lib (1.0.2)
github-markdown (~> 0.5.3)
github-markup (>= 0.7.5, < 1.0.0)
- gitlab-grit (>= 2.5.1)
+ gitlab-grit (~> 2.6.1)
+ gitlab-pygments.rb (~> 0.5.4)
nokogiri (~> 1.5.9)
- pygments.rb (~> 0.4.2)
sanitize (~> 2.0.3)
stringex (~> 1.5.1)
- gitlab-grack (1.0.1)
- rack (~> 1.4.1)
- gitlab-grit (2.6.0)
+ gitlab-grack (2.0.0.pre)
+ rack (~> 1.5.1)
+ gitlab-grit (2.6.3)
charlock_holmes (~> 0.6.9)
diff-lcs (~> 1.1)
mime-types (~> 1.15)
posix-spawn (~> 0.3.6)
- gitlab-pygments.rb (0.3.2)
+ gitlab-linguist (2.9.6)
+ charlock_holmes (~> 0.6.6)
+ escape_utils (~> 0.2.4)
+ gitlab-pygments.rb (~> 0.5.4)
+ mime-types (~> 1.19)
+ gitlab-pygments.rb (0.5.4)
posix-spawn (~> 0.3.6)
yajl-ruby (~> 1.1.0)
- gitlab_git (3.0.0.rc1)
- activesupport (~> 3.2.13)
- github-linguist (~> 2.3.4)
- gitlab-grit (~> 2.6.0)
+ gitlab_git (4.0.0)
+ activesupport (~> 4.0.0)
+ gitlab-grit (~> 2.6.1)
+ gitlab-linguist (~> 2.9.5)
+ gitlab-pygments.rb (~> 0.5.4)
gitlab_meta (6.0)
gitlab_omniauth-ldap (1.0.3)
net-ldap (~> 0.3.1)
omniauth (~> 1.0)
pyu-ruby-sasl (~> 0.0.3.1)
rubyntlm (~> 0.1.1)
- gon (4.1.1)
+ gon (5.0.1)
actionpack (>= 2.3.0)
json
- grape (0.4.1)
+ grape (0.6.1)
activesupport
builder
hashie (>= 1.2.0)
@@ -201,91 +215,91 @@ GEM
rack (>= 1.3.0)
rack-accept
rack-mount
- virtus
+ virtus (>= 1.0.0)
grape-entity (0.3.0)
activesupport
multi_json (>= 1.3.2)
growl (1.0.3)
- guard (1.8.1)
+ guard (2.2.4)
formatador (>= 0.2.4)
- listen (>= 1.0.0)
- lumberjack (>= 1.0.2)
- pry (>= 0.9.10)
- thor (>= 0.14.6)
- guard-rspec (3.0.2)
- guard (>= 1.8)
- rspec (~> 2.13)
+ listen (~> 2.1)
+ lumberjack (~> 1.0)
+ pry (>= 0.9.12)
+ thor (>= 0.18.1)
+ guard-rspec (4.2.0)
+ guard (>= 2.1.1)
+ rspec (>= 2.14, < 4.0)
guard-spinach (0.0.2)
guard (>= 1.1)
spinach
- haml (4.0.3)
+ haml (4.0.4)
tilt
- haml-rails (0.4)
- actionpack (>= 3.1, < 4.1)
- activesupport (>= 3.1, < 4.1)
- haml (>= 3.1, < 4.1)
- railties (>= 3.1, < 4.1)
- hashie (1.2.0)
+ haml-rails (0.5.1)
+ actionpack (~> 4.0.0)
+ activesupport (~> 4.0.0)
+ haml (>= 3.1, < 5.0)
+ railties (~> 4.0.0)
+ hashie (2.0.5)
hike (1.2.3)
hipchat (0.9.0)
httparty
httparty
http_parser.rb (0.5.3)
- httparty (0.11.0)
- multi_json (~> 1.0)
+ httparty (0.12.0)
+ json (~> 1.8)
multi_xml (>= 0.5.2)
httpauth (0.2.0)
- i18n (0.6.5)
- jasmine (1.3.2)
- jasmine-core (~> 1.3.1)
- rack (~> 1.0)
- rspec (>= 1.3.1)
- selenium-webdriver (>= 0.1.3)
- jasmine-core (1.3.1)
- journey (1.0.4)
- jquery-atwho-rails (0.3.0)
+ i18n (0.6.9)
+ ice_nine (0.10.0)
+ jasmine (2.0.0.rc5)
+ jasmine-core (~> 2.0.0.rc5)
+ phantomjs
+ rack (>= 1.2.1)
+ rake
+ jasmine-core (2.0.0.rc5)
+ jquery-atwho-rails (0.3.3)
jquery-rails (2.1.3)
railties (>= 3.1.0, < 5.0)
thor (~> 0.14)
- jquery-turbolinks (1.0.0)
+ jquery-turbolinks (2.0.1)
railties (>= 3.1.0)
turbolinks
jquery-ui-rails (2.0.2)
jquery-rails
railties (>= 3.1.0)
- json (1.7.7)
+ json (1.8.1)
jwt (0.1.8)
multi_json (>= 1.5)
kaminari (0.14.1)
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
- kgio (2.8.0)
- launchy (2.3.0)
+ kgio (2.8.1)
+ launchy (2.4.2)
addressable (~> 2.3)
- letter_opener (1.1.1)
+ letter_opener (1.1.2)
launchy (~> 2.2)
- libv8 (3.11.8.17)
- listen (1.2.2)
+ libv8 (3.16.14.3)
+ listen (2.3.1)
+ celluloid (>= 0.15.2)
rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9)
- rb-kqueue (>= 0.2)
- lumberjack (1.0.3)
+ lumberjack (1.0.4)
mail (2.5.4)
mime-types (~> 1.16)
treetop (~> 1.4.8)
- method_source (0.8.1)
- mime-types (1.25)
- minitest (4.7.4)
+ method_source (0.8.2)
+ mime-types (1.25.1)
+ minitest (4.7.5)
modernizr (2.6.2)
sprockets (~> 2.0)
multi_json (1.8.2)
- multi_xml (0.5.4)
+ multi_xml (0.5.5)
multipart-post (1.2.0)
mysql2 (0.3.11)
net-ldap (0.3.1)
net-scp (1.0.4)
net-ssh (>= 1.99.1)
- net-ssh (2.6.8)
+ net-ssh (2.7.0)
nokogiri (1.5.10)
oauth (0.4.7)
oauth2 (0.8.1)
@@ -297,10 +311,10 @@ GEM
omniauth (1.1.4)
hashie (>= 1.2, < 3)
rack
- omniauth-github (1.1.0)
+ omniauth-github (1.1.1)
omniauth (~> 1.0)
omniauth-oauth2 (~> 1.1)
- omniauth-google-oauth2 (0.1.19)
+ omniauth-google-oauth2 (0.2.1)
omniauth (~> 1.0)
omniauth-oauth2
omniauth-oauth (1.0.1)
@@ -309,11 +323,12 @@ GEM
omniauth-oauth2 (1.1.1)
oauth2 (~> 0.8.0)
omniauth (~> 1.0)
- omniauth-twitter (0.0.17)
+ omniauth-twitter (1.0.1)
multi_json (~> 1.3)
omniauth-oauth (~> 1.0)
- orm_adapter (0.4.0)
+ orm_adapter (0.5.0)
pg (0.15.1)
+ phantomjs (1.9.2.0)
poltergeist (1.4.1)
capybara (~> 2.1.0)
cliver (~> 0.2.1)
@@ -321,44 +336,40 @@ GEM
websocket-driver (>= 0.2.0)
polyglot (0.3.3)
posix-spawn (0.3.6)
- pry (0.9.12.2)
- coderay (~> 1.0.5)
+ protected_attributes (1.0.5)
+ activemodel (>= 4.0.1, < 5.0)
+ pry (0.9.12.4)
+ coderay (~> 1.0)
method_source (~> 0.8)
slop (~> 3.4)
- pygments.rb (0.4.2)
- posix-spawn (~> 0.3.6)
- yajl-ruby (~> 1.1.0)
pyu-ruby-sasl (0.0.3.3)
quiet_assets (1.0.2)
railties (>= 3.1, < 5.0)
- rack (1.4.5)
+ racc (1.4.10)
+ rack (1.5.2)
rack-accept (0.4.5)
rack (>= 0.4)
- rack-attack (2.2.1)
+ rack-attack (2.3.0)
rack
- rack-cache (1.2)
- rack (>= 0.4)
+ rack-cors (0.2.9)
rack-mini-profiler (0.1.31)
rack (>= 1.1.3)
rack-mount (0.8.3)
rack (>= 1.0.0)
- rack-protection (1.5.0)
- rack
- rack-ssl (1.3.3)
+ rack-protection (1.5.1)
rack
rack-test (0.6.2)
rack (>= 1.0)
- rails (3.2.15)
- actionmailer (= 3.2.15)
- actionpack (= 3.2.15)
- activerecord (= 3.2.15)
- activeresource (= 3.2.15)
- activesupport (= 3.2.15)
- bundler (~> 1.0)
- railties (= 3.2.15)
- rails-dev-tweaks (0.6.1)
- actionpack (~> 3.1)
- railties (~> 3.1)
+ rails (4.0.2)
+ actionmailer (= 4.0.2)
+ actionpack (= 4.0.2)
+ activerecord (= 4.0.2)
+ activesupport (= 4.0.2)
+ bundler (>= 1.3.0, < 2.0)
+ railties (= 4.0.2)
+ sprockets-rails (~> 2.0.0)
+ rails-observers (0.1.2)
+ activemodel (~> 4.0)
rails_best_practices (1.14.4)
activesupport
awesome_print
@@ -368,180 +379,179 @@ GEM
i18n
require_all
ruby-progressbar
- railties (3.2.15)
- actionpack (= 3.2.15)
- activesupport (= 3.2.15)
- rack-ssl (~> 1.3.2)
+ railties (4.0.2)
+ actionpack (= 4.0.2)
+ activesupport (= 4.0.2)
rake (>= 0.8.7)
- rdoc (~> 3.4)
- thor (>= 0.14.6, < 2.0)
- raindrops (0.11.0)
+ thor (>= 0.18.1, < 2.0)
+ raindrops (0.12.0)
rake (10.1.0)
raphael-rails (2.1.2)
rb-fsevent (0.9.3)
- rb-inotify (0.9.0)
- ffi (>= 0.5.0)
- rb-kqueue (0.2.0)
+ rb-inotify (0.9.2)
ffi (>= 0.5.0)
rdoc (3.12.2)
json (~> 1.4)
redcarpet (2.2.2)
- redis (3.0.4)
- redis-actionpack (3.2.4)
- actionpack (~> 3.2.0)
- redis-rack (~> 1.4.4)
- redis-store (~> 1.1.4)
- redis-activesupport (3.2.4)
- activesupport (~> 3.2.0)
+ redis (3.0.6)
+ redis-actionpack (4.0.0)
+ actionpack (~> 4)
+ redis-rack (~> 1.5.0)
+ redis-store (~> 1.1.0)
+ redis-activesupport (4.0.0)
+ activesupport (~> 4)
+ redis-store (~> 1.1.0)
+ redis-namespace (1.4.1)
+ redis (~> 3.0.4)
+ redis-rack (1.5.0)
+ rack (~> 1.5)
+ redis-store (~> 1.1.0)
+ redis-rails (4.0.0)
+ redis-actionpack (~> 4)
+ redis-activesupport (~> 4)
redis-store (~> 1.1.0)
- redis-namespace (1.3.1)
- redis (~> 3.0.0)
- redis-rack (1.4.4)
- rack (~> 1.4.0)
- redis-store (~> 1.1.4)
- redis-rails (3.2.4)
- redis-actionpack (~> 3.2.4)
- redis-activesupport (~> 3.2.4)
- redis-store (~> 1.1.4)
redis-store (1.1.4)
redis (>= 2.2)
ref (1.0.5)
- require_all (1.3.1)
+ require_all (1.3.2)
rest-client (1.6.7)
mime-types (>= 1.16)
- rspec (2.13.0)
- rspec-core (~> 2.13.0)
- rspec-expectations (~> 2.13.0)
- rspec-mocks (~> 2.13.0)
- rspec-core (2.13.1)
- rspec-expectations (2.13.0)
+ rspec (2.14.1)
+ rspec-core (~> 2.14.0)
+ rspec-expectations (~> 2.14.0)
+ rspec-mocks (~> 2.14.0)
+ rspec-core (2.14.7)
+ rspec-expectations (2.14.4)
diff-lcs (>= 1.1.3, < 2.0)
- rspec-mocks (2.13.1)
- rspec-rails (2.13.2)
+ rspec-mocks (2.14.4)
+ rspec-rails (2.14.0)
actionpack (>= 3.0)
activesupport (>= 3.0)
railties (>= 3.0)
- rspec-core (~> 2.13.0)
- rspec-expectations (~> 2.13.0)
- rspec-mocks (~> 2.13.0)
+ rspec-core (~> 2.14.0)
+ rspec-expectations (~> 2.14.0)
+ rspec-mocks (~> 2.14.0)
ruby-hmac (0.4.0)
ruby-progressbar (1.2.0)
rubyntlm (0.1.1)
- rubyzip (0.9.9)
- safe_yaml (0.9.3)
- sanitize (2.0.3)
- nokogiri (>= 1.4.4, < 1.6)
- sass (3.2.11)
- sass-rails (3.2.6)
- railties (~> 3.2.0)
+ safe_yaml (0.9.7)
+ sanitize (2.0.6)
+ nokogiri (>= 1.4.4)
+ sass (3.2.12)
+ sass-rails (4.0.1)
+ railties (>= 4.0.0, < 5.0)
sass (>= 3.1.10)
- tilt (~> 1.3)
+ sprockets-rails (~> 2.0.0)
sdoc (0.3.20)
json (>= 1.1.3)
rdoc (~> 3.10)
- seed-fu (2.2.0)
- activerecord (~> 3.1)
- activesupport (~> 3.1)
- select2-rails (3.4.2)
- sass-rails
+ seed-fu (2.3.0)
+ activerecord (>= 3.1, < 4.1)
+ activesupport (>= 3.1, < 4.1)
+ select2-rails (3.5.2)
thor (~> 0.14)
- selenium-webdriver (2.33.0)
- childprocess (>= 0.2.5)
- multi_json (~> 1.0)
- rubyzip
- websocket (~> 1.0.4)
settingslogic (2.0.9)
- sexp_processor (4.3.0)
+ sexp_processor (4.4.0)
shoulda-matchers (2.1.0)
activesupport (>= 3.0.0)
- sidekiq (2.14.0)
- celluloid (>= 0.14.1)
+ sidekiq (2.17.0)
+ celluloid (>= 0.15.2)
connection_pool (>= 1.0.0)
json
redis (>= 3.0.4)
- redis-namespace
+ redis-namespace (>= 1.3.1)
simple_oauth (0.1.9)
- simplecov (0.7.1)
- multi_json (~> 1.0)
- simplecov-html (~> 0.7.1)
- simplecov-html (0.7.1)
- sinatra (1.4.3)
+ simplecov (0.8.2)
+ docile (~> 1.1.0)
+ multi_json
+ simplecov-html (~> 0.8.0)
+ simplecov-html (0.8.0)
+ sinatra (1.4.4)
rack (~> 1.4)
rack-protection (~> 1.4)
tilt (~> 1.3, >= 1.3.4)
six (0.2.0)
- slim (2.0.0)
- temple (~> 0.6.5)
- tilt (~> 1.3, >= 1.3.3)
- slop (3.4.5)
- spinach (0.8.3)
+ slim (2.0.2)
+ temple (~> 0.6.6)
+ tilt (>= 1.3.3, < 2.1)
+ slop (3.4.7)
+ spinach (0.8.7)
colorize (= 0.5.8)
- gherkin-ruby (~> 0.3.0)
+ gherkin-ruby (>= 0.3.1)
spinach-rails (0.2.1)
capybara (>= 2.0.0)
railties (>= 3)
spinach (>= 0.4)
- spork (1.0.0rc2)
- sprockets (2.2.2)
+ spork (1.0.0rc4)
+ sprockets (2.10.1)
hike (~> 1.2)
multi_json (~> 1.0)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
+ sprockets-rails (2.0.1)
+ actionpack (>= 3.0)
+ activesupport (>= 3.0)
+ sprockets (~> 2.8)
stamp (0.5.0)
state_machine (1.2.0)
stringex (1.5.1)
- temple (0.6.5)
+ temple (0.6.7)
term-ansicolor (1.2.2)
tins (~> 0.8)
- test_after_commit (0.2.1)
- therubyracer (0.11.4)
- libv8 (~> 3.11.8.12)
+ test_after_commit (0.2.2)
+ therubyracer (0.12.0)
+ libv8 (~> 3.16.14.0)
ref
- thin (1.5.1)
+ thin (1.6.1)
daemons (>= 1.0.9)
- eventmachine (>= 0.12.6)
+ eventmachine (>= 1.0.0)
rack (>= 1.0.0)
thor (0.18.1)
+ thread_safe (0.1.3)
+ atomic
tilt (1.4.1)
timers (1.1.0)
- tinder (1.9.2)
+ tinder (1.9.3)
eventmachine (~> 1.0)
faraday (~> 0.8)
faraday_middleware (~> 0.9)
- hashie (~> 1.0)
- json (~> 1.7.5)
+ hashie (>= 1.0, < 3)
+ json (~> 1.8.0)
mime-types (~> 1.19)
- multi_json (~> 1.5)
+ multi_json (~> 1.7)
twitter-stream (~> 0.1)
- tins (0.11.0)
+ tins (0.13.1)
treetop (1.4.15)
polyglot
polyglot (>= 0.3.1)
- turbolinks (1.2.0)
+ turbolinks (2.0.0)
coffee-rails
twitter-stream (0.1.16)
eventmachine (>= 0.12.8)
http_parser.rb (~> 0.5.1)
simple_oauth (~> 0.1.4)
tzinfo (0.3.38)
- uglifier (2.1.1)
+ uglifier (2.3.2)
execjs (>= 0.3.0)
- multi_json (~> 1.0, >= 1.0.2)
+ json (>= 1.8.0)
underscore-rails (1.4.4)
unicorn (4.6.3)
kgio (~> 2.6)
rack
raindrops (~> 0.7)
- virtus (0.5.5)
- backports (~> 3.3)
+ unicorn-worker-killer (0.4.2)
+ unicorn (~> 4)
+ virtus (1.0.1)
+ axiom-types (~> 0.0.5)
+ coercible (~> 1.0)
descendants_tracker (~> 0.0.1)
+ equalizer (~> 0.0.7)
warden (1.2.3)
rack (>= 1.0)
- webmock (1.11.0)
+ webmock (1.16.0)
addressable (>= 2.2.7)
crack (>= 0.3.2)
- websocket (1.0.7)
- websocket-driver (0.3.0)
+ websocket-driver (0.3.1)
xpath (2.0.0)
nokogiri (~> 1.3)
yajl-ruby (1.1.0)
@@ -550,42 +560,45 @@ PLATFORMS
ruby
DEPENDENCIES
+ actionpack-action_caching
+ actionpack-page_caching
+ activerecord-deprecated_finders
acts-as-taggable-on
annotate (~> 2.6.0.beta2)
asciidoctor
awesome_print
better_errors
binding_of_caller
- bootstrap-sass
+ bootstrap-sass (~> 3.0)
capybara
carrierwave
- chosen-rails (= 1.0.0)
+ chosen-rails (= 1.0.1)
coffee-rails
colored
coveralls
d3_rails (~> 3.1.4)
database_cleaner
- devise (~> 2.2)
- devise-async
+ devise (= 3.0.4)
+ devise-async (= 0.8.0)
email_spec
enumerize
factory_girl_rails
ffaker
fog (~> 1.3.1)
- font-awesome-rails
+ font-awesome-rails (~> 3.2)
foreman
- gemoji (~> 1.2.1)
- github-linguist
- github-markup (~> 0.7.4)
+ gemoji (~> 1.3.0)
+ github-markup (~> 0.7.4)!
gitlab-flowdock-git-hook (~> 0.4.2)
- gitlab-gollum-lib (~> 1.0.1)
- gitlab-grack (~> 1.0.1)
- gitlab-pygments.rb (~> 0.3.2)
- gitlab_git (~> 3.0.0.rc1)
+ gitlab-gollum-lib (~> 1.0.2)
+ gitlab-grack (~> 2.0.0.pre)
+ gitlab-linguist (~> 2.9.6)
+ gitlab-pygments.rb (~> 0.5.4)
+ gitlab_git (~> 4.0.0)
gitlab_meta (= 6.0)
gitlab_omniauth-ldap (= 1.0.3)
- gon
- grape (~> 0.4.1)
+ gon (~> 5.0.0)
+ grape (~> 0.6.1)
grape-entity (~> 0.3.0)
growl
guard-rspec
@@ -593,8 +606,8 @@ DEPENDENCIES
haml-rails
hipchat (~> 0.9.0)
httparty
- jasmine
- jquery-atwho-rails (= 0.3.0)
+ jasmine (= 2.0.0.rc5)
+ jquery-atwho-rails (~> 0.3.3)
jquery-rails (= 2.1.3)
jquery-turbolinks
jquery-ui-rails (= 2.0.2)
@@ -610,12 +623,14 @@ DEPENDENCIES
omniauth-twitter
pg
poltergeist (~> 1.4.1)
+ protected_attributes
pry
quiet_assets (~> 1.0.1)
rack-attack
+ rack-cors
rack-mini-profiler
- rails (= 3.2.15)
- rails-dev-tweaks
+ rails (~> 4.0.0)
+ rails-observers
rails_best_practices
raphael-rails (~> 2.1.2)
rb-fsevent
@@ -647,4 +662,5 @@ DEPENDENCIES
uglifier
underscore-rails (~> 1.4.4)
unicorn (~> 4.6.3)
+ unicorn-worker-killer
webmock
diff --git a/PROCESS.md b/PROCESS.md
index 668cacc870a..8d0ce0c20ab 100644
--- a/PROCESS.md
+++ b/PROCESS.md
@@ -8,25 +8,25 @@ Below we describe the contributing process to GitLab for two reasons. So that co
### Issue team
- Looks for issues without workflow labels and triages issue
-- Monitors pull requests
-- Closes invalid issues and pull requests with a comment (duplicates, [feature requests](#feature-requests), [fixed in newer version](#issue-fixed-in-newer-version), [issue report for old version](#issue-report-for-old-version), not a problem in GitLab, etc.)
+- Monitors merge requests
+- Closes invalid issues and merge requests with a comment (duplicates, [feature requests](#feature-requests), [fixed in newer version](#issue-fixed-in-newer-version), [issue report for old version](#issue-report-for-old-version), not a problem in GitLab, etc.)
- Assigns appropriate [labels](#how-we-handle-issues)
-- Asks for feedback from issue reporter/pull request initiator ([invalid issue reports](#improperly-formatted-issue), [format code](#code-format), etc.)
+- Asks for feedback from issue reporter/merge request initiator ([invalid issue reports](#improperly-formatted-issue), [format code](#code-format), etc.)
- Asks for feedback from the relevant developer(s) based on the [list of members and their specialities](http://gitlab.org/team/)
-- Monitors all issues/pull requests for feedback (but especially ones commented on since automatically watching them):
+- Monitors all issues/merge requests for feedback (but especially ones commented on since automatically watching them):
- Closes issues with no feedback from the reporter for two weeks
-- Closes stale pull requests
+- Closes stale merge requests
### Development team
-- Responds to issues and pull requests the issue team mentions them in
+- Responds to issues and merge requests the issue team mentions them in
- Monitors for new issues in _Awaiting developer action/feedback_ with no developer activity (once a week)
-- Monitors for new pull requests (at least once a week)
-- Manages their work queue by looking at issues and pull requests assigned to them
+- Monitors for new merge requests (at least once a week)
+- Manages their work queue by looking at issues and merge requests assigned to them
- Close fixed issues (via commit messages or manually)
- Codes [new features](http://feedback.gitlab.com/forums/176466-general/filters/top)!
- Response guidelines
-- Be kind to people trying to contribute. Be aware that people can be a non-native or a native English speaker, they might not understand thing or they might be very sensitive to how your word things. Use emoji to express your feelings (heart, star, smile, etc.). Some good tips about giving feedback to pull requests is in the [Thoughtbot code review guide](https://github.com/thoughtbot/guides/tree/master/code-review).
+- Be kind to people trying to contribute. Be aware that people can be a non-native or a native English speaker, they might not understand thing or they might be very sensitive to how your word things. Use emoji to express your feelings (heart, star, smile, etc.). Some good tips about giving feedback to merge requests is in the [Thoughtbot code review guide](https://github.com/thoughtbot/guides/tree/master/code-review).
## Priorities of the issue team
@@ -45,8 +45,8 @@ Workflow labels are purposely not very detailed since that would be hard to keep
- _Awaiting feedback_: Feedback pending from the reporter
- _Awaiting confirmation of fix_: The issue should already be solved in **master** (generally you can avoid this workflow item and just close the issue right away)
-- _Attached PR_: There is a PR attached and the discussion should happen there
- - We need to let issues stay in sync with the PR's. We can do this with a "Closing #XXXX" or "Fixes #XXXX" comment in the PR. We can't close the issue when there is a pull request because sometimes a PR is not good and we just close the PR, then the issue must stay.
+- _Attached MR_: There is a MR attached and the discussion should happen there
+ - We need to let issues stay in sync with the MR's. We can do this with a "Closing #XXXX" or "Fixes #XXXX" comment in the MR. We can't close the issue when there is a merge request because sometimes a MR is not good and we just close the MR, then the issue must stay.
- _Awaiting developer action/feedback_: Issue needs to be fixed or clarified by a developer
## Functional labels
@@ -59,7 +59,7 @@ If an issue is complex and needs the attention of a specific person, assignment
## Label colors
- Light orange `#fef2c0`: workflow labels for issue team members (awaiting feedback, awaiting confirmation of fix)
-- Bright orange `#eb6420`: workflow labels for core team members (attached PR, awaiting developer action/feedback)
+- Bright orange `#eb6420`: workflow labels for core team members (attached MR, awaiting developer action/feedback)
- Light blue `#82C5FF`: functional labels
- Green labels `#009800`: issues that can generally be ignored. For example, issues given the following labels normally can be closed immediately:
- Feature request (see copy & paste response: [Feature requests](#feature-requests))
@@ -69,19 +69,19 @@ If an issue is complex and needs the attention of a specific person, assignment
### Improperly formatted issue
-Thanks for the issue report. Please reformat your issue to conform to the issue tracker guidelines found in our \[contributing guidelines\]\(https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md#issue-tracker-guidelines).
+Thanks for the issue report. Please reformat your issue to conform to the issue tracker guidelines found in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/masterCONTRIBUTING.md#issue-tracker-guidelines).
### Feature requests
-Thanks for your interest in GitLab. We don't use the GitHub issue tracker for feature requests. Please use http://feedback.gitlab.com/ for this purpose or create a pull request implementing this feature. Have a look at the \[contribution guidelines\]\(https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md) for more information.
+Thanks for your interest in GitLab. We don't use the GitHub issue tracker for feature requests. Please use http://feedback.gitlab.com/ for this purpose or create a merge request implementing this feature. Have a look at the \[contribution guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/masterCONTRIBUTING.md) for more information.
### Issue report for old version
-Thanks for the issue report but we only support issues for the latest stable version of GitLab. I'm closing this issue but if you still experience this problem in the latest stable version, please open a new issue (but also reference the old issue(s)). Make sure to also include the necessary debugging information conforming to the issue tracker guidelines found in our \[contributing guidelines\]\(https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md#issue-tracker-guidelines).
+Thanks for the issue report but we only support issues for the latest stable version of GitLab. I'm closing this issue but if you still experience this problem in the latest stable version, please open a new issue (but also reference the old issue(s)). Make sure to also include the necessary debugging information conforming to the issue tracker guidelines found in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/masterCONTRIBUTING.md#issue-tracker-guidelines).
### Support requests and configuration questions
-Thanks for your interest in GitLab. We don't use the GitHub issue tracker for support requests and configuration questions. Please use the \[support forum\]\(https://groups.google.com/forum/#!forum/gitlabhq), \[Stack Overflow\]\(http://stackoverflow.com/questions/tagged/gitlab), the unofficial #gitlab IRC channel on Freenode or the http://www.gitlab.com paid services for this purpose. Have a look at the \[contribution guidelines\]\(https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md) for more information.
+Thanks for your interest in GitLab. We don't use the GitHub issue tracker for support requests and configuration questions. Please use the \[support forum\]\(https://groups.google.com/forum/#!forum/gitlabhq), \[Stack Overflow\]\(http://stackoverflow.com/questions/tagged/gitlab), the unofficial #gitlab IRC channel on Freenode or the http://www.gitlab.com paid services for this purpose. Have a look at the \[contribution guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/masterCONTRIBUTING.md) for more information.
### Code format
@@ -89,17 +89,17 @@ Please use ``` to format console output, logs, and code as it's very hard to rea
### Issue fixed in newer version
-Thanks for the issue report. This issue has already been fixed in newer versions of GitLab. Due to the size of this project and our limited resources we are only able to support the latest stable release as outlined in our \[contributing guidelines\]\(https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md#issue-tracker). In order to get this bug fix and enjoy many new features please \[upgrade\]\(http://blog.gitlab.org/). If you still experience issues at that time please open a new issue following our issue tracker guidelines found in the \[contributing guidelines\]\(https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md#issue-tracker-guidelines).
+Thanks for the issue report. This issue has already been fixed in newer versions of GitLab. Due to the size of this project and our limited resources we are only able to support the latest stable release as outlined in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/masterCONTRIBUTING.md#issue-tracker). In order to get this bug fix and enjoy many new features please \[upgrade\]\(http://blog.gitlab.org/). If you still experience issues at that time please open a new issue following our issue tracker guidelines found in the \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/masterCONTRIBUTING.md#issue-tracker-guidelines).
-### Improperly formatted pull request
+### Improperly formatted merge request
-Thanks for your interest in improving the GitLab codebase! Please update your pull request according to the \[contributing guidelines\]\(https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md#pull-request-guidelines).
+Thanks for your interest in improving the GitLab codebase! Please update your merge request according to the \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/masterCONTRIBUTING.md#pull-request-guidelines).
### Inactivity close of an issue
-It's been at least 2 weeks (and a new release) since we heard from you. I'm closing this issue but if you still experience this problem, please open a new issue (but also reference the old issue(s)). Make sure to also include the necessary debugging information conforming to the issue tracker guidelines found in our \[contributing guidelines\]\(https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md#issue-tracker-guidelines).
+It's been at least 2 weeks (and a new release) since we heard from you. I'm closing this issue but if you still experience this problem, please open a new issue (but also reference the old issue(s)). Make sure to also include the necessary debugging information conforming to the issue tracker guidelines found in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/masterCONTRIBUTING.md#issue-tracker-guidelines).
-### Inactivity close of a pull request
+### Inactivity close of a merge request
-This pull request has been closed because a request for more information has not been reacted to for more than 2 weeks. If you respond and conform to the pull request guidelines in our \[contributing guidelines\]\(https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md#pull-requests) we will reopen this pull request.
+This merge request has been closed because a request for more information has not been reacted to for more than 2 weeks. If you respond and conform to the merge request guidelines in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/masterCONTRIBUTING.md#pull-requests) we will reopen this merge request.
diff --git a/README.md b/README.md
index b1c109950a0..c151f966c5a 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
## GitLab: self hosted Git management software
-![logo](https://raw.github.com/gitlabhq/gitlabhq/master/public/gitlab_logo.png)
+![logo](https://gitlab.com/gitlab-org/gitlab-ce/raw/master/public/gitlab_logo.png)
![animated-screenshots](https://gist.github.com/fnkr/2f9badd56bfe0ed04ee7/raw/4f48806fbae97f556c2f78d8c2d299c04500cb0d/compiled.gif)
@@ -32,7 +32,9 @@
* GitLab.com commercial services: [Homepage](http://www.gitlab.com/) | [Subscription](http://www.gitlab.com/subscription/) | [Consultancy](http://www.gitlab.com/consultancy/) | [GitLab Cloud](http://www.gitlab.com/cloud/) | [Blog](http://blog.gitlab.com/)
-* GitLab CI: [Readme](https://github.com/gitlabhq/gitlab-ci/blob/master/README.md) of the GitLab open-source continuous integration server
+* [GitLab Enterprise Edition](https://www.gitlab.com/features/) offers additional features that are useful for larger organizations (100+ users).
+
+* [GitLab CI](https://gitlab.com/gitlab-org/gitlab-ci/blob/master/README.md) is a continuous integration (CI) server that is easy to integrate with GitLab.
### Requirements
@@ -46,29 +48,24 @@
### Installation
-#### Official production installation
-
-* [Installation guide for a production server](doc/install/installation.md)
+#### Official installation methods
+* [Manual installation guide for a production server](doc/install/installation.md)
-#### Official development installation
+* [GitLab Chef Cookbook](https://gitlab.com/gitlab-org/cookbook-gitlab/blob/master/README.md) This cookbook can be used both for development installations and production installations. If you want to [contribute](CONTRIBUTE.md) to GitLab we suggest you follow the [development installation on a virtual machine with Vagrant](https://gitlab.com/gitlab-org/cookbook-gitlab/blob/master/doc/development.md) instructions to install all testing dependencies.
-If you want to contribute, please first read our [Contributing Guidelines](https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md) and then we suggest you to use the Vagrant virtual machine project to get an environment working with all dependencies.
+#### Third party one-click installers
-* [Vagrant virtual machine for development](https://github.com/gitlabhq/gitlab-vagrant-vm)
+* [Digital Ocean 1-Click Application Install](https://www.digitalocean.com/blog_posts/host-your-git-repositories-in-55-seconds-with-gitlab) Have a new server up in 55 seconds. Digital Ocean uses SSD disks which is great for an IO intensive app such as GitLab.
+* [BitNami one-click installers](http://bitnami.com/stack/gitlab) This package contains both GitLab and GitLab CI. It is available as installer, virtual machine or for cloud hosting providers (Amazon Web Services/Azure/etc.).
-#### Unofficial production installations
+#### Unofficial installation methods
-* [GitLab recipes](https://github.com/gitlabhq/gitlab-recipes) repository with unofficial guides for using GitLab with different software (operating systems, webservers, etc.) than the official version.
+* [GitLab recipes](https://gitlab.com/gitlab-org/gitlab-recipes/) repository with unofficial guides for using GitLab with different software (operating systems, webservers, etc.) than the official version.
* [Installation guides](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Unofficial-Installation-Guides) public wiki with unofficial guides to install GitLab on different operating systems.
-* [BitNami one-click installers](http://bitnami.com/stack/gitlab)
-
-* [TurnKey Linux virtual appliance](http://www.turnkeylinux.org/gitlab)
-
-
### New versions and upgrading
Since 2011 GitLab is released on the 22nd of every month. Every new release includes an upgrade guide.
@@ -79,7 +76,6 @@ Since 2011 GitLab is released on the 22nd of every month. Every new release incl
* Features that will be in the next releases are listed on [the feedback and suggestions forum](http://feedback.gitlab.com/forums/176466-general) with the status [started](http://feedback.gitlab.com/forums/176466-general/status/796456) and [completed](http://feedback.gitlab.com/forums/176466-general/status/796457).
-
### Run in production mode
The Installation guide contains instructions on how to download an init script and run it automatically on boot. You can also start the init script manually:
@@ -99,7 +95,7 @@ Start it with [Foreman](https://github.com/ddollar/foreman)
or start each component separately
bundle exec rails s
- bundle exec rake sidekiq:start
+ script/background_jobs start
### Run the tests
@@ -110,7 +106,7 @@ or start each component separately
* Run all tests
- bundle exec rake gitlab:test
+ bundle exec rake gitlab:test RAILS_ENV=test
* [RSpec](http://rspec.info/) unit and functional tests
@@ -147,15 +143,17 @@ or start each component separately
* [Mailing list](https://groups.google.com/forum/#!forum/gitlabhq) and [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) are the best places to ask questions. For example you can use it if you have questions about: permission denied errors, invisible repos, can't clone/pull/push or with web hooks that don't fire. Please search for similar issues before posting your own, there's a good chance somebody else had the same issue you have now and has resolved it. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there to a fix.
-* [Unofficial #gitlab IRC on Freenode](http://www.freenode.net/) is another way to get in touch with other GitLab users who may be able to help you.
-
* [Feedback and suggestions forum](http://feedback.gitlab.com) is the place to propose and discuss new features for GitLab.
-* [Contributing guide](https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md) describes how to submit pull requests and issues. Pull requests and issues not in line with the guidelines in this document will be closed.
+* [Contributing guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/masterCONTRIBUTING.md) describes how to submit merge requests and issues. Pull requests and issues not in line with the guidelines in this document will be closed.
* [Support subscription](http://www.gitlab.com/subscription/) connects you to the knowledge of GitLab experts that will resolve your issues and answer your questions.
-* [Consultancy](http://www.gitlab.com/consultancy/) allows you hire GitLab experts for installations, upgrades and customizations.
+* [Consultancy](http://www.gitlab.com/consultancy/) from the GitLab experts for installations, upgrades and customizations.
+
+* [#gitlab IRC channel](http://www.freenode.net/) on Freenode to get in touch with other GitLab users and get help, it's managed by James Newton, Drew Blessing and Sam Gleske
+
+* [Book](http://www.packtpub.com/gitlab-repository-management/book) written by GitLab enthusiast Jonathan M. Hethey is unofficial but it offers a good overview.
### Getting in touch
diff --git a/VERSION b/VERSION
index 5a903253f44..24f0dc3141e 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-6.3.0.pre
+6.5.0.pre
diff --git a/app/assets/images/favicon.ico b/app/assets/images/favicon.ico
index 057f74ac7ab..bfb74960c48 100644
--- a/app/assets/images/favicon.ico
+++ b/app/assets/images/favicon.ico
Binary files differ
diff --git a/app/assets/images/logo-black.png b/app/assets/images/logo-black.png
index be9b7649f3a..31c4a63cd08 100644
--- a/app/assets/images/logo-black.png
+++ b/app/assets/images/logo-black.png
Binary files differ
diff --git a/app/assets/images/logo-white.png b/app/assets/images/logo-white.png
index 5c7ebc284e2..8a4ec851b1d 100644
--- a/app/assets/images/logo-white.png
+++ b/app/assets/images/logo-white.png
Binary files differ
diff --git a/app/assets/javascripts/admin.js.coffee b/app/assets/javascripts/admin.js.coffee
index 6230fe7f93f..6634bb6cc34 100644
--- a/app/assets/javascripts/admin.js.coffee
+++ b/app/assets/javascripts/admin.js.coffee
@@ -8,6 +8,23 @@ class Admin
else
elems.removeAttr 'disabled'
+ $('body').on 'click', '.js-toggle-colors-link', (e) ->
+ e.preventDefault()
+ $('.js-toggle-colors-link').hide()
+ $('.js-toggle-colors-container').show()
+
+ $('input#broadcast_message_color').on 'input', ->
+ previewColor = $('input#broadcast_message_color').val()
+ $('div.broadcast-message-preview').css('background-color', previewColor)
+
+ $('input#broadcast_message_font').on 'input', ->
+ previewColor = $('input#broadcast_message_font').val()
+ $('div.broadcast-message-preview').css('color', previewColor)
+
+ $('textarea#broadcast_message_message').on 'input', ->
+ previewMessage = $('textarea#broadcast_message_message').val()
+ $('div.broadcast-message-preview span').text(previewMessage)
+
$('.log-tabs a').click (e) ->
e.preventDefault()
$(this).tab('show')
diff --git a/app/assets/javascripts/api.js.coffee b/app/assets/javascripts/api.js.coffee
index a36d944cbcb..5f4a38ebbd0 100644
--- a/app/assets/javascripts/api.js.coffee
+++ b/app/assets/javascripts/api.js.coffee
@@ -2,6 +2,7 @@
users_path: "/api/:version/users.json"
user_path: "/api/:version/users/:id.json"
notes_path: "/api/:version/projects/:id/notes.json"
+ namespaces_path: "/api/:version/namespaces.json"
# Get 20 (depends on api) recent notes
# and sort the ascending from oldest to newest
@@ -49,6 +50,20 @@
).done (users) ->
callback(users)
+ # Return namespaces list. Filtered by query
+ namespaces: (query, callback) ->
+ url = Api.buildUrl(Api.namespaces_path)
+
+ $.ajax(
+ url: url
+ data:
+ private_token: gon.api_token
+ search: query
+ per_page: 20
+ dataType: "json"
+ ).done (namespaces) ->
+ callback(namespaces)
+
buildUrl: (url) ->
url = gon.relative_url_root + url if gon.relative_url_root?
return url.replace(':version', gon.api_version)
diff --git a/app/assets/javascripts/behaviors/toggler_behavior.coffee b/app/assets/javascripts/behaviors/toggler_behavior.coffee
index 7e438c51c1c..5afb656e696 100644
--- a/app/assets/javascripts/behaviors/toggler_behavior.coffee
+++ b/app/assets/javascripts/behaviors/toggler_behavior.coffee
@@ -3,7 +3,7 @@ $ ->
container = $(@).closest(".js-toggler-container")
container.toggleClass("on")
-
+
$("body").on "click", ".js-toggle-visibility-link", (e) ->
$(@).find('i').
toggleClass('icon-chevron-down').
@@ -11,7 +11,7 @@ $ ->
container = $(".js-toggle-visibility-container")
container.toggleClass("hide")
e.preventDefault()
-
+
$("body").on "click", ".js-toggle-button", (e) ->
$(@).closest(".js-toggle-container").find(".js-toggle-content").toggle()
e.preventDefault()
diff --git a/app/assets/javascripts/blob.js.coffee b/app/assets/javascripts/blob.js.coffee
index 03e280f8976..99cb1cec911 100644
--- a/app/assets/javascripts/blob.js.coffee
+++ b/app/assets/javascripts/blob.js.coffee
@@ -1,24 +1,76 @@
class BlobView
constructor: ->
+ # handle multi-line select
+ handleMultiSelect = (e) ->
+ [ first_line, last_line ] = parseSelectedLines()
+ [ line_number ] = parseSelectedLines($(this).attr("id"))
+ hash = "L#{line_number}"
+
+ if e.shiftKey and not isNaN(first_line) and not isNaN(line_number)
+ if line_number < first_line
+ last_line = first_line
+ first_line = line_number
+ else
+ last_line = line_number
+
+ hash = if first_line == last_line then "L#{first_line}" else "L#{first_line}-#{last_line}"
+
+ setHash(hash)
+ e.preventDefault()
+
# See if there are lines selected
# "#L12" and "#L34-56" supported
- highlightBlobLines = ->
- if window.location.hash isnt ""
- matches = window.location.hash.match(/\#L(\d+)(\-(\d+))?/)
+ highlightBlobLines = (e) ->
+ [ first_line, last_line ] = parseSelectedLines()
+
+ unless isNaN first_line
+ $("#tree-content-holder .highlight .line").removeClass("hll")
+ $("#LC#{line}").addClass("hll") for line in [first_line..last_line]
+ $("#L#{first_line}").ScrollTo() unless e?
+
+ # parse selected lines from hash
+ # always return first and last line (initialized to NaN)
+ parseSelectedLines = (str) ->
+ first_line = NaN
+ last_line = NaN
+ hash = str || window.location.hash
+
+ if hash isnt ""
+ matches = hash.match(/\#?L(\d+)(\-(\d+))?/)
first_line = parseInt(matches?[1])
last_line = parseInt(matches?[3])
+ last_line = first_line if isNaN(last_line)
+
+ [ first_line, last_line ]
+
+ setHash = (hash) ->
+ hash = hash.replace(/^\#/, "")
+ nodes = $("#" + hash)
+ # if any nodes are using this id, they must be temporarily changed
+ # also, add a temporary div at the top of the screen to prevent scrolling
+ if nodes.length > 0
+ scroll_top = $(document).scrollTop()
+ nodes.attr("id", "")
+ tmp = $("<div></div>")
+ .css({ position: "absolute", visibility: "hidden", top: scroll_top + "px" })
+ .attr("id", hash)
+ .appendTo(document.body)
+
+ window.location.hash = hash
+
+ # restore the nodes
+ if nodes.length > 0
+ tmp.remove()
+ nodes.attr("id", hash)
- unless isNaN first_line
- last_line = first_line if isNaN(last_line)
- $("#tree-content-holder .highlight .line").removeClass("hll")
- $("#LC#{line}").addClass("hll") for line in [first_line..last_line]
- $("#L#{first_line}").ScrollTo()
+ # initialize multi-line select
+ $("#tree-content-holder .line_numbers a[id^=L]").on("click", handleMultiSelect)
# Highlight the correct lines on load
highlightBlobLines()
# Highlight the correct lines when the hash part of the URL changes
- $(window).on 'hashchange', highlightBlobLines
+ $(window).on("hashchange", highlightBlobLines)
@BlobView = BlobView
diff --git a/app/assets/javascripts/commits.js.coffee b/app/assets/javascripts/commits.js.coffee
index de4c06a2728..9c004c997ed 100644
--- a/app/assets/javascripts/commits.js.coffee
+++ b/app/assets/javascripts/commits.js.coffee
@@ -4,13 +4,13 @@ class CommitsList
limit: 0
offset: 0
@disable = false
-
+
@showProgress: ->
$('.loading').show()
-
+
@hideProgress: ->
$('.loading').hide()
-
+
@init: (ref, limit) ->
$(".day-commits-table li.commit").live 'click', (event) ->
if event.target.nodeName != "A"
@@ -21,7 +21,7 @@ class CommitsList
@data.ref = ref
@data.limit = limit
@data.offset = limit
-
+
this.initLoadMore()
this.showProgress()
@@ -32,7 +32,9 @@ class CommitsList
url: location.href
data: @data
complete: this.hideProgress
- dataType: "script"
+ success: (data) ->
+ CommitsList.append(data.count, data.html)
+ dataType: "json"
@append: (count, html) ->
$("#commits-list").append(html)
@@ -40,7 +42,7 @@ class CommitsList
@data.offset += count
else
@disable = true
-
+
@initLoadMore: ->
$(document).unbind('scroll')
$(document).endlessScroll
diff --git a/app/assets/javascripts/issues.js.coffee b/app/assets/javascripts/issues.js.coffee
index 67d9498c50a..43571409bc5 100644
--- a/app/assets/javascripts/issues.js.coffee
+++ b/app/assets/javascripts/issues.js.coffee
@@ -22,7 +22,7 @@
backgroundColor: '#DDD'
opacity: .4
)
-
+
reload: ->
Issues.initSelects()
Issues.initChecks()
@@ -54,7 +54,16 @@
unless terms is last_terms
last_terms = terms
if terms.length >= 2 or terms.length is 0
- form.submit()
+ $.ajax
+ type: "GET"
+ url: location.href
+ data: "issue_search=" + terms
+ complete: ->
+ $(".loading").hide()
+ success: (data) ->
+ $('.issues-holder').html(data.html)
+ Issues.reload()
+ dataType: "json"
checkChanged: ->
checked_issues = $(".selected_issue:checked")
@@ -70,3 +79,9 @@
$("#update_issues_ids").val []
$(".issues_bulk_update").hide()
$(".issues-filters").show()
+
+$ ->
+ $('.edit-issue.inline-update input[type="submit"]').hide();
+ $("body").on "change", ".edit-issue.inline-update select", ->
+ $(this).submit()
+
diff --git a/app/assets/javascripts/main.js.coffee b/app/assets/javascripts/main.js.coffee
index ccd5ad48dbf..5add7541a73 100644
--- a/app/assets/javascripts/main.js.coffee
+++ b/app/assets/javascripts/main.js.coffee
@@ -1,6 +1,3 @@
-window.updatePage = (data) ->
- $.ajax({type: "GET", url: location.href, data: data, dataType: "script"})
-
window.slugify = (text) ->
text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase()
@@ -56,7 +53,7 @@ window.unbindEvents = ->
document.addEventListener("page:fetch", startSpinner)
document.addEventListener("page:fetch", unbindEvents)
-document.addEventListener("page:receive", stopSpinner)
+document.addEventListener("page:change", stopSpinner)
$ ->
# Click a .one_click_select field, select the contents
@@ -84,6 +81,7 @@ $ ->
$(@).parents('form').submit()
$("abbr.timeago").timeago()
+ $('.js-timeago').timeago()
# Flash
if (flash = $(".flash-container")).length > 0
diff --git a/app/assets/javascripts/merge_requests.js.coffee b/app/assets/javascripts/merge_requests.js.coffee
index 5400bc5c1ad..2eef7df1c64 100644
--- a/app/assets/javascripts/merge_requests.js.coffee
+++ b/app/assets/javascripts/merge_requests.js.coffee
@@ -21,7 +21,7 @@ class MergeRequest
this.initMergeWidget()
this.$('.show-all-commits').on 'click', =>
this.showAllCommits()
-
+
modal = $('#modal_merge_info').modal(show: false)
# Local jQuery finder
@@ -83,12 +83,12 @@ class MergeRequest
url: this.$('.nav-tabs .diffs-tab a').attr('href')
beforeSend: =>
this.$('.status').addClass 'loading'
-
complete: =>
@diffs_loaded = true
this.$('.status').removeClass 'loading'
-
- dataType: 'script'
+ success: (data) =>
+ this.$(".diffs").html(data.html)
+ dataType: 'json'
showAllCommits: ->
this.$('.first-commits').remove()
diff --git a/app/assets/javascripts/namespace_select.js.coffee b/app/assets/javascripts/namespace_select.js.coffee
new file mode 100644
index 00000000000..00d135d1449
--- /dev/null
+++ b/app/assets/javascripts/namespace_select.js.coffee
@@ -0,0 +1,24 @@
+$ ->
+ namespaceFormatResult = (namespace) ->
+ markup = "<div class='namespace-result'>"
+ markup += "<span class='namespace-kind'>" + namespace.kind + "</span>"
+ markup += "<span class='namespace-path'>" + namespace.path + "</span>"
+ markup += "</div>"
+ markup
+
+ formatSelection = (namespace) ->
+ namespace.kind + ": " + namespace.path
+
+ $('.ajax-namespace-select').each (i, select) ->
+ $(select).select2
+ placeholder: "Search for namespace"
+ multiple: $(select).hasClass('multiselect')
+ minimumInputLength: 0
+ query: (query) ->
+ Api.namespaces query.term, (namespaces) ->
+ data = { results: namespaces }
+ query.callback(data)
+
+ dropdownCssClass: "ajax-namespace-dropdown"
+ formatResult: namespaceFormatResult
+ formatSelection: formatSelection
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
deleted file mode 100644
index 5225623c1f0..00000000000
--- a/app/assets/javascripts/notes.js
+++ /dev/null
@@ -1,580 +0,0 @@
-var NoteList = {
- id: null,
- notes_path: null,
- target_params: null,
- target_id: 0,
- target_type: null,
-
- init: function(tid, tt, path) {
- NoteList.notes_path = path + ".js";
- NoteList.target_id = tid;
- NoteList.target_type = tt;
- NoteList.target_params = "target_type=" + NoteList.target_type + "&target_id=" + NoteList.target_id;
-
- NoteList.setupMainTargetNoteForm();
-
- // get initial set of notes
- NoteList.getContent();
-
- // Unbind events to prevent firing twice
- $(document).off("click", ".js-add-diff-note-button");
- $(document).off("click", ".js-discussion-reply-button");
- $(document).off("click", ".js-note-preview-button");
- $(document).off("click", ".js-note-attachment-input");
- $(document).off("click", ".js-close-discussion-note-form");
- $(document).off("click", ".js-note-delete");
- $(document).off("click", ".js-note-edit");
- $(document).off("click", ".js-note-edit-cancel");
- $(document).off("click", ".js-note-attachment-delete");
- $(document).off("click", ".js-choose-note-attachment-button");
- $(document).off("click", ".js-show-outdated-discussion");
-
- $(document).off("ajax:complete", ".js-main-target-form");
-
-
- // add a new diff note
- $(document).on("click",
- ".js-add-diff-note-button",
- NoteList.addDiffNote);
-
- // reply to diff/discussion notes
- $(document).on("click",
- ".js-discussion-reply-button",
- NoteList.replyToDiscussionNote);
-
- // setup note preview
- $(document).on("click",
- ".js-note-preview-button",
- NoteList.previewNote);
-
- // update the file name when an attachment is selected
- $(document).on("change",
- ".js-note-attachment-input",
- NoteList.updateFormAttachment);
-
- // hide diff note form
- $(document).on("click",
- ".js-close-discussion-note-form",
- NoteList.removeDiscussionNoteForm);
-
- // remove a note (in general)
- $(document).on("click",
- ".js-note-delete",
- NoteList.removeNote);
-
- // show the edit note form
- $(document).on("click",
- ".js-note-edit",
- NoteList.showEditNoteForm);
-
- // cancel note editing
- $(document).on("click",
- ".note-edit-cancel",
- NoteList.cancelNoteEdit);
-
- // delete note attachment
- $(document).on("click",
- ".js-note-attachment-delete",
- NoteList.deleteNoteAttachment);
-
- // update the note after editing
- $(document).on("ajax:complete",
- "form.edit_note",
- NoteList.updateNote);
-
- // reset main target form after submit
- $(document).on("ajax:complete",
- ".js-main-target-form",
- NoteList.resetMainTargetForm);
-
-
- $(document).on("click",
- ".js-choose-note-attachment-button",
- NoteList.chooseNoteAttachment);
-
- $(document).on("click",
- ".js-show-outdated-discussion",
- function(e) { $(this).next('.outdated-discussion').show(); e.preventDefault() });
- },
-
-
- /**
- * When clicking on buttons
- */
-
- /**
- * Called when clicking on the "add a comment" button on the side of a diff line.
- *
- * Inserts a temporary row for the form below the line.
- * Sets up the form and shows it.
- */
- addDiffNote: function(e) {
- e.preventDefault();
-
- // find the form
- var form = $(".js-new-note-form");
- var row = $(this).closest("tr");
- var nextRow = row.next();
-
- // does it already have notes?
- if (nextRow.is(".notes_holder")) {
- $.proxy(NoteList.replyToDiscussionNote,
- nextRow.find(".js-discussion-reply-button")
- ).call();
- } else {
- // add a notes row and insert the form
- row.after('<tr class="notes_holder js-temp-notes-holder"><td class="notes_line" colspan="2"></td><td class="notes_content"></td></tr>');
- form.clone().appendTo(row.next().find(".notes_content"));
-
- // show the form
- NoteList.setupDiscussionNoteForm($(this), row.next().find("form"));
- }
- },
-
- /**
- * Called when clicking the "Choose File" button.
- *
- * Opens the file selection dialog.
- */
- chooseNoteAttachment: function() {
- var form = $(this).closest("form");
-
- form.find(".js-note-attachment-input").click();
- },
-
- /**
- * Shows the note preview.
- *
- * Lets the server render GFM into Html and displays it.
- *
- * Note: uses the Toggler behavior to toggle preview/edit views/buttons
- */
- previewNote: function(e) {
- e.preventDefault();
-
- var form = $(this).closest("form");
- var preview = form.find('.js-note-preview');
- var noteText = form.find('.js-note-text').val();
-
- if(noteText.trim().length === 0) {
- preview.text('Nothing to preview.');
- } else {
- preview.text('Loading...');
- $.post($(this).data('url'), {note: noteText})
- .success(function(previewData) {
- preview.html(previewData);
- });
- }
- },
-
- /**
- * Called in response to "cancel" on a diff note form.
- *
- * Shows the reply button again.
- * Removes the form and if necessary it's temporary row.
- */
- removeDiscussionNoteForm: function() {
- var form = $(this).closest("form");
- var row = form.closest("tr");
-
- // show the reply button (will only work for replies)
- form.prev(".js-discussion-reply-button").show();
-
- if (row.is(".js-temp-notes-holder")) {
- // remove temporary row for diff lines
- row.remove();
- } else {
- // only remove the form
- form.remove();
- }
- },
-
- /**
- * Called in response to deleting a note of any kind.
- *
- * Removes the actual note from view.
- * Removes the whole discussion if the last note is being removed.
- */
- removeNote: function() {
- var note = $(this).closest(".note");
- var notes = note.closest(".notes");
-
- // check if this is the last note for this line
- if (notes.find(".note").length === 1) {
- // for discussions
- notes.closest(".discussion").remove();
-
- // for diff lines
- notes.closest("tr").remove();
- }
-
- note.remove();
- NoteList.updateVotes();
- },
-
- /**
- * Called in response to clicking the edit note link
- *
- * Replaces the note text with the note edit form
- * Adds a hidden div with the original content of the note to fill the edit note form with
- * if the user cancels
- */
- showEditNoteForm: function(e) {
- e.preventDefault();
- var note = $(this).closest(".note");
- note.find(".note-text").hide();
-
- // Show the attachment delete link
- note.find(".js-note-attachment-delete").show();
-
- GitLab.GfmAutoComplete.setup();
-
- var form = note.find(".note-edit-form");
- form.show();
-
- var textarea = form.find("textarea");
- var p = $("<p></p>").text(textarea.val());
- var hidden_div = $('<div class="note-original-content"></div>').append(p);
- form.append(hidden_div);
- hidden_div.hide();
- textarea.focus();
- },
-
- /**
- * Called in response to clicking the cancel button when editing a note
- *
- * Resets and hides the note editing form
- */
- cancelNoteEdit: function(e) {
- e.preventDefault();
- var note = $(this).closest(".note");
- NoteList.resetNoteEditing(note);
- },
-
-
- /**
- * Called in response to clicking the delete attachment link
- *
- * Removes the attachment wrapper view, including image tag if it exists
- * Resets the note editing form
- */
- deleteNoteAttachment: function() {
- var note = $(this).closest(".note");
- note.find(".note-attachment").remove();
- NoteList.resetNoteEditing(note);
- NoteList.rewriteTimestamp(note.find(".note-last-update"));
- },
-
-
- /**
- * Called when clicking on the "reply" button for a diff line.
- *
- * Shows the note form below the notes.
- */
- replyToDiscussionNote: function() {
- // find the form
- var form = $(".js-new-note-form");
-
- // hide reply button
- $(this).hide();
- // insert the form after the button
- form.clone().insertAfter($(this));
-
- // show the form
- NoteList.setupDiscussionNoteForm($(this), $(this).next("form"));
- },
-
-
- /**
- * Helper for inserting and setting up note forms.
- */
-
-
- /**
- * Called in response to creating a note failing validation.
- *
- * Adds the rendered errors to the respective form.
- * If "discussionId" is null or undefined, the main target form is assumed.
- */
- errorsOnForm: function(errorsHtml, discussionId) {
- // find the form
- if (discussionId) {
- var form = $("form[rel='"+discussionId+"']");
- } else {
- var form = $(".js-main-target-form");
- }
-
- form.find(".js-errors").remove();
- form.prepend(errorsHtml);
-
- form.find(".js-note-text").focus();
- },
-
-
- /**
- * Shows the diff/discussion form and does some setup on it.
- *
- * Sets some hidden fields in the form.
- *
- * Note: dataHolder must have the "discussionId", "lineCode", "noteableType"
- * and "noteableId" data attributes set.
- */
- setupDiscussionNoteForm: function(dataHolder, form) {
- // setup note target
- form.attr("rel", dataHolder.data("discussionId"));
- form.find("#note_commit_id").val(dataHolder.data("commitId"));
- form.find("#note_line_code").val(dataHolder.data("lineCode"));
- form.find("#note_noteable_type").val(dataHolder.data("noteableType"));
- form.find("#note_noteable_id").val(dataHolder.data("noteableId"));
-
- NoteList.setupNoteForm(form);
-
- form.find(".js-note-text").focus();
- },
-
- /**
- * Shows the main form and does some setup on it.
- *
- * Sets some hidden fields in the form.
- */
- setupMainTargetNoteForm: function() {
- // find the form
- var form = $(".js-new-note-form");
- // insert the form after the button
- form.clone().replaceAll($(".js-main-target-form"));
-
- form = form.prev("form");
-
- // show the form
- NoteList.setupNoteForm(form);
-
- // fix classes
- form.removeClass("js-new-note-form");
- form.addClass("js-main-target-form");
-
- // remove unnecessary fields and buttons
- form.find("#note_line_code").remove();
- form.find(".js-close-discussion-note-form").remove();
- },
-
- /**
- * General note form setup.
- *
- * * deactivates the submit button when text is empty
- * * hides the preview button when text is empty
- * * setup GFM auto complete
- * * show the form
- */
- setupNoteForm: function(form) {
- disableButtonIfEmptyField(form.find(".js-note-text"), form.find(".js-comment-button"));
-
- form.removeClass("js-new-note-form");
-
- // setup preview buttons
- form.find(".js-note-edit-button, .js-note-preview-button")
- .tooltip({ placement: 'left' });
-
- previewButton = form.find(".js-note-preview-button");
- form.find(".js-note-text").on("input", function() {
- if ($(this).val().trim() !== "") {
- previewButton.removeClass("turn-off").addClass("turn-on");
- } else {
- previewButton.removeClass("turn-on").addClass("turn-off");
- }
- });
-
- // remove notify commit author checkbox for non-commit notes
- if (form.find("#note_noteable_type").val() !== "Commit") {
- form.find(".js-notify-commit-author").remove();
- }
-
- GitLab.GfmAutoComplete.setup();
-
- form.show();
- },
-
-
- /**
- * Handle loading the initial set of notes.
- * And set up loading more notes when scrolling to the bottom of the page.
- */
-
-
- /**
- * Gets an initial set of notes.
- */
- getContent: function() {
- $.ajax({
- url: NoteList.notes_path,
- data: NoteList.target_params,
- complete: function(){ $('.js-notes-busy').removeClass("loading")},
- beforeSend: function() { $('.js-notes-busy').addClass("loading") },
- dataType: "script"
- });
- },
-
- /**
- * Called in response to getContent().
- * Replaces the content of #notes-list with the given html.
- */
- setContent: function(newNoteIds, html) {
- $("#notes-list").html(html);
- },
-
-
- /**
- * Adds a single common note to #notes-list.
- */
- appendNewNote: function(id, html) {
- $("#notes-list").append(html);
- NoteList.updateVotes();
- },
-
- /**
- * Adds a single discussion note to #notes-list.
- *
- * Also removes the corresponding form.
- */
- appendNewDiscussionNote: function(discussionId, diffRowHtml, noteHtml) {
- var form = $("form[rel='"+discussionId+"']");
- var row = form.closest("tr");
-
- // is this the first note of discussion?
- if (row.is(".js-temp-notes-holder")) {
- // insert the note and the reply button after the temp row
- row.after(diffRowHtml);
- // remove the note (will be added again below)
- row.next().find(".note").remove();
- }
-
- // append new note to all matching discussions
- $(".notes[rel='"+discussionId+"']").append(noteHtml);
-
- // cleanup after successfully creating a diff/discussion note
- $.proxy(NoteList.removeDiscussionNoteForm, form).call();
- },
-
- /**
- * Called in response the main target form has been successfully submitted.
- *
- * Removes any errors.
- * Resets text and preview.
- * Resets buttons.
- */
- resetMainTargetForm: function(){
- var form = $(this);
-
- // remove validation errors
- form.find(".js-errors").remove();
-
- // reset text and preview
- var previewContainer = form.find(".js-toggler-container.note_text_and_preview");
- if (previewContainer.is(".on")) {
- previewContainer.removeClass("on");
- }
- form.find(".js-note-text").val("").trigger("input");
- },
-
- /**
- * Called after an attachment file has been selected.
- *
- * Updates the file name for the selected attachment.
- */
- updateFormAttachment: function() {
- var form = $(this).closest("form");
-
- // get only the basename
- var filename = $(this).val().replace(/^.*[\\\/]/, '');
-
- form.find(".js-attachment-filename").text(filename);
- },
-
- /**
- * Recalculates the votes and updates them (if they are displayed at all).
- *
- * Assumes all relevant notes are displayed (i.e. there are no more notes to
- * load via getMore()).
- * Might produce inaccurate results when not all notes have been loaded and a
- * recalculation is triggered (e.g. when deleting a note).
- */
- updateVotes: function() {
- var votes = $("#votes .votes");
- var notes = $("#notes-list .note .vote");
-
- // only update if there is a vote display
- if (votes.size()) {
- var upvotes = notes.filter(".upvote").size();
- var downvotes = notes.filter(".downvote").size();
- var votesCount = upvotes + downvotes;
- var upvotesPercent = votesCount ? (100.0 / votesCount * upvotes) : 0;
- var downvotesPercent = votesCount ? (100.0 - upvotesPercent) : 0;
-
- // change vote bar lengths
- votes.find(".bar-success").css("width", upvotesPercent+"%");
- votes.find(".bar-danger").css("width", downvotesPercent+"%");
- // replace vote numbers
- votes.find(".upvotes").text(votes.find(".upvotes").text().replace(/\d+/, upvotes));
- votes.find(".downvotes").text(votes.find(".downvotes").text().replace(/\d+/, downvotes));
- }
- },
-
- /**
- * Called in response to the edit note form being submitted
- *
- * Updates the current note field.
- * Hides the edit note form
- */
- updateNote: function(e, xhr, settings) {
- response = JSON.parse(xhr.responseText);
- if (response.success) {
- var note_li = $("#note_" + response.id);
- var note_text = note_li.find(".note-text");
- note_text.html(response.note).show();
-
- var note_form = note_li.find(".note-edit-form");
- note_form.hide();
- note_form.find(".btn-save").enableButton();
-
- // Update the "Edited at xxx label" on the note to show it's just been updated
- NoteList.rewriteTimestamp(note_li.find(".note-last-update"));
- }
- },
-
- /**
- * Called in response to the 'cancel note' link clicked, or after deleting a note attachment
- *
- * Hides the edit note form and shows the note
- * Resets the edit note form textarea with the original content of the note
- */
- resetNoteEditing: function(note) {
- note.find(".note-text").show();
-
- // Hide the attachment delete link
- note.find(".js-note-attachment-delete").hide();
-
- // Put the original content of the note back into the edit form textarea
- var form = note.find(".note-edit-form");
- var original_content = form.find(".note-original-content");
- form.find("textarea").val(original_content.text());
- original_content.remove();
-
- note.find(".note-edit-form").hide();
- },
-
- /**
- * Utility function to generate new timestamp text for a note
- *
- */
- rewriteTimestamp: function(element) {
- // Strip all newlines from the existing timestamp
- var ts = element.text().replace(/\n/g, ' ').trim();
-
- // If the timestamp already has '(Edited xxx ago)' text, remove it
- ts = ts.replace(new RegExp("\\(Edited [A-Za-z0-9 ]+\\)$", "gi"), "");
-
- // Append "(Edited just now)"
- ts = (ts + " <small>(Edited just now)</small>");
-
- element.html(ts);
- }
-};
diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee
new file mode 100644
index 00000000000..6501cf38a89
--- /dev/null
+++ b/app/assets/javascripts/notes.js.coffee
@@ -0,0 +1,432 @@
+class Notes
+ @interval: null
+
+ constructor: (notes_url, note_ids) ->
+ @notes_url = notes_url
+ @notes_url = gon.relative_url_root + @notes_url if gon.relative_url_root?
+ @note_ids = note_ids
+ @initRefresh()
+ @setupMainTargetNoteForm()
+ @cleanBinding()
+ @addBinding()
+
+ addBinding: ->
+ # add note to UI after creation
+ $(document).on "ajax:success", ".js-main-target-form", @addNote
+ $(document).on "ajax:success", ".js-discussion-note-form", @addDiscussionNote
+
+ # change note in UI after update
+ $(document).on "ajax:success", "form.edit_note", @updateNote
+
+ # Edit note link
+ $(document).on "click", ".js-note-edit", @showEditForm
+ $(document).on "click", ".note-edit-cancel", @cancelEdit
+
+ # remove a note (in general)
+ $(document).on "click", ".js-note-delete", @removeNote
+
+ # delete note attachment
+ $(document).on "click", ".js-note-attachment-delete", @removeAttachment
+
+ # Preview button
+ $(document).on "click", ".js-note-preview-button", @previewNote
+
+ # reset main target form after submit
+ $(document).on "ajax:complete", ".js-main-target-form", @resetMainTargetForm
+
+ # attachment button
+ $(document).on "click", ".js-choose-note-attachment-button", @chooseNoteAttachment
+
+ # reply to diff/discussion notes
+ $(document).on "click", ".js-discussion-reply-button", @replyToDiscussionNote
+
+ # add diff note
+ $(document).on "click", ".js-add-diff-note-button", @addDiffNote
+
+ # hide diff note form
+ $(document).on "click", ".js-close-discussion-note-form", @cancelDiscussionForm
+
+ cleanBinding: ->
+ $(document).off "ajax:success", ".js-main-target-form"
+ $(document).off "ajax:success", ".js-discussion-note-form"
+ $(document).off "ajax:success", "form.edit_note"
+ $(document).off "click", ".js-note-edit"
+ $(document).off "click", ".note-edit-cancel"
+ $(document).off "click", ".js-note-delete"
+ $(document).off "click", ".js-note-attachment-delete"
+ $(document).off "click", ".js-note-preview-button"
+ $(document).off "ajax:complete", ".js-main-target-form"
+ $(document).off "click", ".js-choose-note-attachment-button"
+ $(document).off "click", ".js-discussion-reply-button"
+ $(document).off "click", ".js-add-diff-note-button"
+
+
+ initRefresh: ->
+ clearInterval(Notes.interval)
+ Notes.interval = setInterval =>
+ @refresh()
+ , 15000
+
+ refresh: ->
+ @getContent()
+
+ getContent: ->
+ $.ajax
+ url: @notes_url
+ dataType: "json"
+ success: (data) =>
+ notes = data.notes
+ $.each notes, (i, note) =>
+ @renderNote(note)
+
+
+ ###
+ Render note in main comments area.
+
+ Note: for rendering inline notes use renderDiscussionNote
+ ###
+ renderNote: (note) ->
+ # render note if it not present in loaded list
+ # or skip if rendered
+ if @isNewNote(note)
+ @note_ids.push(note.id)
+ $('ul.main-notes-list').append(note.html)
+
+
+ ###
+ Check if note does not exists on page
+ ###
+ isNewNote: (note) ->
+ $.inArray(note.id, @note_ids) == -1
+
+
+ ###
+ Render note in discussion area.
+
+ Note: for rendering inline notes use renderDiscussionNote
+ ###
+ renderDiscussionNote: (note) ->
+ @note_ids.push(note.id)
+ form = $("form[rel='" + note.discussion_id + "']")
+ row = form.closest("tr")
+
+ # is this the first note of discussion?
+ if row.is(".js-temp-notes-holder")
+ # insert the note and the reply button after the temp row
+ row.after note.discussion_html
+
+ # remove the note (will be added again below)
+ row.next().find(".note").remove()
+
+ # append new note to all matching discussions
+ $(".notes[rel='" + note.discussion_id + "']").append note.html
+
+ # cleanup after successfully creating a diff/discussion note
+ @removeDiscussionNoteForm(form)
+
+ ###
+ Shows the note preview.
+
+ Lets the server render GFM into Html and displays it.
+
+ Note: uses the Toggler behavior to toggle preview/edit views/buttons
+ ###
+ previewNote: (e) ->
+ e.preventDefault()
+ form = $(this).closest("form")
+ preview = form.find(".js-note-preview")
+ noteText = form.find(".js-note-text").val()
+ if noteText.trim().length is 0
+ preview.text "Nothing to preview."
+ else
+ preview.text "Loading..."
+ $.post($(this).data("url"),
+ note: noteText
+ ).success (previewData) ->
+ preview.html previewData
+
+ ###
+ Called in response the main target form has been successfully submitted.
+
+ Removes any errors.
+ Resets text and preview.
+ Resets buttons.
+ ###
+ resetMainTargetForm: ->
+ form = $(".js-main-target-form")
+
+ # remove validation errors
+ form.find(".js-errors").remove()
+
+ # reset text and preview
+ previewContainer = form.find(".js-toggler-container.note_text_and_preview")
+ previewContainer.removeClass "on" if previewContainer.is(".on")
+ form.find(".js-note-text").val("").trigger "input"
+
+ ###
+ Called when clicking the "Choose File" button.
+
+ Opens the file selection dialog.
+ ###
+ chooseNoteAttachment: ->
+ form = $(this).closest("form")
+ form.find(".js-note-attachment-input").click()
+
+ ###
+ Shows the main form and does some setup on it.
+
+ Sets some hidden fields in the form.
+ ###
+ setupMainTargetNoteForm: ->
+
+ # find the form
+ form = $(".js-new-note-form")
+
+ # insert the form after the button
+ form.clone().replaceAll $(".js-main-target-form")
+ form = form.prev("form")
+
+ # show the form
+ @setupNoteForm(form)
+
+ # fix classes
+ form.removeClass "js-new-note-form"
+ form.addClass "js-main-target-form"
+
+ # remove unnecessary fields and buttons
+ form.find("#note_line_code").remove()
+ form.find(".js-close-discussion-note-form").remove()
+
+ ###
+ General note form setup.
+
+ deactivates the submit button when text is empty
+ hides the preview button when text is empty
+ setup GFM auto complete
+ show the form
+ ###
+ setupNoteForm: (form) ->
+ disableButtonIfEmptyField form.find(".js-note-text"), form.find(".js-comment-button")
+ form.removeClass "js-new-note-form"
+
+ # setup preview buttons
+ form.find(".js-note-edit-button, .js-note-preview-button").tooltip placement: "left"
+ previewButton = form.find(".js-note-preview-button")
+ form.find(".js-note-text").on "input", ->
+ if $(this).val().trim() isnt ""
+ previewButton.removeClass("turn-off").addClass "turn-on"
+ else
+ previewButton.removeClass("turn-on").addClass "turn-off"
+
+
+ # remove notify commit author checkbox for non-commit notes
+ form.find(".js-notify-commit-author").remove() if form.find("#note_noteable_type").val() isnt "Commit"
+ GitLab.GfmAutoComplete.setup()
+ form.show()
+
+
+ ###
+ Called in response to the new note form being submitted
+
+ Adds new note to list.
+ ###
+ addNote: (xhr, note, status) =>
+ @renderNote(note)
+ @updateVotes()
+
+ ###
+ Called in response to the new note form being submitted
+
+ Adds new note to list.
+ ###
+ addDiscussionNote: (xhr, note, status) =>
+ @renderDiscussionNote(note)
+
+ ###
+ Called in response to the edit note form being submitted
+
+ Updates the current note field.
+ ###
+ updateNote: (xhr, note, status) =>
+ note_li = $("#note_" + note.id)
+ note_li.replaceWith(note.html)
+
+ ###
+ Called in response to clicking the edit note link
+
+ Replaces the note text with the note edit form
+ Adds a hidden div with the original content of the note to fill the edit note form with
+ if the user cancels
+ ###
+ showEditForm: (e) ->
+ e.preventDefault()
+ note = $(this).closest(".note")
+ note.find(".note-text").hide()
+
+ # Show the attachment delete link
+ note.find(".js-note-attachment-delete").show()
+ GitLab.GfmAutoComplete.setup()
+ form = note.find(".note-edit-form")
+ form.show()
+ form.find("textarea").focus()
+
+ ###
+ Called in response to clicking the edit note link
+
+ Hides edit form
+ ###
+ cancelEdit: (e) ->
+ e.preventDefault()
+ note = $(this).closest(".note")
+ note.find(".note-text").show()
+ note.find(".js-note-attachment-delete").hide()
+ note.find(".note-edit-form").hide()
+
+ ###
+ Called in response to deleting a note of any kind.
+
+ Removes the actual note from view.
+ Removes the whole discussion if the last note is being removed.
+ ###
+ removeNote: ->
+ note = $(this).closest(".note")
+ notes = note.closest(".notes")
+
+ # check if this is the last note for this line
+ if notes.find(".note").length is 1
+
+ # for discussions
+ notes.closest(".discussion").remove()
+
+ # for diff lines
+ notes.closest("tr").remove()
+
+ note.remove()
+
+ ###
+ Called in response to clicking the delete attachment link
+
+ Removes the attachment wrapper view, including image tag if it exists
+ Resets the note editing form
+ ###
+ removeAttachment: ->
+ note = $(this).closest(".note")
+ note.find(".note-attachment").remove()
+ note.find(".note-text").show()
+ note.find(".js-note-attachment-delete").hide()
+ note.find(".note-edit-form").hide()
+
+ ###
+ Called when clicking on the "reply" button for a diff line.
+
+ Shows the note form below the notes.
+ ###
+ replyToDiscussionNote: (e) =>
+ form = $(".js-new-note-form")
+ replyLink = $(e.target)
+ replyLink.hide()
+
+ # insert the form after the button
+ form.clone().insertAfter replyLink
+
+ # show the form
+ @setupDiscussionNoteForm(replyLink, replyLink.next("form"))
+
+ ###
+ Shows the diff or discussion form and does some setup on it.
+
+ Sets some hidden fields in the form.
+
+ Note: dataHolder must have the "discussionId", "lineCode", "noteableType"
+ and "noteableId" data attributes set.
+ ###
+ setupDiscussionNoteForm: (dataHolder, form) =>
+ # setup note target
+ form.attr "rel", dataHolder.data("discussionId")
+ form.find("#note_commit_id").val dataHolder.data("commitId")
+ form.find("#note_line_code").val dataHolder.data("lineCode")
+ form.find("#note_noteable_type").val dataHolder.data("noteableType")
+ form.find("#note_noteable_id").val dataHolder.data("noteableId")
+ @setupNoteForm form
+ form.find(".js-note-text").focus()
+ form.addClass "js-discussion-note-form"
+
+ ###
+ General note form setup.
+
+ deactivates the submit button when text is empty
+ hides the preview button when text is empty
+ setup GFM auto complete
+ show the form
+ ###
+ setupNoteForm: (form) =>
+ disableButtonIfEmptyField form.find(".js-note-text"), form.find(".js-comment-button")
+ form.removeClass "js-new-note-form"
+ form.removeClass "js-new-note-form"
+ GitLab.GfmAutoComplete.setup()
+
+ # setup preview buttons
+ previewButton = form.find(".js-note-preview-button")
+ form.find(".js-note-text").on "input", ->
+ if $(this).val().trim() isnt ""
+ previewButton.removeClass("turn-off").addClass "turn-on"
+ else
+ previewButton.removeClass("turn-on").addClass "turn-off"
+
+ form.show()
+
+ ###
+ Called when clicking on the "add a comment" button on the side of a diff line.
+
+ Inserts a temporary row for the form below the line.
+ Sets up the form and shows it.
+ ###
+ addDiffNote: (e) =>
+ e.preventDefault()
+ link = e.target
+ form = $(".js-new-note-form")
+ row = $(link).closest("tr")
+ nextRow = row.next()
+
+ # does it already have notes?
+ if nextRow.is(".notes_holder")
+ replyButton = nextRow.find(".js-discussion-reply-button")
+ if replyButton.length > 0
+ $.proxy(@replyToDiscussionNote, replyButton).call()
+ else
+ # add a notes row and insert the form
+ row.after "<tr class=\"notes_holder js-temp-notes-holder\"><td class=\"notes_line\" colspan=\"2\"></td><td class=\"notes_content\"></td></tr>"
+ form.clone().appendTo row.next().find(".notes_content")
+
+ # show the form
+ @setupDiscussionNoteForm $(link), row.next().find("form")
+
+ ###
+ Called in response to "cancel" on a diff note form.
+
+ Shows the reply button again.
+ Removes the form and if necessary it's temporary row.
+ ###
+ removeDiscussionNoteForm: (form)->
+ row = form.closest("tr")
+
+ # show the reply button (will only work for replies)
+ form.prev(".js-discussion-reply-button").show()
+ if row.is(".js-temp-notes-holder")
+ # remove temporary row for diff lines
+ row.remove()
+ else
+ # only remove the form
+ form.remove()
+
+
+ cancelDiscussionForm: (e) =>
+ e.preventDefault()
+ form = $(".js-new-note-form")
+ form = $(e.target).closest(".js-discussion-note-form")
+ @removeDiscussionNoteForm(form)
+
+ updateVotes: ->
+ (new NotesVotes).updateVotes()
+
+@Notes = Notes
diff --git a/app/assets/javascripts/notes_votes.js.coffee b/app/assets/javascripts/notes_votes.js.coffee
new file mode 100644
index 00000000000..b31eb9ac9de
--- /dev/null
+++ b/app/assets/javascripts/notes_votes.js.coffee
@@ -0,0 +1,22 @@
+class NotesVotes
+ updateVotes: ->
+ votes = $("#votes .votes")
+ notes = $("#notes-list .note .vote")
+
+ # only update if there is a vote display
+ if votes.size()
+ upvotes = notes.filter(".upvote").size()
+ downvotes = notes.filter(".downvote").size()
+ votesCount = upvotes + downvotes
+ upvotesPercent = (if votesCount then (100.0 / votesCount * upvotes) else 0)
+ downvotesPercent = (if votesCount then (100.0 - upvotesPercent) else 0)
+
+ # change vote bar lengths
+ votes.find(".bar-success").css "width", upvotesPercent + "%"
+ votes.find(".bar-danger").css "width", downvotesPercent + "%"
+
+ # replace vote numbers
+ votes.find(".upvotes").text votes.find(".upvotes").text().replace(/\d+/, upvotes)
+ votes.find(".downvotes").text votes.find(".downvotes").text().replace(/\d+/, downvotes)
+
+@NotesVotes = NotesVotes
diff --git a/app/assets/javascripts/pager.js.coffee b/app/assets/javascripts/pager.js.coffee
index 5bd11d273a7..1f763e8b956 100644
--- a/app/assets/javascripts/pager.js.coffee
+++ b/app/assets/javascripts/pager.js.coffee
@@ -19,8 +19,9 @@
data: "limit=" + @limit + "&offset=" + @offset
complete: ->
$(".loading").hide()
-
- dataType: "script"
+ success: (data) ->
+ Pager.append(data.count, data.html)
+ dataType: "json"
append: (count, html) ->
$(".content_list").append html
diff --git a/app/assets/javascripts/project.js.coffee b/app/assets/javascripts/project.js.coffee
index bdb18574b6d..9a41ec7a0be 100644
--- a/app/assets/javascripts/project.js.coffee
+++ b/app/assets/javascripts/project.js.coffee
@@ -40,3 +40,9 @@ $ ->
# Ref switcher
$('.project-refs-select').on 'change', ->
$(@).parents('form').submit()
+
+ $('.hide-no-ssh-message').on 'click', (e) ->
+ path = '/'
+ $.cookie('hide_no_ssh_message', 'false', { path: path })
+ $(@).parents('.no-ssh-key-message').hide()
+ e.preventDefault()
diff --git a/app/assets/javascripts/stat_graph_contributors_graph.js.coffee b/app/assets/javascripts/stat_graph_contributors_graph.js.coffee
index 48443644169..834c7e5dab0 100644
--- a/app/assets/javascripts/stat_graph_contributors_graph.js.coffee
+++ b/app/assets/javascripts/stat_graph_contributors_graph.js.coffee
@@ -46,11 +46,7 @@ class window.ContributorsGraph
class window.ContributorsMasterGraph extends ContributorsGraph
constructor: (@data) ->
- if $(window).width() > 1214
- @width = 1100
- else
- @width = 870
-
+ @width = $('.container').width() - 70
@height = 200
@x = null
@y = null
@@ -88,7 +84,6 @@ class window.ContributorsMasterGraph extends ContributorsGraph
x(d.date)
).y0(@height).y1((d) ->
xa = d.commits = d.commits ? d.additions ? d.deletions
- console.log(xa)
y(xa)
).interpolate("basis")
create_brush: ->
@@ -124,11 +119,7 @@ class window.ContributorsMasterGraph extends ContributorsGraph
class window.ContributorsAuthorGraph extends ContributorsGraph
constructor: (@data) ->
- if $(window).width() > 1214
- @width = 490
- else
- @width = 380
-
+ @width = $('.container').width()/2 - 100
@height = 200
@x = null
@y = null
diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee
index 327667fede0..92cd3a9905b 100644
--- a/app/assets/javascripts/users_select.js.coffee
+++ b/app/assets/javascripts/users_select.js.coffee
@@ -6,12 +6,12 @@ $ ->
avatar = gon.gravatar_url
avatar = avatar.replace('%{hash}', md5(user.email))
avatar = avatar.replace('%{size}', '24')
- markup = "<div class='user-result'>"
- markup += "<div class='user-image'><img class='avatar s24' src='" + avatar + "'></div>"
- markup += "<div class='user-name'>" + user.name + "</div>"
- markup += "<div class='user-username'>" + user.username + "</div>"
- markup += "</div>"
- markup
+
+ "<div class='user-result'>
+ <div class='user-image'><img class='avatar s24' src='#{avatar}'></div>
+ <div class='user-name'>#{user.name}</div>
+ <div class='user-username'>#{user.username}</div>
+ </div>"
userFormatSelection = (user) ->
user.name
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index b1a23427add..ea5e556cd91 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -9,36 +9,41 @@
*= require_self
*/
+@import "main/variables.scss";
+@import "main/mixins.scss";
+@import "main/fonts.scss";
+
/**
- * GitLab bootstrap:
+ * Customized Twitter bootstrap
*/
-@import "gitlab_bootstrap.scss";
+@import 'gl_bootstrap';
+/**
+ * Font icons
+ *
+ */
+@import "font-awesome";
+
+/**
+ * Generic css (forms, nav etc):
+ */
+@import "generic.scss";
+
+
+/**
+ * Unrefactored css
+ */
@import "common.scss";
-@import "selects.scss";
-
-@import "sections/header.scss";
-@import "sections/nav.scss";
-@import "sections/commits.scss";
-@import "sections/issues.scss";
-@import "sections/projects.scss";
-@import "sections/snippets.scss";
-@import "sections/votes.scss";
-@import "sections/merge_requests.scss";
-@import "sections/graph.scss";
-@import "sections/events.scss";
-@import "sections/themes.scss";
-@import "sections/tree.scss";
-@import "sections/notes.scss";
-@import "sections/profile.scss";
-@import "sections/login.scss";
-@import "sections/editor.scss";
-@import "sections/admin.scss";
-@import "sections/wiki.scss";
-@import "sections/wall.scss";
-@import "sections/dashboard.scss";
-@import "sections/stat_graph.scss";
+
+/**
+ * Page specific styles (issues, projects etc):
+ */
+@import "sections.scss";
+
+/**
+ * Code ighlight
+ */
@import "highlight/white.scss";
@import "highlight/dark.scss";
@import "highlight/solarized_dark.scss";
@@ -57,4 +62,3 @@
* Styles for JS behaviors.
*/
@import "behaviors.scss";
-
diff --git a/app/assets/stylesheets/common.scss b/app/assets/stylesheets/common.scss
index dbd50b4dcc8..9bcdb8c183b 100644
--- a/app/assets/stylesheets/common.scss
+++ b/app/assets/stylesheets/common.scss
@@ -5,6 +5,8 @@ html {
/** LAYOUT **/
body {
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
margin-bottom: 20px;
}
@@ -117,18 +119,6 @@ p.time {
margin: 30px 3px 3px 2px;
}
-.search-holder {
- label, input {
- height: 30px;
- padding: 0;
- font-size: 14px;
- }
- label {
- line-height: 30px;
- color: #666;
- }
-}
-
.highlight {
text-shadow: none;
}
@@ -207,7 +197,7 @@ li.note {
}
.git_error_tips {
- @extend .span6;
+ @extend .col-md-6;
text-align: left;
margin-top: 40px;
pre {
@@ -220,7 +210,6 @@ li.note {
.error-message {
padding: 10px;
background: #C67;
- padding-left: 20px;
margin: 0;
color: #FFF;
@@ -228,8 +217,18 @@ li.note {
color: #fff;
text-decoration: underline;
}
- &.centered {
- text-align: center;
+}
+
+.no-ssh-key-message {
+ padding: 10px 0;
+ background: #C67;
+ margin: 0;
+ color: #FFF;
+ text-align: center;
+
+ a {
+ color: #fff;
+ text-decoration: underline;
}
}
@@ -317,11 +316,6 @@ img.emoji {
margin-bottom: 10px;
}
-.group-name {
- font-size: 14px;
- line-height: 24px;
-}
-
table {
td.permission-x {
background: #D9EDF7 !important;
@@ -341,4 +335,57 @@ table {
.navbar-gitlab .navbar-inner .nav > li .btn-sign-in {
@extend .btn-new;
padding: 5px 15px;
+ text-shadow: none;
+}
+
+.broadcast-message {
+ padding: 10px;
+ text-align: center;
+ background: #555;
+ color: #BBB;
+}
+
+.broadcast-message-preview {
+ @extend .broadcast-message;
+ margin-bottom: 20px;
+}
+
+.ajax-users-select {
+ width: 400px;
+
+ &.input-large {
+ width: 210px;
+ }
+
+ &.input-clamp {
+ max-width: 100%;
+ }
+}
+
+.user-result {
+ .user-image {
+ float: left;
+ }
+ .user-name {
+ }
+ .user-username {
+ color: #999;
+ }
+}
+
+.namespace-result {
+ .namespace-kind {
+ color: #AAA;
+ font-weight: normal;
+ }
+ .namespace-path {
+ margin-left: 10px;
+ font-weight: bolder;
+ }
+}
+
+.side-filters {
+ fieldset {
+ margin-bottom: 15px;
+ }
}
diff --git a/app/assets/stylesheets/generic.scss b/app/assets/stylesheets/generic.scss
new file mode 100644
index 00000000000..4c59358ced4
--- /dev/null
+++ b/app/assets/stylesheets/generic.scss
@@ -0,0 +1,12 @@
+@import "generic/avatar.scss";
+@import "generic/nav.scss";
+@import "generic/common.scss";
+@import "generic/typography.scss";
+@import "generic/buttons.scss";
+@import "generic/blocks.scss";
+@import "generic/ui_box.scss";
+@import "generic/issue_box.scss";
+@import "generic/files.scss";
+@import "generic/lists.scss";
+@import "generic/forms.scss";
+@import "generic/selects.scss";
diff --git a/app/assets/stylesheets/gitlab_bootstrap/avatar.scss b/app/assets/stylesheets/generic/avatar.scss
index 4f038b977e2..4f038b977e2 100644
--- a/app/assets/stylesheets/gitlab_bootstrap/avatar.scss
+++ b/app/assets/stylesheets/generic/avatar.scss
diff --git a/app/assets/stylesheets/generic/blocks.scss b/app/assets/stylesheets/generic/blocks.scss
new file mode 100644
index 00000000000..1cbd7439835
--- /dev/null
+++ b/app/assets/stylesheets/generic/blocks.scss
@@ -0,0 +1,4 @@
+.light-well {
+ background: #f9f9f9;
+ padding: 15px;
+}
diff --git a/app/assets/stylesheets/gitlab_bootstrap/buttons.scss b/app/assets/stylesheets/generic/buttons.scss
index 9c547cc1cea..347da1ad680 100644
--- a/app/assets/stylesheets/gitlab_bootstrap/buttons.scss
+++ b/app/assets/stylesheets/generic/buttons.scss
@@ -1,96 +1,106 @@
.btn {
display: inline-block;
- padding: 6px 12px;
margin-bottom: 0;
- font-size: 13px;
- line-height: $baseLineHeight;
+ font-weight: normal;
text-align: center;
vertical-align: middle;
cursor: pointer;
- border: 1px solid #BBB;
- color: $style_color;
- @include border-radius($baseBorderRadius);
- @include box-shadow(inset 0 1px 0 rgba(255,255,255,.2));
- @include linear-gradient(#f1f1f1, #e1e1e1);
- text-shadow: 0 1px 1px #FFF;
- text-decoration: none;
+ background-image: none;
+ border: 1px solid transparent;
+ white-space: nowrap;
+ padding: 6px 12px;
+ font-size: 13px;
+ line-height: 18px;
+ border-radius: 4px;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -o-user-select: none;
+ user-select: none;
+ color: #444444;
+ background-color: #fff;
+ border-color: #ccc;
+ text-shadow: none;
&.hover,
&:hover {
- color: $style_color;
- background: #f1f1f1;
- border-color: #AAA;
+ color: #444444;
text-decoration: none;
- @include linear-gradient(#fAfAfA, #f1f1f1);
+ background-color: #ebebeb;
+ border-color: #adadad;
}
&.focus,
&:focus {
+ color: #444444;
text-decoration: none;
- @include box-shadow(inset 0 2px 4px rgba(0,0,0,.15));
+ outline: thin dotted #333;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
}
&.active,
&:active {
- background-image: none;
outline: 0;
- text-decoration: none;
- @include box-shadow(inset 0 2px 4px rgba(0,0,0,.15));
+ background-image: none;
+ -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
}
&.disabled,
&[disabled] {
- cursor: default;
- background-image: none;
- @include opacity(65);
- @include box-shadow(none);
+ cursor: not-allowed;
+ pointer-events: none;
+ opacity: 0.65;
+ filter: alpha(opacity=65);
+ -webkit-box-shadow: none;
+ box-shadow: none;
}
&.btn-primary {
- color: #FFF;
- border-color: #189;
- text-shadow: 0 1px 1px #189;
- @include linear-gradient(#4AC, #289);
+ color: #ffffff;
+ background-color: #429bca;
+ border-color: #358ebd;
&.hover,
&:hover,
&.disabled,
&[disabled] {
- color: #FFF;
- background: #389;
+ color: #ffffff;
+ background-color: #3286b1;
+ border-color: #286e8e;
}
}
&.btn-success {
- color: #FFF;
- border-color: #1A1;
- text-shadow: 0 1px 1px #FFF;
- text-shadow: 0 1px 1px #181;
- @include linear-gradient(#62C452, #51a351);
+ color: #ffffff;
+ background-color: #5cb85c;
+ border-color: #4cae4c;
&.hover,
&:hover,
&.disabled,
&[disabled] {
- color: #FFF;
- background: #2A2;
+ color: #ffffff;
+ background-color: #47a447;
+ border-color: #398439;
}
}
&.btn-danger {
- color: #FFF;
- text-shadow: 0 1px 1px #811;
- border-color: #BD362F;
- @include linear-gradient(#EE5F5B, #BD362F);
+ color: #ffffff;
+ background-color: #d9534f;
+ border-color: #d43f3a;
&.hover,
&:hover,
&.disabled,
&[disabled] {
- color: #FFF;
- background: #A22;
+ color: #ffffff;
+ background-color: #d2322d;
+ border-color: #ac2925;
}
}
diff --git a/app/assets/stylesheets/gitlab_bootstrap/common.scss b/app/assets/stylesheets/generic/common.scss
index 2ca60178df7..a692628534b 100644
--- a/app/assets/stylesheets/gitlab_bootstrap/common.scss
+++ b/app/assets/stylesheets/generic/common.scss
@@ -1,10 +1,12 @@
/** COLORS **/
.cgray { color: gray }
+.clgray { color: #BBB }
.cred { color: #D12F19 }
.cgreen { color: #4a2 }
.cblue { color: #29A }
.cblack { color: #111 }
.cdark { color: #444 }
+.camber { color: #ffc000 }
.cwhite { color: #fff!important }
.bgred { background: #F2DEDE!important }
@@ -18,6 +20,7 @@
.append-right-10 { margin-right:10px }
.append-right-20 { margin-right:20px }
.append-bottom-10 { margin-bottom:10px }
+.append-bottom-15 { margin-bottom:15px }
.append-bottom-20 { margin-bottom:20px }
.inline { display: inline-block }
@@ -67,12 +70,6 @@
}
}
-.pagination ul > li > a, .pagination ul > li >span {
- @include linear-gradient(#f1f1f1, #e1e1e1);
- color: #333;
- text-shadow: 0 1px 1px #FFF;
-}
-
pre.well-pre {
border: 1px solid #EEE;
background: #f9f9f9;
@@ -88,23 +85,14 @@ pre.well-pre {
@include box-shadow(inset 0 2px 4px rgba(0,0,0,.15));
}
-.label {
- padding: 2px 4px;
- font-size: 12px;
- font-style: normal;
- font-weight: normal;
-}
-
/** Big Labels **/
.state-label {
font-size: 14px;
- padding: 5px 15px;
+ padding: 6px 25px;
text-align: center;
- float: right;
- position: relative;
- top: -5px;
@include border-radius(4px);
text-shadow: none;
+ margin-left: 10px;
&.state-label-green {
background: #4A4;
@@ -116,3 +104,18 @@ pre.well-pre {
color: #FFF;
}
}
+
+.dropdown-menu > li > a {
+ text-shadow: none;
+}
+
+.dropdown-menu > li > a:hover,
+.dropdown-menu > li > a:focus {
+ background: #29b;
+}
+
+.breadcrumb > li + li:before {
+ content: "/";
+ padding: 0;
+ color: #666;
+}
diff --git a/app/assets/stylesheets/gitlab_bootstrap/files.scss b/app/assets/stylesheets/generic/files.scss
index a286e530cd6..6607ae327e0 100644
--- a/app/assets/stylesheets/gitlab_bootstrap/files.scss
+++ b/app/assets/stylesheets/generic/files.scss
@@ -11,8 +11,8 @@
}
.file-title {
- border-bottom: 1px solid #bbb;
- @include bg-dark-gray-gradient;
+ background: #DDD;
+ border-bottom: 1px solid #CCC;
text-shadow: 0 1px 1px #fff;
margin: 0;
font-weight: normal;
@@ -20,7 +20,6 @@
text-align: left;
color: $style_color;
padding: 9px 10px;
- height: 18px;
.options {
float: right;
diff --git a/app/assets/stylesheets/generic/forms.scss b/app/assets/stylesheets/generic/forms.scss
new file mode 100644
index 00000000000..4f80a8a983b
--- /dev/null
+++ b/app/assets/stylesheets/generic/forms.scss
@@ -0,0 +1,49 @@
+input[type='search'].search-text-input {
+ background-image: url("icon-search.png");
+ background-repeat: no-repeat;
+ background-position: 10px;
+ padding-left: 25px;
+}
+
+input[type='text'].danger {
+ background: #F2DEDE!important;
+ border-color: #D66;
+ text-shadow: 0 1px 1px #fff
+}
+
+fieldset legend {
+ font-size: 16px;
+}
+
+.datetime-controls {
+ select {
+ width: 100px;
+ }
+}
+
+.form-actions {
+ padding: 17px 20px 18px;
+ margin-top: 18px;
+ margin-bottom: 18px;
+ background-color: whitesmoke;
+ border-top: 1px solid #e5e5e5;
+ padding-left: 17%;
+}
+
+label {
+ &.control-label {
+ @extend .col-sm-2;
+ }
+
+ &.inline-label {
+ margin: 0;
+ }
+}
+
+.inline-input-group {
+ width: 250px;
+}
+
+.input-mx-250 {
+ max-width: 250px;
+}
diff --git a/app/assets/stylesheets/generic/issue_box.scss b/app/assets/stylesheets/generic/issue_box.scss
new file mode 100644
index 00000000000..6330523ea22
--- /dev/null
+++ b/app/assets/stylesheets/generic/issue_box.scss
@@ -0,0 +1,46 @@
+/**
+ * Issue box:
+ * Huge block (one per page) for storing title, descripion and other information.
+ * Used for Issue#show page, MergeRequest#show page etc
+ *
+ * CLasses:
+ * .issue-box - Regular box
+ */
+
+.issue-box {
+ color: #666;
+ margin:20px 0;
+ background: #FAFAFA;
+ border: 1px solid #DDD;
+
+ .control-group {
+ margin-bottom: 0;
+ }
+
+ .title {
+ font-size: 20px;
+ font-weight: 500;
+ line-height: 28px;
+ margin: 0;
+ color: #444;
+ }
+
+ .context {
+ border: none;
+ background-color: #f5f5f5;
+ border: none;
+ border-top: 1px solid #eee;
+ }
+
+ .description {
+ border-top: 1px solid #eee;
+ }
+
+ .title, .context, .description {
+ padding: 15px;
+
+ .clearfix {
+ margin: 0;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/gitlab_bootstrap/lists.scss b/app/assets/stylesheets/generic/lists.scss
index 83066b5beec..245cccf855d 100644
--- a/app/assets/stylesheets/gitlab_bootstrap/lists.scss
+++ b/app/assets/stylesheets/generic/lists.scss
@@ -4,7 +4,9 @@
*/
.well-list {
margin: 0;
+ padding: 0;
list-style: none;
+
li {
padding: 10px;
min-height: 20px;
diff --git a/app/assets/stylesheets/gitlab_bootstrap/nav.scss b/app/assets/stylesheets/generic/nav.scss
index aa4cb1ed5fd..ec01988d52d 100644
--- a/app/assets/stylesheets/gitlab_bootstrap/nav.scss
+++ b/app/assets/stylesheets/generic/nav.scss
@@ -15,18 +15,21 @@
> li > a {
border-left: 4px solid #EEE;
padding: 12px;
+ color: #777;
}
> .active > a {
border-color: $primary_color;
- border-radius: 0;
- background: #F1F1F1;
- color: $style_color;
- font-weight: bold;
- text-shadow: 0 1px 1px #fff;
+ background: none;
+ color: #333;
+ font-weight: bolder;
+
+ &:hover {
+ background: none;
+ color: #333;
+ }
}
&.nav-stacked-menu {
- background: #FAFAFA;
li > a {
padding: 16px;
}
@@ -36,6 +39,7 @@
&.nav-pills-small {
> li > a {
padding: 8px 12px;
+ font-size: 12px;
}
}
}
@@ -49,38 +53,6 @@
*
*/
.nav-tabs > li > a, .nav-pills > li > a { color: $style_color; }
-.nav.nav-tabs {
- li {
- > a {
- padding: 8px 20px;
- margin-right: 7px;
- line-height: 20px;
- border-color: #EEE;
- color: #888;
- border-bottom: 1px solid #ddd;
- .badge {
- background-color: #eee;
- color: #888;
- text-shadow: 0 1px 1px #fff;
- }
- i[class^="icon-"] {
- line-height: 14px;
- }
- }
- &.active {
- > a {
- border-color: #CCC;
- border-bottom: 1px solid #fff;
- color: #333;
- font-weight: bold;
- }
- }
- }
-
- &.nav-small-tabs > li > a { padding: 6px 9px; }
-}
-
-
/**
* fix to keep tooltips position in top navigation bar
diff --git a/app/assets/stylesheets/generic/selects.scss b/app/assets/stylesheets/generic/selects.scss
new file mode 100644
index 00000000000..31a1d49e334
--- /dev/null
+++ b/app/assets/stylesheets/generic/selects.scss
@@ -0,0 +1,103 @@
+/** Chosen.js selectbox style override **/
+.chosen-container {
+ min-width: 100px;
+
+ .chosen-single {
+ height: 26px;
+ background: #EEE !important;
+ border: 1px solid #DDD !important;
+ @include box-shadow(none !important);
+ @include border-radius(4px !important);
+ }
+
+ .chosen-results li.highlighted {
+ background: #29b;
+ }
+
+ .chosen-drop {
+ margin-top: 10px;
+ border: 1px solid #DDD !important;
+ @include border-radius(4px !important);
+ }
+
+ .chosen-search input {
+ border: 1px solid #CCC !important;
+ @include box-shadow(none !important);
+ }
+}
+
+/** Select2 styling **/
+.select2-container .select2-choice {
+ @include bg-light-gray-gradient;
+}
+
+.select2-container .select2-choice div {
+ border: none;
+ background: none;
+}
+
+.select2-drop {
+ padding-top: 8px;
+}
+
+.select2-no-results, .select2-searching {
+ padding: 7px;
+ color: #666;
+}
+
+.chosen-container .chosen-single div b {
+ background-position-y: 0px !important;
+}
+
+.chosen-container .chosen-drop .chosen-search input {
+ background-position-y: -24px !important;
+}
+
+.chosen-compact {
+ max-width: 170px !important;
+}
+
+select {
+ &.chosen {
+ width: 100px;
+ }
+
+ &.chosen-sm {
+ width: 100px;
+ }
+}
+
+@media (min-width: $screen-sm-min) {
+ select {
+ &.chosen {
+ width: 150px;
+ }
+ &.chosen-sm {
+ width: 120px;
+ }
+ }
+}
+
+/* Medium devices (desktops, 992px and up) */
+@media (min-width: $screen-md-min) {
+ select {
+ &.chosen {
+ width: 170px;
+ }
+ &.chosen-sm {
+ width: 140px;
+ }
+ }
+}
+
+/* Large devices (large desktops, 1200px and up) */
+@media (min-width: $screen-lg-min) {
+ select {
+ &.chosen {
+ width: 200px;
+ }
+ &.chosen-sm {
+ width: 150px;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/gitlab_bootstrap/typography.scss b/app/assets/stylesheets/generic/typography.scss
index d3986556376..4490566d4e8 100644
--- a/app/assets/stylesheets/gitlab_bootstrap/typography.scss
+++ b/app/assets/stylesheets/generic/typography.scss
@@ -2,11 +2,6 @@
* Headers
*
*/
-h1, h2, h3, h4, h5, h6 {
- font-weight: 500;
- line-height: 1.1;
-}
-
h1.page-title {
@include page-title;
font-size: 28px;
@@ -99,6 +94,7 @@ a:focus {
background: #f5f5f5;
}
ul {
+ padding: 0;
margin: 0 0 9px 25px !important;
}
}
diff --git a/app/assets/stylesheets/gitlab_bootstrap/blocks.scss b/app/assets/stylesheets/generic/ui_box.scss
index 53de8067abc..7a977eae70d 100644
--- a/app/assets/stylesheets/gitlab_bootstrap/blocks.scss
+++ b/app/assets/stylesheets/generic/ui_box.scss
@@ -1,106 +1,55 @@
/**
- * ===================================
- * Contain UI block elements:
+ * UI box:
+ * Block element for separating information on page.
+ * Used for storing issues lists, grouped data.
+ * You can have multiple ui boxes on one page
+ *
+ * Classes:
* .ui-box - for any block & widgets
- * ===================================
- */
-
-/**
- * UI Block
+ * .ui-box.ui-box-small - same but with smaller title
+ * .ui-box.ui-box-danger - with red title
+ *
+ * Ex. 1: List
+ * .ui-box
+ * .title
+ * # title here
+ * %ul
+ * # content here
+ *
+ * Ex. 2: Block data
+ * .ui-box
+ * .title
+ * # title here
+ * .body
+ * # content here
*
*/
+
.ui-box {
background: #FFF;
margin-bottom: 20px;
border: 1px solid #DDD;
word-wrap: break-word;
- &.small-box {
- margin-bottom: 10px;
-
- .title {
- font-size: 13px;
- line-height: 30px;
-
- a {
- color: #666;
- &:hover {
- text-decoration: underline;
- }
- }
- }
- }
-
- &.ui-box-show {
- color: #666;
- margin:20px 0;
- background: #FFF;
- box-shadow: inset 0 1px 0 #fff, 0 1px 5px #f1f1f1;
- @include linear-gradient(#fafafa, #f1f1f1);
-
- .control-group {
- margin-bottom: 0;
- }
- }
-
- &.ui-box-danger {
- .title {
- @include linear-gradient(#F26E5E, #bd362f);
- color: #fff;
- text-shadow: 0 1px 1px #900;
- font-weight: bold;
- }
+ img {
+ max-width: 100%;
}
- img { max-width: 100%; }
-
pre {
code {
background: none !important;
}
}
- .ui-box-head,
- .ui-box-body,
- .ui-box-bottom {
- padding: 15px;
-
- .clearfix {
- margin: 0;
- }
- }
-
- .ui-box-head {
- .box-title {
- font-size: 18px;
- font-weight: normal;
- line-height: 28px;
- margin: 0;
- }
- h3 {
- margin: 0;
- }
- }
-
- .ui-box-body {
- border: none;
- background-color: #f5f5f5;
- border: none;
- border-top: 1px solid #eee;
- }
-
- .ui-box-bottom {
- border-top: 1px solid #eee;
- }
-
ul {
margin: 0;
+ padding: 0;
}
.title {
- @include bg-gray-gradient;
- border-bottom: 1px solid #CCC;
- color: #456;
+ background-color: #EEE;
+ border-bottom: 1px solid #DDD;
+ color: #666;
font-size: 16px;
text-shadow: 0 1px 1px #fff;
padding: 0 10px;
@@ -145,6 +94,10 @@
}
}
+ .body {
+ padding: 10px;
+ }
+
&.padded {
h5, .title {
margin: -20px;
@@ -154,7 +107,7 @@
}
.row_title {
- font-weight: bold;
+ font-weight: 500;
color: #444;
&:hover {
color: #444;
@@ -176,13 +129,45 @@
}
}
+/*
+ * Small box
+ */
+.ui-box.ui-box-small {
+ margin-bottom: 10px;
+
+ .title {
+ font-size: 13px;
+ line-height: 30px;
+
+ a {
+ color: #666;
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+ }
+}
+
+/*
+ * Danger box
+ */
+.ui-box.ui-box-danger {
+ background: #f7f7f7;
+ border: none;
+
+ .title {
+ background: #D65;
+ color: #fff;
+ text-shadow: none;
+ font-weight: 500;
+ }
+}
+
+/*
+ * Block under tw-bootstrap tabs
+ */
.tab-pane {
.ui-box {
margin: 3px 3px 25px 3px;
}
}
-
-.light-well {
- background: #f9f9f9;
- padding: 15px;
-}
diff --git a/app/assets/stylesheets/gitlab_bootstrap.scss b/app/assets/stylesheets/gitlab_bootstrap.scss
deleted file mode 100644
index faf36b702c0..00000000000
--- a/app/assets/stylesheets/gitlab_bootstrap.scss
+++ /dev/null
@@ -1,66 +0,0 @@
-/** Override bootstrap variables **/
-$baseFontSize: 13px !default;
-$baseLineHeight: 18px !default;
-
-/**
- * BOOTSTRAP
- */
-@import "bootstrap/variables";
-@import "bootstrap/mixins";
-@import "bootstrap/reset";
-@import "bootstrap/scaffolding";
-@import "bootstrap/grid";
-@import "bootstrap/layouts";
-@import "bootstrap/type";
-@import "bootstrap/code";
-@import "bootstrap/forms";
-@import "bootstrap/tables";
-@import "bootstrap/sprites";
-@import "bootstrap/dropdowns";
-@import "bootstrap/wells";
-@import "bootstrap/component-animations";
-@import "bootstrap/close";
-@import "bootstrap/button-groups";
-@import "bootstrap/alerts";
-@import "bootstrap/navs";
-@import "bootstrap/navbar";
-@import "bootstrap/breadcrumbs";
-@import "bootstrap/pagination";
-@import "bootstrap/pager";
-@import "bootstrap/modals";
-@import "bootstrap/tooltip";
-@import "bootstrap/popovers";
-@import "bootstrap/thumbnails";
-@import "bootstrap/media";
-@import "bootstrap/labels-badges";
-@import "bootstrap/progress-bars";
-@import "bootstrap/accordion";
-@import "bootstrap/carousel";
-@import "bootstrap/hero-unit";
-@import "bootstrap/utilities";
-@import "bootstrap/responsive-utilities";
-@import "bootstrap/responsive-1200px-min";
-
-/**
- * Font icons
- *
- */
-@import "font-awesome";
-
-/**
- * GitLab bootstrap.
- * Overrides some styles of twitter bootstrap.
- * Also give some common classes for GitLab app
- */
-@import "gitlab_bootstrap/variables.scss";
-@import "gitlab_bootstrap/fonts.scss";
-@import "gitlab_bootstrap/mixins.scss";
-@import "gitlab_bootstrap/avatar.scss";
-@import "gitlab_bootstrap/nav.scss";
-@import "gitlab_bootstrap/common.scss";
-@import "gitlab_bootstrap/typography.scss";
-@import "gitlab_bootstrap/buttons.scss";
-@import "gitlab_bootstrap/blocks.scss";
-@import "gitlab_bootstrap/files.scss";
-@import "gitlab_bootstrap/lists.scss";
-@import "gitlab_bootstrap/forms.scss";
diff --git a/app/assets/stylesheets/gitlab_bootstrap/forms.scss b/app/assets/stylesheets/gitlab_bootstrap/forms.scss
deleted file mode 100644
index a2612166c78..00000000000
--- a/app/assets/stylesheets/gitlab_bootstrap/forms.scss
+++ /dev/null
@@ -1,51 +0,0 @@
-form {
- @extend .form-horizontal;
-
- label {
- @extend .control-label;
- }
-}
-
-input.input-xpadding,
-.add-on.input-xpadding {
- padding: 6px 10px;
-}
-
-.control-group {
- .control-label {
- padding-top: 6px;
- }
- .controls {
- input, textarea {
- padding: 6px 10px;
- }
-
- input[type="radio"], input[type="checkbox"] {
- margin-top: 6px;
- }
-
- .add-on {
- padding: 6px;
- }
- }
-}
-
-input[type='search'].search-text-input {
- background-image: url("icon-search.png");
- background-repeat: no-repeat;
- background-position: 10px;
- padding-left: 25px;
- @include border-radius(4px);
- border: 1px solid #ccc;
-}
-
-input[type='text'].danger {
- background: #F2DEDE!important;
- border-color: #D66;
- text-shadow: 0 1px 1px #fff
-}
-
-fieldset legend {
- font-size: 16px;
- margin-bottom: 10px;
-}
diff --git a/app/assets/stylesheets/gl_bootstrap.scss b/app/assets/stylesheets/gl_bootstrap.scss
new file mode 100644
index 00000000000..5c5e3929028
--- /dev/null
+++ b/app/assets/stylesheets/gl_bootstrap.scss
@@ -0,0 +1,129 @@
+// Core variables and mixins
+@import "bootstrap/variables";
+@import "bootstrap/mixins";
+
+// Reset
+@import "bootstrap/normalize";
+@import "bootstrap/print";
+
+// Core CSS
+@import "bootstrap/scaffolding";
+@import "bootstrap/type";
+@import "bootstrap/code";
+@import "bootstrap/grid";
+@import "bootstrap/tables";
+@import "bootstrap/forms";
+
+// Components
+@import "bootstrap/component-animations";
+@import "bootstrap/dropdowns";
+@import "bootstrap/button-groups";
+@import "bootstrap/input-groups";
+@import "bootstrap/navs";
+@import "bootstrap/navbar";
+@import "bootstrap/breadcrumbs";
+@import "bootstrap/pagination";
+@import "bootstrap/pager";
+@import "bootstrap/labels";
+@import "bootstrap/badges";
+@import "bootstrap/jumbotron";
+@import "bootstrap/thumbnails";
+@import "bootstrap/alerts";
+@import "bootstrap/progress-bars";
+@import "bootstrap/list-group";
+@import "bootstrap/wells";
+@import "bootstrap/close";
+
+// Components w/ JavaScript
+@import "bootstrap/modals";
+@import "bootstrap/tooltip";
+@import "bootstrap/popovers";
+@import "bootstrap/carousel";
+
+// Utility classes
+.clearfix {
+ @include clearfix();
+}
+.center-block {
+ @include center-block();
+}
+.pull-right {
+ float: right !important;
+}
+.pull-left {
+ float: left !important;
+}
+.hide {
+ display: none;
+}
+.show {
+ display: block !important;
+}
+.invisible {
+ visibility: hidden;
+}
+.text-hide {
+ @include text-hide();
+}
+.hidden {
+ display: none !important;
+ visibility: hidden !important;
+}
+.affix {
+ position: fixed;
+}
+
+@import "bootstrap/responsive-utilities";
+
+// Labels
+.label {
+ padding: 2px 4px;
+ font-size: 12px;
+ font-style: normal;
+ font-weight: normal;
+ display: inline-block;
+
+ &.label-gray {
+ background-color: #eee;
+ color: #999;
+ text-shadow: none;
+ }
+
+ &.label-inverse {
+ background-color: #333333;
+ }
+}
+
+// Nav tabs
+.nav.nav-tabs {
+ li {
+ > a {
+ padding: 8px 20px;
+ margin-right: 7px;
+ line-height: 20px;
+ border-color: #EEE;
+ color: #888;
+ border-bottom: 1px solid #ddd;
+ .badge {
+ background-color: #eee;
+ color: #888;
+ text-shadow: 0 1px 1px #fff;
+ }
+ i[class^="icon-"] {
+ line-height: 14px;
+ }
+ }
+ &.active {
+ > a {
+ border-color: #CCC;
+ border-bottom: 1px solid #fff;
+ color: #333;
+ font-weight: bold;
+ }
+ }
+ }
+
+ &.nav-small-tabs > li > a {
+ padding: 6px 9px;
+ }
+}
diff --git a/app/assets/stylesheets/gitlab_bootstrap/fonts.scss b/app/assets/stylesheets/main/fonts.scss
index 8cc9986415c..8cc9986415c 100644
--- a/app/assets/stylesheets/gitlab_bootstrap/fonts.scss
+++ b/app/assets/stylesheets/main/fonts.scss
diff --git a/app/assets/stylesheets/gitlab_bootstrap/mixins.scss b/app/assets/stylesheets/main/mixins.scss
index 8b975a12cf7..e637b645b55 100644
--- a/app/assets/stylesheets/gitlab_bootstrap/mixins.scss
+++ b/app/assets/stylesheets/main/mixins.scss
@@ -25,6 +25,7 @@
background-image: -webkit-gradient(linear, 0 0, 0 100%, from($from), to($to));
background-image: -webkit-linear-gradient($from, $to);
background-image: -moz-linear-gradient($from, $to);
+ background-image: -ms-linear-gradient($from, $to);
background-image: -o-linear-gradient($from, $to);
}
@@ -45,6 +46,7 @@
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #f5f5f5), to(#e1e1e1));
background-image: -webkit-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
background-image: -moz-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
+ background-image: -ms-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
background-image: -o-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
}
@@ -53,6 +55,7 @@
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
+ background-image: -ms-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
}
@@ -60,6 +63,7 @@
background: #eee;
background-image: -webkit-linear-gradient(#e9e9e9, #d7d7d7);
background-image: -moz-linear-gradient(#e9e9e9, #d7d7d7);
+ background-image: -ms-linear-gradient(#e9e9e9, #d7d7d7);
background-image: -o-linear-gradient(#e9e9e9, #d7d7d7);
}
@@ -75,20 +79,40 @@
color: $style_color;
text-shadow: 0 1px 1px #FFF;
font-size: 16px;
- line-height: 40px;
+ line-height: 44px;
font-weight: normal;
}
@mixin md-typography {
+ img {
+ max-width: 100%;
+ }
+
*:first-child {
margin-top: 0;
}
code { padding: 0 4px; }
- h1 { margin-top: 30px;}
- h2 { margin-top: 25px;}
- h3 { margin-top: 20px;}
- h4 { margin-top: 15px;}
+
+ h1 {
+ margin-top: 45px;
+ font-size: 2.5em;
+ }
+
+ h2 {
+ margin-top: 40px;
+ font-size: 2em;
+ }
+
+ h3 {
+ margin-top: 35px;
+ font-size: 2em;
+ }
+
+ h4 {
+ margin-top: 30px;
+ font-size: 1.5em;
+ }
blockquote p {
color: #888;
@@ -103,10 +127,20 @@
background: #EEE;
}
}
+
+ code {
+ font-size: inherit;
+ font-weight: inherit;
+ color: #555;
+ }
+
+ li {
+ line-height: 1.5;
+ }
}
@mixin page-title {
- color: $style_color;
+ color: #333;
font-size: 20px;
line-height: 1.5;
margin-top: 0px;
diff --git a/app/assets/stylesheets/gitlab_bootstrap/variables.scss b/app/assets/stylesheets/main/variables.scss
index aeabe7ad2e8..86b207f02cc 100644
--- a/app/assets/stylesheets/gitlab_bootstrap/variables.scss
+++ b/app/assets/stylesheets/main/variables.scss
@@ -1,3 +1,6 @@
+/** Override bootstrap variables **/
+$font-size-base: 13px !default;
+
/**
* General Colors
*/
diff --git a/app/assets/stylesheets/sections.scss b/app/assets/stylesheets/sections.scss
new file mode 100644
index 00000000000..52bb449eb93
--- /dev/null
+++ b/app/assets/stylesheets/sections.scss
@@ -0,0 +1,21 @@
+@import "sections/header.scss";
+@import "sections/nav.scss";
+@import "sections/commits.scss";
+@import "sections/issues.scss";
+@import "sections/projects.scss";
+@import "sections/snippets.scss";
+@import "sections/votes.scss";
+@import "sections/merge_requests.scss";
+@import "sections/graph.scss";
+@import "sections/events.scss";
+@import "sections/themes.scss";
+@import "sections/tree.scss";
+@import "sections/notes.scss";
+@import "sections/profile.scss";
+@import "sections/login.scss";
+@import "sections/editor.scss";
+@import "sections/admin.scss";
+@import "sections/wiki.scss";
+@import "sections/wall.scss";
+@import "sections/dashboard.scss";
+@import "sections/stat_graph.scss";
diff --git a/app/assets/stylesheets/sections/admin.scss b/app/assets/stylesheets/sections/admin.scss
index e189fd27ac6..8ad9bc732b2 100644
--- a/app/assets/stylesheets/sections/admin.scss
+++ b/app/assets/stylesheets/sections/admin.scss
@@ -20,4 +20,19 @@
label { width: 110px; }
.controls { margin-left: 130px; }
.form-actions { padding-left: 130px; background: #fff }
+ .visibility-levels {
+ .controls {
+ margin-bottom: 9px;
+ }
+
+ i {
+ color: inherit;
+ }
+ }
+}
+
+.broadcast-messages {
+ .message {
+ line-height: 2;
+ }
}
diff --git a/app/assets/stylesheets/sections/commits.scss b/app/assets/stylesheets/sections/commits.scss
index 45a83cb6081..7b1b0ea4723 100644
--- a/app/assets/stylesheets/sections/commits.scss
+++ b/app/assets/stylesheets/sections/commits.scss
@@ -16,36 +16,29 @@
.header {
@extend .clearfix;
+ background: #DDD;
+ border-bottom: 1px solid #CCC;
padding: 5px 5px 5px 10px;
color: #555;
- border-bottom: 1px solid #CCC;
- background: #eee;
- // TODO Replace with linear-gradient mixin
- background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
- background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
- background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
- background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
-
- a{
- color: $style_color;
- }
> span {
font-family: $monospace_font;
font-size: 14px;
- line-height: 30px;
+ line-height: 2;
}
- a.view-file{
+ .view-file {
font-weight: bold;
+ float: right;
+ background-color: #EEE;
}
- .commit-short-id{
+ .commit-short-id {
font-family: $monospace_font;
font-size: smaller;
}
- .file-mode{
+ .file-mode {
font-family: $monospace_font;
}
}
@@ -55,13 +48,13 @@
background: #FFF;
color: #333;
font-size: 12px;
- .old{
- span.idiff{
+ .old {
+ span.idiff {
background-color: #FAA;
}
}
- .new{
- span.idiff{
+ .new {
+ span.idiff {
background-color: #AFA;
}
}
@@ -77,7 +70,7 @@
font-size: 12px;
}
}
- .old_line, .new_line {
+ .old_line, .new_line, .diff_line {
margin: 0px;
padding: 0px;
border: none;
@@ -87,7 +80,7 @@
border-right: 1px solid #ccc;
text-align: right;
min-width: 35px;
- max-width: 35px;
+ max-width: 50px;
width: 35px;
@include user-select(none);
a {
@@ -99,6 +92,15 @@
text-decoration: underline;
}
}
+ &.new {
+ background: #CFD;
+ }
+ &.old {
+ background: #FDD;
+ }
+ }
+ .diff_line {
+ padding: 0;
}
.line_holder {
&.old .old_line,
@@ -129,6 +131,11 @@
color: #ccc;
background: #fafafa;
}
+ &.parallel {
+ display: table-cell;
+ overflow: hidden;
+ width: 50%;
+ }
}
}
.image {
@@ -293,6 +300,7 @@
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
+ background-image: -ms-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
ul, li{
@@ -391,8 +399,8 @@
.commits-compare-switch{
background: url("switch_icon.png") no-repeat center center;
- width: 22px;
- height: 22px;
+ width: 32px;
+ height: 32px;
text-indent: -9999px;
float: left;
margin-right: 9px;
@@ -474,8 +482,8 @@ li.commit {
}
.commit-row-message {
- color: #555;
- font-weight: bolder;
+ color: #333;
+ font-weight: 500;
&:hover {
color: #444;
text-decoration: underline;
@@ -484,13 +492,14 @@ li.commit {
}
.commit-row-info {
+ color: #777;
+
a {
color: #777;
}
.committed_ago {
float: right;
- @extend .cgray;
}
}
diff --git a/app/assets/stylesheets/sections/dashboard.scss b/app/assets/stylesheets/sections/dashboard.scss
index 61043147fc1..1fd82c84fc9 100644
--- a/app/assets/stylesheets/sections/dashboard.scss
+++ b/app/assets/stylesheets/sections/dashboard.scss
@@ -1,11 +1,5 @@
.dashboard {
- @extend .row;
- .activities {
- }
-
.side {
- @extend .pull-right;
-
.ui-box {
margin: 0px;
box-shadow: none;
@@ -20,7 +14,7 @@
.search-text-input {
float:left;
- @extend .span2;
+ @extend .col-md-2;
}
.btn {
margin-left: 5px;
@@ -32,14 +26,15 @@
.dash-filter {
margin: 7px 0;
padding: 4px 6px;
- width: 202px;
+ width: 220px;
float: left;
+ height: inherit;
}
}
@media (max-width: 1200px) {
.dashboard .dash-filter {
- width: 132px;
+ width: 150px;
}
}
@@ -51,7 +46,7 @@
li {
&.active {
a {
- @include linear-gradient(#f5f5f5, #eee);
+ background-color: #EEE;
border-bottom: 1px solid #EEE !important;
&:hover {
background: #eee;
@@ -66,37 +61,58 @@
}
.project-row, .group-row {
- padding: 10px 15px !important;
+ padding: 10px 12px !important;
+ font-size: 14px;
+ line-height: 24px;
- .namespace-name {
- color: #666;
- font-weight: bold;
+ a {
+ display: block;
}
.project-name, .group-name {
- font-size: 15px;
+ font-weight: 500;
}
.arrow {
float: right;
- padding: 10px 5px;
+ padding: 0px 5px;
margin: 0;
font-size: 20px;
color: #666;
}
.last-activity {
+ float: right;
+ font-size: 12px;
color: #AAA;
display: block;
- margin-top: 5px;
.date {
color: #777;
}
}
}
-.group-row {
- .arrow {
- padding: 2px 5px;
+.project-access-icon {
+ margin-left: 10px;
+ float: left;
+ margin-right: 15px;
+ font-size: 20px;
+ margin-bottom: 15px;
+ border: 1px solid #EEE;
+ padding: 8px 12px;
+ border-radius: 50px;
+ background: #f5f5f5;
+ text-align: center;
+
+ i {
+ color: #BBB;
}
}
+
+.dash-project-access-icon {
+ float: left;
+ margin-right: 3px;
+ color: #999;
+ margin-bottom: 10px;
+ width: 16px;
+}
diff --git a/app/assets/stylesheets/sections/editor.scss b/app/assets/stylesheets/sections/editor.scss
index a71e5438936..0bfbc0f2ad5 100644
--- a/app/assets/stylesheets/sections/editor.scss
+++ b/app/assets/stylesheets/sections/editor.scss
@@ -42,7 +42,7 @@
line-height: 20px;
}
textarea {
- @extend .span8;
+ @extend .col-md-8;
}
}
}
diff --git a/app/assets/stylesheets/sections/events.scss b/app/assets/stylesheets/sections/events.scss
index 966282750d9..a576fefdea1 100644
--- a/app/assets/stylesheets/sections/events.scss
+++ b/app/assets/stylesheets/sections/events.scss
@@ -75,6 +75,7 @@
margin-top: 4px;
margin-left: 0px;
max-width: 200px;
+ float: none;
}
p:last-child {
@@ -147,7 +148,7 @@
float: left;
padding: 9px 6px;
font-size: 18px;
- width: 26px;
+ width: 40px;
@include border-radius(3px);
}
diff --git a/app/assets/stylesheets/sections/header.scss b/app/assets/stylesheets/sections/header.scss
index bd72d08295a..b0dd0d08356 100644
--- a/app/assets/stylesheets/sections/header.scss
+++ b/app/assets/stylesheets/sections/header.scss
@@ -4,17 +4,24 @@
*/
header {
&.navbar-gitlab {
+ margin-bottom: 0;
+ min-height: 40px;
+
.navbar-inner {
- height: 40px;
- padding: 3px;
background: #F1F1F1;
+ border-bottom: 1px solid #DDD;
filter: none;
.nav > li > a {
color: $style_color;
text-shadow: 0 1px 0 #fff;
font-size: 14px;
- padding: 10px;
+ line-height: 32px;
+ padding: 6px 10px;
+
+ &:hover {
+ background: none;
+ }
}
/** NAV block with links and profile **/
@@ -35,9 +42,6 @@ header {
.app_logo {
float: left;
margin-right: 9px;
- position: relative;
- top: -5px;
- padding-top: 5px;
a {
float: left;
@@ -46,10 +50,10 @@ header {
h1 {
margin: 0;
- background: url('logo-black.png') no-repeat center 1px;
- background-size: 38px;
+ background: url('logo-black.png') no-repeat center center;
+ background-size: 32px;
float: left;
- height: 40px;
+ height: 46px;
width: 40px;
@include header-font;
text-indent: -9999px;
@@ -75,7 +79,7 @@ header {
.profile-pic {
position: relative;
- top: -4px;
+ top: -1px;
img {
width: 26px;
height: 26px;
@@ -91,21 +95,25 @@ header {
.search {
margin-right: 10px;
margin-left: 10px;
+ margin-top: 8px;
+
+ form {
+ margin: 0;
+ padding: 0;
+ }
.search-input {
- @extend .span3;
background-image: url("icon-search.png");
background-repeat: no-repeat;
background-position: 10px;
+ height: inherit;
+ padding: 4px 6px;
padding-left: 25px;
font-size: 13px;
@include border-radius(3px);
border: 1px solid #c6c6c6;
box-shadow: none;
@include transition(all 0.15s ease-in 0s);
- &:focus {
- @extend .span4;
- }
}
}
@@ -152,8 +160,8 @@ header {
.app_logo {
a {
h1 {
- background: url('logo-white.png') no-repeat center 1px;
- background-size: 38px;
+ background: url('logo-white.png') no-repeat center center;
+ background-size: 32px;
color: #fff;
text-shadow: 0 1px 1px #444;
}
@@ -181,12 +189,26 @@ header {
.separator {
float: left;
height: 46px;
- width: 1px;
+ width: 2px;
background: white;
border-left: 1px solid #DDD;
- margin-top: -3px;
margin-left: 10px;
margin-right: 10px;
}
}
+.search .search-input {
+ width: 300px;
+ &:focus {
+ width: 400px;
+ }
+}
+
+@media (max-width: 1200px) {
+ .search .search-input {
+ width: 200px;
+ &:focus {
+ width: 300px;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/sections/issues.scss b/app/assets/stylesheets/sections/issues.scss
index e384aebc76c..10add561c36 100644
--- a/app/assets/stylesheets/sections/issues.scss
+++ b/app/assets/stylesheets/sections/issues.scss
@@ -77,8 +77,8 @@ input.check_all_issues {
@media (min-width: 800px) { .issues_filters select { width: 160px; } }
@media (min-width: 1200px) { .issues_filters select { width: 220px; } }
-@media (min-width: 800px) { .issues_bulk_update select { width: 120px; } }
-@media (min-width: 1200px) { .issues_bulk_update select { width: 160px; } }
+@media (min-width: 800px) { .issues_bulk_update .chosen-container { min-width: 120px; } }
+@media (min-width: 1200px) { .issues_bulk_update .chosen-container { min-width: 160px; } }
.issues-holder {
.issues_filters {
@@ -103,3 +103,27 @@ input.check_all_issues {
.participants {
margin-bottom: 10px;
}
+
+.issues_bulk_update {
+ .chosen-container {
+ text-shadow: none;
+ }
+}
+
+.issue-search-form {
+ margin: 0;
+ height: 24px;
+
+ .issue_search {
+ border: 1px solid #DDD !important;
+ background-color: #f4f4f4;
+ }
+}
+
+.issue-show-labels .label {
+ padding: 6px 10px;
+}
+
+form.edit-issue {
+ margin: 0;
+}
diff --git a/app/assets/stylesheets/sections/login.scss b/app/assets/stylesheets/sections/login.scss
index 33bef59c089..186294424a2 100644
--- a/app/assets/stylesheets/sections/login.scss
+++ b/app/assets/stylesheets/sections/login.scss
@@ -1,48 +1,58 @@
/* Login Page */
-body.login-page{
- .container > .content {
- padding-top: 20px;
+.login-page {
+ h1 {
+ font-size: 3em;
+ font-weight: 200;
}
-}
-
-.login-box{
- width: 304px;
- position: relative;
- @include border-radius(5px);
- margin: auto;
- padding: 20px;
- background: white;
-}
-
-.login-box .login-logo{
- margin: 10px 0 30px 0;
- display: block;
-}
-
-.login-box input.text{background-color: #f1f1f1; font-size: 16px; @include border-radius(0); padding: 14px 10px; width: 280px}
-
-.login-box input.text.top{
- @include border-radius(5px 5px 0 0);
- margin-bottom: 0px;
-}
-.login-box input.text.bottom{
- @include border-radius(0 0 5px 5px);
- border-top: 0;
- margin-bottom: 20px;
-}
+ .login-box{
+ width: 304px;
+ position: relative;
+ @include border-radius(5px);
+ margin: auto;
+ padding: 20px;
+ background: white;
+ }
-.login-box input.text.middle{
- border-top: 0;
- margin-bottom:0px;
-}
+ .login-logo{
+ margin: 10px 0 30px 0;
+ display: block;
+ }
-.login-box a.forgot{float: right; padding-top: 6px}
+ .form-control {
+ background-color: #f1f1f1;
+ font-size: 16px;
+ padding: 14px 10px;
+ width: 280px;
+ height: auto;
+
+ &.top {
+ @include border-radius(5px 5px 0 0);
+ margin-bottom: 0px;
+ }
+
+ &.bottom {
+ @include border-radius(0 0 5px 5px);
+ border-top: 0;
+ margin-bottom: 20px;
+ }
+
+ &.middle {
+ border-top: 0;
+ margin-bottom:0px;
+ @include border-radius(0);
+ }
+ }
-.remember_me {
- text-align: left;
+ .login-box a.forgot {
+ float: right;
+ padding-top: 6px
+ }
- input {
- margin: 2px;
+ .devise-errors {
+ h2 {
+ font-size: 14px;
+ color: #a00;
+ }
}
}
diff --git a/app/assets/stylesheets/sections/merge_requests.scss b/app/assets/stylesheets/sections/merge_requests.scss
index aa61cae4b9a..4dc67f1080b 100644
--- a/app/assets/stylesheets/sections/merge_requests.scss
+++ b/app/assets/stylesheets/sections/merge_requests.scss
@@ -15,29 +15,9 @@
}
}
- .accept_group {
- float: left;
- border: 1px solid #ADA;
- padding: 2px;
- @include border-radius(5px);
- background: #CEB;
-
- .accept_merge_request {
- font-size: 13px;
- float: left;
- }
- .remove_branch_holder {
- margin-left: 20px;
- margin-right: 10px;
- float: left;
- }
- label {
- color: #444;
- text-align: left
- }
+ .accept-group {
}
-
.how_to_merge_link {
@extend .primary;
}
@@ -110,9 +90,24 @@
.merge-request-angle {
text-align: center;
- margin: 0;
+ margin: 0 auto;
+ font-size: 2em;
+ line-height: 1.1;
}
.merge-request-form-info {
- padding: 15px 0;
+ padding-top: 15px;
+}
+
+.merge-request-branches {
+ .commit-row-message {
+ font-weight: normal !important;
+ }
+
+ .chosen-container .chosen-single {
+ span {
+ font-weight: bold;
+ color: #555;
+ }
+ }
}
diff --git a/app/assets/stylesheets/sections/nav.scss b/app/assets/stylesheets/sections/nav.scss
index 54263523e85..f706854b4fe 100644
--- a/app/assets/stylesheets/sections/nav.scss
+++ b/app/assets/stylesheets/sections/nav.scss
@@ -6,6 +6,7 @@
border-bottom: 1px solid #E1E1E1;
ul {
+ padding: 0;
margin: auto;
height: 40px;
overflow: hidden;
diff --git a/app/assets/stylesheets/sections/notes.scss b/app/assets/stylesheets/sections/notes.scss
index 94b9ca3b181..1ae0e161aa2 100644
--- a/app/assets/stylesheets/sections/notes.scss
+++ b/app/assets/stylesheets/sections/notes.scss
@@ -2,7 +2,7 @@
* Notes
*/
-@-webkit-keyframes target-note {
+@-webkit-keyframes targe3-note {
from { background:#fffff0; }
50% { background:#ffffd3; }
to { background:#fffff0; }
@@ -119,9 +119,9 @@ ul.notes {
}
.file .notes_holder {
- font-family: $sansFontFamily;
font-size: 13px;
line-height: 18px;
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
td {
border: 1px solid #ddd;
@@ -130,9 +130,15 @@ ul.notes {
&.notes_line {
text-align: center;
padding: 10px 0;
+ background: #eee;
+ }
+ &.notes_line2 {
+ text-align: center;
+ padding: 10px 0;
+ border-left: 1px solid #ddd !important;
}
&.notes_content {
- background-color: $white;
+ background-color: #fff;
border-width: 1px 0;
padding-top: 0;
@@ -251,12 +257,12 @@ ul.notes {
.file,
.discussion {
.new_note {
- margin: 8px 5px 8px 0;
+ margin: 0;
+ border: none;
}
}
.new_note {
display: none;
-
.buttons {
float: left;
margin-top: 8px;
@@ -270,10 +276,9 @@ ul.notes {
// preview/edit buttons
> a {
- font-size: 24px;
- padding: 4px;
position: absolute;
- right: 10px;
+ right: 5px;
+ bottom: -60px;
}
.note_preview {
background: #f5f5f5;
@@ -298,7 +303,7 @@ ul.notes {
}
.note-image-attach {
- @extend .span4;
+ @extend .col-md-4;
@extend .thumbnail;
margin-left: 45px;
}
@@ -306,10 +311,8 @@ ul.notes {
.common-note-form {
margin: 0;
- height: 140px;
background: #F9F9F9;
padding: 3px;
- padding-bottom: 25px;
border: 1px solid #DDD;
}
@@ -320,7 +323,7 @@ ul.notes {
padding: 0 5px;
.note-form-option {
- margin-top: 10px;
+ margin-top: 8px;
margin-left: 30px;
@extend .pull-left;
}
@@ -358,3 +361,7 @@ ul.notes {
.js-note-attachment-delete {
display: none;
}
+
+.parallel-comment {
+ padding: 6px;
+}
diff --git a/app/assets/stylesheets/sections/profile.scss b/app/assets/stylesheets/sections/profile.scss
index 21f4ed0577d..83dc80e1ba5 100644
--- a/app/assets/stylesheets/sections/profile.scss
+++ b/app/assets/stylesheets/sections/profile.scss
@@ -1,7 +1,6 @@
.update-notifications {
- margin-bottom: 0;
- label {
- margin-bottom: 0;
+ .radio-inline {
+ margin-right: 30px;
}
}
@@ -17,7 +16,7 @@
legend {
border: none;
- margin: 0;
+ margin-bottom: 10px;
}
}
}
@@ -42,3 +41,8 @@
margin-right: 12px;
}
+.profile-avatar-form-option {
+ hr {
+ margin: 10px 0;
+ }
+}
diff --git a/app/assets/stylesheets/sections/projects.scss b/app/assets/stylesheets/sections/projects.scss
index 0e5f99d4b7d..6142cbf9b32 100644
--- a/app/assets/stylesheets/sections/projects.scss
+++ b/app/assets/stylesheets/sections/projects.scss
@@ -16,12 +16,18 @@
.project-home-panel {
border-bottom: 1px solid #DDD;
- padding-bottom: 30px;
+ padding-bottom: 25px;
margin-bottom: 30px;
+ &.empty-project {
+ border-bottom: 0px;
+ padding-bottom: 15px;
+ margin-bottom: 0px;
+ }
+
.project-home-title {
font-size: 18px;
- color: #777;
+ color: #444;
margin: 0;
line-height: 32px;
}
@@ -34,7 +40,7 @@
.project-home-desc {
float: left;
- color: #999;
+ color: #777;
}
.project-home-links {
@@ -45,29 +51,47 @@
}
}
- .public-label {
- font-size: 14px;
+ .visibility-level-label {
+ font-size: 17px;
background: #f1f1f1;
- padding: 8px 10px;
border-radius: 4px;
- margin-left: 10px;
color: #888;
+ position: absolute;
+ margin-left: -55px;
text-shadow: 0 1px 1px #FFF;
+ width: 40px;
+ text-align: center;
+ padding: 6px;
+
+ i {
+ color: inherit;
+ }
}
}
.git-clone-holder {
- float: right;
+ margin-right: 45px;
border: 1px solid #E1E1E1;
@include border-radius(4px);
+ input[type="text"],
.btn {
- margin-left: 3px;
border: none;
- background: none;
+ @include border-radius(0px);
+ border-left: 1px solid #E1E1E1;
box-shadow: none;
+ padding: 6px 9px;
+ }
+
+ .btn {
+ float: left;
+ background: none;
color: #29b;
- padding: 6px;
+
+ &:first-child {
+ @include border-radius-left(4px);
+ border-left: 0px;
+ }
&.active {
color: #333;
@@ -76,20 +100,51 @@
}
input[type="text"] {
- margin-left: 2px;
- border: none;
- border-radius: 0;
- border-left: 1px solid #E1E1E1;
+ cursor: auto;
@extend .monospace;
- box-shadow: none;
background: #FAFAFA;
- padding: 6px 10px;
+ width: 100%;
+ }
+
+ .protocol-clone {
+ overflow: hidden;
}
}
-.project-public-holder {
- .help-inline {
- padding-top: 7px;
+.project-visibility-level-holder {
+ .controls {
+ padding-bottom: 9px;
+ }
+
+ .controls {
+ input {
+ float: left;
+ }
+ .descr {
+ display: block;
+ margin-left: 1.5em;
+ &.restricted {
+ color: #888;
+ }
+
+ label {
+ float: none;
+ padding: 0;
+ margin: 0;
+ text-align: left;
+ }
+ }
+ .info {
+ display: block;
+ margin-top: 5px;
+ }
+ strong {
+ display: inline-block;
+ width: 4em;
+ }
+ }
+ i {
+ color: inherit;
}
}
@@ -130,7 +185,8 @@ ul.nav.nav-projects-tabs {
margin: 0px;
}
-.my-projects {
+.my-projects,
+.public-projects {
li {
.project-info {
margin-bottom: 10px;
@@ -166,3 +222,62 @@ ul.nav.nav-projects-tabs {
color: #777;
}
}
+
+.project-side {
+ .btn-block {
+ background-image: none;
+ background-color: #F1f1f1;
+ border-color: #EEE;
+ &:hover {
+ background-color: #eee;
+ border-color: #DDD;
+ }
+ }
+ .project-fork-icon {
+ float: left;
+ font-size: 26px;
+ margin-right: 10px;
+ line-height: 1.5;
+ }
+}
+
+.transfer-project .chosen-container {
+ min-width: 200px;
+}
+
+/** Branch/tag selector **/
+.project-refs-form {
+ margin: 0;
+ span {
+ background:none !important;
+ position:static !important;
+ width:auto !important;
+ height:auto !important;
+ }
+}
+
+.project-refs-form .chosen-container {
+ position: relative;
+ top: 0;
+ left: 0;
+ margin-right: 10px;
+
+ .chosen-single span {
+ font-weight: bold;
+ color: #555;
+ }
+
+ &.chosen-container-active {
+ .chosen-drop {
+ min-width: 400px;
+ }
+
+ .chosen-results {
+ max-height: 400px;
+ }
+ }
+}
+
+.deploy-project-label {
+ margin: 1px;
+}
diff --git a/app/assets/stylesheets/sections/tree.scss b/app/assets/stylesheets/sections/tree.scss
index 96f2a5711fa..ef4546d3066 100644
--- a/app/assets/stylesheets/sections/tree.scss
+++ b/app/assets/stylesheets/sections/tree.scss
@@ -24,10 +24,10 @@
th {
font-weight: normal;
font-size: 15px;
- border-bottom: 1px solid #CCC;
+ border-bottom: 1px solid #CCC !important;
}
td {
- border-color: #F1F1F1;
+ border-color: #F1F1F1 !important;
}
&:hover {
td {
@@ -111,5 +111,16 @@
.tree-ref-holder {
float: left;
- margin-top: 5px;
+ margin-top: 8px;
+}
+
+.readme-holder {
+ border-top: 1px dashed #CCC;
+ padding-top: 10px;
+
+ h4 {
+ font-size: 14px;
+ margin-bottom: 20px;
+ color: #777;
+ }
}
diff --git a/app/assets/stylesheets/sections/votes.scss b/app/assets/stylesheets/sections/votes.scss
index 49489babab7..13f811e01a1 100644
--- a/app/assets/stylesheets/sections/votes.scss
+++ b/app/assets/stylesheets/sections/votes.scss
@@ -36,3 +36,8 @@
display: inline-block;
margin: 0 8px;
}
+
+.votes-holder {
+ float: right;
+ width: 250px;
+}
diff --git a/app/assets/stylesheets/sections/wall.scss b/app/assets/stylesheets/sections/wall.scss
index d6ac08fcf6f..3705afdb87c 100644
--- a/app/assets/stylesheets/sections/wall.scss
+++ b/app/assets/stylesheets/sections/wall.scss
@@ -1,6 +1,6 @@
.wall-page {
.wall-note-form {
- @extend .span12;
+ @extend .col-md-12;
margin: 0;
height: 140px;
diff --git a/app/assets/stylesheets/sections/wiki.scss b/app/assets/stylesheets/sections/wiki.scss
index ed3a432ded0..dfaeba41cf6 100644
--- a/app/assets/stylesheets/sections/wiki.scss
+++ b/app/assets/stylesheets/sections/wiki.scss
@@ -1,4 +1,4 @@
-h3.page-title .edit-wiki-header {
+.title .edit-wiki-header {
width: 780px;
margin-left: auto;
margin-right: auto;
diff --git a/app/assets/stylesheets/selects.scss b/app/assets/stylesheets/selects.scss
deleted file mode 100644
index 09ae57b6692..00000000000
--- a/app/assets/stylesheets/selects.scss
+++ /dev/null
@@ -1,153 +0,0 @@
-/* CHZN reset few styles */
-.chosen-container-single .chosen-single {
- background: #FFF;
- border: 1px solid #bbb;
- box-shadow: none;
-}
-.chosen-container-active .chosen-single {
- background: #fff;
-}
-
-.ajax-users-select {
- width: 400px;
-
- &.input-large {
- width: 210px;
- }
-}
-
-.user-result {
- .user-image {
- float: left;
- }
- .user-name {
- }
- .user-username {
- color: #999;
- }
-}
-
-/** Branch/tag selector **/
-.project-refs-form {
- margin: 0;
- span {
- background:none !important;
- position:static !important;
- width:auto !important;
- height:auto !important;
- }
-}
-.project-refs-select {
- width: 120px;
-}
-
-.project-refs-form .chosen-container {
- position: relative;
- top: 0;
- left: 0;
- margin-right: 10px;
-
- .chosen-drop {
- min-width: 400px;
- .chosen-results {
- max-height: 300px;
- }
- .chosen-search input {
- min-width: 365px;
- }
- }
-}
-
-/** Fix for Search Dropdown Border **/
-.chosen-container {
- min-width: 100px;
-
- .chosen-search {
- input:focus {
- @include box-shadow(none);
- }
- }
-
- .chosen-drop {
- margin: 7px 0;
- min-width: 200px;
- border: 1px solid #bbb;
- @include border-radius(0);
-
- .chosen-results {
- margin-top: 5px;
- max-height: 300px;
-
- .group-result {
- color: $style_color;
- border-bottom: 1px solid #EEE;
- padding: 8px;
- }
- .active-result {
- @include border-radius(0);
-
- &.highlighted {
- background: $hover;
- color: $style_color;
- }
- &.result-selected {
- background: #EEE;
- border-left: 4px solid #CCC;
- }
- }
- }
-
- .chosen-search {
- @include bg-gray-gradient;
- input {
- min-width: 165px;
- border-color: #CCC;
- }
- }
- }
-}
-
-.chosen-container .chosen-single,
-.chosen-container.chosen-with-drop .chosen-single {
- @include bg-light-gray-gradient;
-
- div {
- background: transparent;
- border-left: none;
- }
-
- span {
- font-weight: normal;
- }
-}
-
-/** Select2 styling **/
-.select2-container .select2-choice {
- background: #f1f1f1;
- background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, whitesmoke), to(#e1e1e1));
- background-image: -webkit-linear-gradient(whitesmoke 6.6%, #e1e1e1);
- background-image: -moz-linear-gradient(whitesmoke 6.6%, #e1e1e1);
- background-image: -o-linear-gradient(whitesmoke 6.6%, #e1e1e1);
-}
-
-.select2-container .select2-choice div {
- border: none;
- background: none;
-}
-
-.select2-drop {
- padding-top: 8px;
-}
-
-.select2-no-results, .select2-searching {
- padding: 7px;
- color: #666;
-}
-
-.chosen-container .chosen-single div b {
- background-position-y: 0px !important;
-}
-
-.chosen-container .chosen-drop .chosen-search input {
- background-position-y: -24px !important;
-}
diff --git a/app/assets/stylesheets/themes/ui_color.scss b/app/assets/stylesheets/themes/ui_color.scss
index 1fd54ff18a6..7d9cab215c8 100644
--- a/app/assets/stylesheets/themes/ui_color.scss
+++ b/app/assets/stylesheets/themes/ui_color.scss
@@ -27,6 +27,12 @@
background: #435;
border-left: 1px solid #658;
}
+ .nav > li > a {
+ color: #98B;
+ }
+ .search-input {
+ border-color: #98B;
+ }
}
}
}
diff --git a/app/assets/stylesheets/themes/ui_mars.scss b/app/assets/stylesheets/themes/ui_mars.scss
index a2b8c21ea11..aba3e0ca827 100644
--- a/app/assets/stylesheets/themes/ui_mars.scss
+++ b/app/assets/stylesheets/themes/ui_mars.scss
@@ -23,12 +23,17 @@
background-color: #373D47;
}
}
+ .separator {
+ background: #373D47;
+ border-left: 1px solid #575D67;
+ }
+ .nav > li > a {
+ color: #979DA7;
+ }
+ .search-input {
+ border-color: #979DA7;
+ }
}
}
-
- .separator {
- background: #31363E;
- border-left: 1px solid #666;
- }
}
}
diff --git a/app/assets/stylesheets/themes/ui_modern.scss b/app/assets/stylesheets/themes/ui_modern.scss
index 6173757082e..015a4bbf0c3 100644
--- a/app/assets/stylesheets/themes/ui_modern.scss
+++ b/app/assets/stylesheets/themes/ui_modern.scss
@@ -27,6 +27,12 @@
background: #234;
border-left: 1px solid #456;
}
+ .nav > li > a {
+ color: #89A;
+ }
+ .search-input {
+ border-color: #89A;
+ }
}
}
}
diff --git a/app/contexts/base_context.rb b/app/contexts/base_context.rb
index 101be50d54b..6accd9b2457 100644
--- a/app/contexts/base_context.rb
+++ b/app/contexts/base_context.rb
@@ -17,4 +17,3 @@ class BaseContext
abilities.allowed?(object, action, subject)
end
end
-
diff --git a/app/contexts/files/base_context.rb b/app/contexts/files/base_context.rb
new file mode 100644
index 00000000000..44f9826652c
--- /dev/null
+++ b/app/contexts/files/base_context.rb
@@ -0,0 +1,31 @@
+module Files
+ class BaseContext < ::BaseContext
+ attr_reader :ref, :path
+
+ def initialize(project, user, params, ref, path = nil)
+ @project, @current_user, @params = project, user, params.dup
+ @ref = ref
+ @path = path
+ end
+
+ private
+
+ def error(message)
+ {
+ error: message,
+ status: :error
+ }
+ end
+
+ def success
+ {
+ error: '',
+ status: :success
+ }
+ end
+
+ def repository
+ project.repository
+ end
+ end
+end
diff --git a/app/contexts/files/create_context.rb b/app/contexts/files/create_context.rb
new file mode 100644
index 00000000000..b3d62a028c7
--- /dev/null
+++ b/app/contexts/files/create_context.rb
@@ -0,0 +1,46 @@
+require_relative "base_context"
+
+module Files
+ class CreateContext < BaseContext
+ def execute
+ allowed = if project.protected_branch?(ref)
+ can?(current_user, :push_code_to_protected_branches, project)
+ else
+ can?(current_user, :push_code, project)
+ end
+
+ unless allowed
+ return error("You are not allowed to create file in this branch")
+ end
+
+ unless repository.branch_names.include?(ref)
+ return error("You can only create files if you are on top of a branch")
+ end
+
+ file_name = File.basename(path)
+ file_path = path
+
+ unless file_name =~ Gitlab::Regex.path_regex
+ return error("Your changes could not be committed, because file name contains not allowed characters")
+ end
+
+ blob = repository.blob_at(ref, file_path)
+
+ if blob
+ return error("Your changes could not be committed, because file with such name exists")
+ end
+
+ new_file_action = Gitlab::Satellite::NewFileAction.new(current_user, project, ref, file_path)
+ created_successfully = new_file_action.commit!(
+ params[:content],
+ params[:commit_message]
+ )
+
+ if created_successfully
+ success
+ else
+ error("Your changes could not be committed, because the file has been changed")
+ end
+ end
+ end
+end
diff --git a/app/contexts/files/delete_context.rb b/app/contexts/files/delete_context.rb
new file mode 100644
index 00000000000..39ff7c2a4b1
--- /dev/null
+++ b/app/contexts/files/delete_context.rb
@@ -0,0 +1,40 @@
+require_relative "base_context"
+
+module Files
+ class DeleteContext < BaseContext
+ def execute
+ allowed = if project.protected_branch?(ref)
+ can?(current_user, :push_code_to_protected_branches, project)
+ else
+ can?(current_user, :push_code, project)
+ end
+
+ unless allowed
+ return error("You are not allowed to push into this branch")
+ end
+
+ unless repository.branch_names.include?(ref)
+ return error("You can only create files if you are on top of a branch")
+ end
+
+ blob = repository.blob_at(ref, path)
+
+ unless blob
+ return error("You can only edit text files")
+ end
+
+ delete_file_action = Gitlab::Satellite::DeleteFileAction.new(current_user, project, ref, path)
+
+ deleted_successfully = delete_file_action.commit!(
+ nil,
+ params[:commit_message]
+ )
+
+ if deleted_successfully
+ success
+ else
+ error("Your changes could not be committed, because the file has been changed")
+ end
+ end
+ end
+end
diff --git a/app/contexts/files/update_context.rb b/app/contexts/files/update_context.rb
new file mode 100644
index 00000000000..556027a3256
--- /dev/null
+++ b/app/contexts/files/update_context.rb
@@ -0,0 +1,39 @@
+require_relative "base_context"
+
+module Files
+ class UpdateContext < BaseContext
+ def execute
+ allowed = if project.protected_branch?(ref)
+ can?(current_user, :push_code_to_protected_branches, project)
+ else
+ can?(current_user, :push_code, project)
+ end
+
+ unless allowed
+ return error("You are not allowed to push into this branch")
+ end
+
+ unless repository.branch_names.include?(ref)
+ return error("You can only create files if you are on top of a branch")
+ end
+
+ blob = repository.blob_at(ref, path)
+
+ unless blob
+ return error("You can only edit text files")
+ end
+
+ new_file_action = Gitlab::Satellite::EditFileAction.new(current_user, project, ref, path)
+ created_successfully = new_file_action.commit!(
+ params[:content],
+ params[:commit_message]
+ )
+
+ if created_successfully
+ success
+ else
+ error("Your changes could not be committed, because the file has been changed")
+ end
+ end
+ end
+end
diff --git a/app/contexts/filter_context.rb b/app/contexts/filter_context.rb
index 2607b12b4ae..fb0d42cf87d 100644
--- a/app/contexts/filter_context.rb
+++ b/app/contexts/filter_context.rb
@@ -1,24 +1,35 @@
class FilterContext
- attr_accessor :items, :params
+ attr_accessor :klass, :current_user, :params
- def initialize(items, params)
- @items = items
+ def initialize(klass, current_user, params)
+ @klass = klass
+ @current_user = current_user
@params = params
end
def execute
- apply_filter(items)
+ items = by_scope
+ items = by_state(items)
+ items = by_project(items)
+ items = by_search(items)
end
- def apply_filter items
- if params[:project_id].present?
- items = items.of_projects(params[:project_id])
- end
+ private
- if params[:search].present?
- items = items.search(params[:search])
+ def by_scope
+ table_name = klass.table_name
+
+ case params[:scope]
+ when 'authored' then
+ current_user.send(table_name)
+ when 'all' then
+ klass.of_projects(current_user.authorized_projects.pluck(:id))
+ else
+ current_user.send("assigned_#{table_name}")
end
+ end
+ def by_state(items)
case params[:status]
when 'closed'
items.closed
@@ -28,4 +39,20 @@ class FilterContext
items.opened
end
end
+
+ def by_project(items)
+ if params[:project_id].present?
+ items = items.of_projects(params[:project_id])
+ end
+
+ items
+ end
+
+ def by_search(items)
+ if params[:search].present?
+ items = items.search(params[:search])
+ end
+
+ items
+ end
end
diff --git a/app/contexts/issues/bulk_update_context.rb b/app/contexts/issues/bulk_update_context.rb
index 73a3c353523..ab38a4f8ab5 100644
--- a/app/contexts/issues/bulk_update_context.rb
+++ b/app/contexts/issues/bulk_update_context.rb
@@ -22,7 +22,7 @@ module Issues
opts[:milestone_id] = milestone_id if milestone_id.present?
opts[:assignee_id] = assignee_id if assignee_id.present?
- issues = Issue.where(id: issues_ids).all
+ issues = Issue.where(id: issues_ids)
issues = issues.select { |issue| can?(current_user, :modify_issue, issue) }
issues.each do |issue|
diff --git a/app/contexts/issues/list_context.rb b/app/contexts/issues/list_context.rb
index da2eed0e259..b256fd4c732 100644
--- a/app/contexts/issues/list_context.rb
+++ b/app/contexts/issues/list_context.rb
@@ -30,7 +30,24 @@ module Issues
@issues = @issues.where(milestone_id: (params[:milestone_id] == '0' ? nil : params[:milestone_id]))
end
+ # Sort by :sort param
+ @issues = sort(@issues, params[:sort])
+
@issues
end
+
+ private
+
+ def sort(issues, condition)
+ case condition
+ when 'newest' then issues.except(:order).order('created_at DESC')
+ when 'oldest' then issues.except(:order).order('created_at ASC')
+ when 'recently_updated' then issues.except(:order).order('updated_at DESC')
+ when 'last_updated' then issues.except(:order).order('updated_at ASC')
+ when 'milestone_due_soon' then issues.except(:order).joins(:milestone).order("milestones.due_date ASC")
+ when 'milestone_due_later' then issues.except(:order).joins(:milestone).order("milestones.due_date DESC")
+ else issues
+ end
+ end
end
end
diff --git a/app/contexts/projects/create_context.rb b/app/contexts/projects/create_context.rb
index 1c60a5de141..2acb9fbfe14 100644
--- a/app/contexts/projects/create_context.rb
+++ b/app/contexts/projects/create_context.rb
@@ -8,6 +8,11 @@ module Projects
# get namespace id
namespace_id = params.delete(:namespace_id)
+ # check that user is allowed to set specified visibility_level
+ unless Gitlab::VisibilityLevel.allowed_for?(current_user, params[:visibility_level])
+ params.delete(:visibility_level)
+ end
+
# Load default feature settings
default_features = Gitlab.config.gitlab.default_projects_features
@@ -17,7 +22,7 @@ module Projects
wall_enabled: default_features.wall,
snippets_enabled: default_features.snippets,
merge_requests_enabled: default_features.merge_requests,
- public: default_features.public
+ visibility_level: default_features.visibility_level
}.stringify_keys
@project = Project.new(default_opts.merge(params))
@@ -47,8 +52,6 @@ module Projects
@project.creator = current_user
if @project.save
- @project.discover_default_branch
-
unless @project.group
@project.users_projects.create(
project_access: UsersProject::MASTER,
diff --git a/app/contexts/projects/update_context.rb b/app/contexts/projects/update_context.rb
index 40385fa65b0..94de10de0f6 100644
--- a/app/contexts/projects/update_context.rb
+++ b/app/contexts/projects/update_context.rb
@@ -2,7 +2,17 @@ module Projects
class UpdateContext < BaseContext
def execute(role = :default)
params[:project].delete(:namespace_id)
- params[:project].delete(:public) unless can?(current_user, :change_public_mode, project)
+ # check that user is allowed to set specified visibility_level
+ unless can?(current_user, :change_visibility_level, project) && Gitlab::VisibilityLevel.allowed_for?(current_user, params[:project][:visibility_level])
+ params[:project].delete(:visibility_level)
+ end
+
+ new_branch = params[:project].delete(:default_branch)
+
+ if project.repository.exists? && new_branch && new_branch != project.default_branch
+ project.change_head(new_branch)
+ end
+
project.update_attributes(params[:project], as: role)
end
end
diff --git a/app/contexts/search_context.rb b/app/contexts/search_context.rb
index 48def0784fd..5985ab1fb0c 100644
--- a/app/contexts/search_context.rb
+++ b/app/contexts/search_context.rb
@@ -1,19 +1,24 @@
class SearchContext
- attr_accessor :project_ids, :params
+ attr_accessor :project_ids, :current_user, :params
- def initialize(project_ids, params)
- @project_ids, @params = project_ids, params.dup
+ def initialize(project_ids, user, params)
+ @project_ids, @current_user, @params = project_ids, user, params.dup
end
def execute
query = params[:search]
+ query = Shellwords.shellescape(query) if query.present?
return result unless query.present?
-
- projects = Project.where(id: project_ids)
- result[:projects] = projects.search(query).limit(20)
+ visibility_levels = @current_user ? [ Gitlab::VisibilityLevel::INTERNAL, Gitlab::VisibilityLevel::PUBLIC ] : [ Gitlab::VisibilityLevel::PUBLIC ]
+ result[:projects] = Project.where("projects.id in (?) OR projects.visibility_level in (?)", project_ids, visibility_levels).search(query).limit(20)
# Search inside single project
+ single_project_search(Project.where(id: project_ids), query)
+ result
+ end
+
+ def single_project_search(projects, query)
project = projects.first if projects.length == 1
if params[:search_code].present?
@@ -23,7 +28,6 @@ class SearchContext
result[:issues] = Issue.where(project_id: project_ids).search(query).order('updated_at DESC').limit(20)
result[:wiki_pages] = []
end
- result
end
def result
diff --git a/app/controllers/admin/broadcast_messages_controller.rb b/app/controllers/admin/broadcast_messages_controller.rb
new file mode 100644
index 00000000000..9a70ef9d199
--- /dev/null
+++ b/app/controllers/admin/broadcast_messages_controller.rb
@@ -0,0 +1,32 @@
+class Admin::BroadcastMessagesController < Admin::ApplicationController
+ before_filter :broadcast_messages
+
+ def index
+ @broadcast_message = BroadcastMessage.new
+ end
+
+ def create
+ @broadcast_message = BroadcastMessage.new(params[:broadcast_message])
+
+ if @broadcast_message.save
+ redirect_to admin_broadcast_messages_path, notice: 'Broadcast Message was successfully created.'
+ else
+ render :index
+ end
+ end
+
+ def destroy
+ BroadcastMessage.find(params[:id]).destroy
+
+ respond_to do |format|
+ format.html { redirect_to :back }
+ format.js { render nothing: true }
+ end
+ end
+
+ protected
+
+ def broadcast_messages
+ @broadcast_messages ||= BroadcastMessage.order("starts_at DESC").page(params[:page])
+ end
+end
diff --git a/app/controllers/admin/dashboard_controller.rb b/app/controllers/admin/dashboard_controller.rb
index 3c80b6503fa..be19139c9b1 100644
--- a/app/controllers/admin/dashboard_controller.rb
+++ b/app/controllers/admin/dashboard_controller.rb
@@ -2,5 +2,6 @@ class Admin::DashboardController < Admin::ApplicationController
def index
@projects = Project.order("created_at DESC").limit(10)
@users = User.order("created_at DESC").limit(10)
+ @groups = Group.order("created_at DESC").limit(10)
end
end
diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb
index 088174fd3b8..7e3e29f59fb 100644
--- a/app/controllers/admin/projects_controller.rb
+++ b/app/controllers/admin/projects_controller.rb
@@ -1,12 +1,14 @@
class Admin::ProjectsController < Admin::ApplicationController
- before_filter :project, only: [:edit, :show, :update, :destroy, :team_update]
+ before_filter :project, only: [:show, :transfer]
+ before_filter :group, only: [:show, :transfer]
+ before_filter :repository, only: [:show, :transfer]
def index
owner_id = params[:owner_id]
user = User.find_by_id(owner_id)
- @projects = user ? user.owned_projects : Project.scoped
- @projects = @projects.where(public: true) if params[:public_only].present?
+ @projects = user ? user.owned_projects : Project.all
+ @projects = @projects.where("visibility_level IN (?)", params[:visibility_levels]) if params[:visibility_levels].present?
@projects = @projects.with_push if params[:with_push].present?
@projects = @projects.abandoned if params[:abandoned].present?
@projects = @projects.search(params[:name]) if params[:name].present?
@@ -14,8 +16,16 @@ class Admin::ProjectsController < Admin::ApplicationController
end
def show
- @repository = @project.repository
- @group = @project.group
+ end
+
+ def transfer
+ result = ::Projects::TransferContext.new(@project, current_user, project: params).execute(:admin)
+
+ if result
+ redirect_to [:admin, @project]
+ else
+ render :show
+ end
end
protected
@@ -26,4 +36,12 @@ class Admin::ProjectsController < Admin::ApplicationController
@project = Project.find_with_namespace(id)
@project || render_404
end
+
+ def group
+ @group ||= project.group
+ end
+
+ def repository
+ @repository ||= project.repository
+ end
end
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index c9875b96321..2f981627137 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -2,8 +2,7 @@ class Admin::UsersController < Admin::ApplicationController
before_filter :user, only: [:show, :edit, :update, :destroy]
def index
- @users = User.scoped
- @users = @users.filter(params[:filter])
+ @users = User.filter(params[:filter])
@users = @users.search(params[:name]) if params[:name].present?
@users = @users.alphabetically.page(params[:page])
end
@@ -48,7 +47,7 @@ class Admin::UsersController < Admin::ApplicationController
@user.admin = (admin && admin.to_i > 0)
@user.created_by_id = current_user.id
@user.generate_password
- @user.confirm!
+ @user.skip_confirmation!
respond_to do |format|
if @user.save
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index cfa3cac5e88..b6501eacea3 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -1,3 +1,5 @@
+require 'gon'
+
class ApplicationController < ActionController::Base
before_filter :authenticate_user!
before_filter :reject_blocked!
@@ -7,6 +9,7 @@ class ApplicationController < ActionController::Base
before_filter :dev_tools if Rails.env == 'development'
before_filter :default_headers
before_filter :add_gon_variables
+ before_filter :configure_permitted_parameters, if: :devise_controller?
protect_from_forgery
@@ -81,6 +84,9 @@ class ApplicationController < ActionController::Base
if @project and can?(current_user, :read_project, @project)
@project
+ elsif current_user.nil?
+ @project = nil
+ authenticate_user!
else
@project = nil
render_404 and return
@@ -102,7 +108,7 @@ class ApplicationController < ActionController::Base
end
def authorize_code_access!
- return access_denied! unless can?(current_user, :download_code, project) or project.public?
+ return access_denied! unless can?(current_user, :download_code, project)
end
def authorize_push!
@@ -154,6 +160,9 @@ class ApplicationController < ActionController::Base
def default_headers
headers['X-Frame-Options'] = 'DENY'
headers['X-XSS-Protection'] = '1; mode=block'
+ headers['X-UA-Compatible'] = 'IE=edge'
+ headers['X-Content-Type-Options'] = 'nosniff'
+ headers['Strict-Transport-Security'] = 'max-age=31536000' if Gitlab.config.gitlab.https
end
def add_gon_variables
@@ -174,4 +183,31 @@ class ApplicationController < ActionController::Base
filters = cookies['event_filter'].split(',') if cookies['event_filter'].present?
@event_filter ||= EventFilter.new(filters)
end
+
+ # JSON for infinite scroll via Pager object
+ def pager_json(partial, count)
+ html = render_to_string(
+ partial,
+ layout: false,
+ formats: [:html]
+ )
+
+ render json: {
+ html: html,
+ count: count
+ }
+ end
+
+ def view_to_html_string(partial)
+ render_to_string(
+ partial,
+ layout: false,
+ formats: [:html]
+ )
+ end
+
+ def configure_permitted_parameters
+ devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:username, :email, :password) }
+ devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:username, :email, :name, :password, :password_confirmation) }
+ end
end
diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb
index ac319384434..27955c62488 100644
--- a/app/controllers/dashboard_controller.rb
+++ b/app/controllers/dashboard_controller.rb
@@ -22,7 +22,7 @@ class DashboardController < ApplicationController
respond_to do |format|
format.html
- format.js
+ format.json { pager_json("events/_events", @events.count) }
format.atom { render layout: false }
end
end
@@ -40,26 +40,23 @@ class DashboardController < ApplicationController
end
@projects = @projects.where(namespace_id: Group.find_by_name(params[:group])) if params[:group].present?
- @projects = @projects.includes(:namespace).sorted_by_activity
+ @projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present?
+ @projects = @projects.includes(:namespace)
+ @projects = @projects.tagged_with(params[:label]) if params[:label].present?
+ @projects = @projects.sort(@sort = params[:sort])
+ @projects = @projects.page(params[:page]).per(30)
@labels = current_user.authorized_projects.tags_on(:labels)
@groups = current_user.authorized_groups
-
- @projects = @projects.tagged_with(params[:label]) if params[:label].present?
- @projects = @projects.page(params[:page]).per(30)
end
- # Get authored or assigned open merge requests
def merge_requests
- @merge_requests = current_user.cared_merge_requests
- @merge_requests = FilterContext.new(@merge_requests, params).execute
+ @merge_requests = FilterContext.new(MergeRequest, current_user, params).execute
@merge_requests = @merge_requests.recent.page(params[:page]).per(20)
end
- # Get only assigned issues
def issues
- @issues = current_user.assigned_issues
- @issues = FilterContext.new(@issues, params).execute
+ @issues = FilterContext.new(Issue, current_user, params).execute
@issues = @issues.recent.page(params[:page]).per(20)
@issues = @issues.includes(:author, :project)
@@ -72,6 +69,6 @@ class DashboardController < ApplicationController
protected
def load_projects
- @projects = current_user.authorized_projects.sorted_by_activity
+ @projects = current_user.authorized_projects.sorted_by_activity.non_archived
end
end
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index f80167da4cb..5e8efe35a46 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -38,22 +38,22 @@ class GroupsController < ApplicationController
respond_to do |format|
format.html
- format.js
+ format.json { pager_json("events/_events", @events.count) }
format.atom { render layout: false }
end
end
# Get authored or assigned open merge requests
def merge_requests
- @merge_requests = current_user.cared_merge_requests.of_group(@group)
- @merge_requests = FilterContext.new(@merge_requests, params).execute
+ @merge_requests = FilterContext.new(MergeRequest, current_user, params).execute
+ @merge_requests = @merge_requests.of_group(@group)
@merge_requests = @merge_requests.recent.page(params[:page]).per(20)
end
# Get only assigned issues
def issues
- @issues = current_user.assigned_issues.of_group(@group)
- @issues = FilterContext.new(@issues, params).execute
+ @issues = FilterContext.new(Issue, current_user, params).execute
+ @issues = @issues.of_group(@group)
@issues = @issues.recent.page(params[:page]).per(20)
@issues = @issues.includes(:author, :project)
@@ -102,7 +102,7 @@ class GroupsController < ApplicationController
# Dont allow unauthorized access to group
def authorize_read_group!
- unless projects.present? or can?(current_user, :read_group, @group)
+ unless @group and (projects.present? or can?(current_user, :read_group, @group))
return render_404
end
end
diff --git a/app/controllers/profiles/avatars_controller.rb b/app/controllers/profiles/avatars_controller.rb
new file mode 100644
index 00000000000..57f3bbf0627
--- /dev/null
+++ b/app/controllers/profiles/avatars_controller.rb
@@ -0,0 +1,13 @@
+class Profiles::AvatarsController < ApplicationController
+ layout "profile"
+
+ def destroy
+ @user = current_user
+ @user.remove_avatar!
+
+ @user.save
+ @user.reset_events_cache
+
+ redirect_to profile_path
+ end
+end
diff --git a/app/controllers/profiles/keys_controller.rb b/app/controllers/profiles/keys_controller.rb
index c36dae2abd3..541319e8d0f 100644
--- a/app/controllers/profiles/keys_controller.rb
+++ b/app/controllers/profiles/keys_controller.rb
@@ -2,7 +2,7 @@ class Profiles::KeysController < ApplicationController
layout "profile"
def index
- @keys = current_user.keys.order('id DESC').all
+ @keys = current_user.keys.order('id DESC')
end
def show
diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb
index 47cfc5e63f5..9234cd1708f 100644
--- a/app/controllers/profiles_controller.rb
+++ b/app/controllers/profiles_controller.rb
@@ -13,6 +13,8 @@ class ProfilesController < ApplicationController
end
def update
+ params[:user].delete(:email) if @user.ldap_user?
+
if @user.update_attributes(params[:user])
flash[:notice] = "Profile was successfully updated"
else
diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb
index 8fd4565f367..7e4580017dd 100644
--- a/app/controllers/projects/application_controller.rb
+++ b/app/controllers/projects/application_controller.rb
@@ -10,7 +10,7 @@ class Projects::ApplicationController < ApplicationController
id = params[:project_id] || params[:id]
@project = Project.find_with_namespace(id)
- return if @project && @project.public
+ return if @project && @project.public?
end
super
@@ -23,4 +23,10 @@ class Projects::ApplicationController < ApplicationController
'public_projects'
end
end
+
+ def require_branch_head
+ unless @repository.branch_names.include?(@ref)
+ redirect_to project_tree_path(@project, @ref), notice: "This action is not allowed unless you are on top of a branch"
+ end
+ end
end
diff --git a/app/controllers/projects/base_tree_controller.rb b/app/controllers/projects/base_tree_controller.rb
new file mode 100644
index 00000000000..5e305934433
--- /dev/null
+++ b/app/controllers/projects/base_tree_controller.rb
@@ -0,0 +1,8 @@
+class Projects::BaseTreeController < Projects::ApplicationController
+ include ExtractsPath
+
+ before_filter :authorize_read_project!
+ before_filter :authorize_code_access!
+ before_filter :require_non_empty_project
+end
+
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index ba466251b29..fc9807e3c47 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -7,9 +7,30 @@ class Projects::BlobController < Projects::ApplicationController
before_filter :authorize_code_access!
before_filter :require_non_empty_project
+ before_filter :blob
+
def show
- @blob = @repository.blob_at(@commit.id, @path)
+ end
+
+ def destroy
+ result = Files::DeleteContext.new(@project, current_user, params, @ref, @path).execute
+
+ if result[:status] == :success
+ flash[:notice] = "Your changes have been successfully committed"
+ redirect_to project_tree_path(@project, @ref)
+ else
+ flash[:alert] = result[:error]
+ render :show
+ end
+ end
+
+ private
+
+ def blob
+ @blob ||= @repository.blob_at(@commit.id, @path)
+
+ return not_found! unless @blob
- not_found! unless @blob
+ @blob
end
end
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index 242aa41182d..65b8a7283a7 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -24,8 +24,8 @@ class Projects::CommitController < Projects::ApplicationController
@line_notes = result[:line_notes]
@branches = result[:branches]
@notes_count = result[:notes_count]
- @target_type = :commit
- @target_id = @commit.id
+ @notes = project.notes.for_commit_id(@commit.id).not_inline.fresh
+ @noteable = @commit
@comments_allowed = @reply_allowed = true
@comments_target = { noteable_type: 'Commit',
diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb
index bdffc940ea5..12856191c26 100644
--- a/app/controllers/projects/commits_controller.rb
+++ b/app/controllers/projects/commits_controller.rb
@@ -16,7 +16,7 @@ class Projects::CommitsController < Projects::ApplicationController
respond_to do |format|
format.html # index.html.erb
- format.js
+ format.json { pager_json("projects/commits/_commits", @commits.size) }
format.atom { render layout: false }
end
end
diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb
index 0750e0a146f..6e1a76ff417 100644
--- a/app/controllers/projects/deploy_keys_controller.rb
+++ b/app/controllers/projects/deploy_keys_controller.rb
@@ -7,7 +7,7 @@ class Projects::DeployKeysController < Projects::ApplicationController
layout "project_settings"
def index
- @enabled_keys = @project.deploy_keys.all
+ @enabled_keys = @project.deploy_keys
@available_keys = available_keys - @enabled_keys
end
diff --git a/app/controllers/projects/edit_tree_controller.rb b/app/controllers/projects/edit_tree_controller.rb
index 5d05c585ecc..3921273620c 100644
--- a/app/controllers/projects/edit_tree_controller.rb
+++ b/app/controllers/projects/edit_tree_controller.rb
@@ -1,49 +1,26 @@
-# Controller for edit a repository's file
-class Projects::EditTreeController < Projects::ApplicationController
- include ExtractsPath
-
- # Authorize
- before_filter :authorize_read_project!
- before_filter :authorize_code_access!
- before_filter :require_non_empty_project
-
- before_filter :edit_requirements, only: [:show, :update]
+class Projects::EditTreeController < Projects::BaseTreeController
+ before_filter :require_branch_head
+ before_filter :blob
def show
@last_commit = Gitlab::Git::Commit.last_for_path(@repository, @ref, @path).sha
end
def update
- edit_file_action = Gitlab::Satellite::EditFileAction.new(current_user, @project, @ref, @path)
- updated_successfully = edit_file_action.commit!(
- params[:content],
- params[:commit_message],
- params[:last_commit]
- )
+ result = Files::UpdateContext.new(@project, current_user, params, @ref, @path).execute
- if updated_successfully
- redirect_to project_blob_path(@project, @id), notice: "Your changes have been successfully commited"
+ if result[:status] == :success
+ flash[:notice] = "Your changes have been successfully committed"
+ redirect_to project_blob_path(@project, @id)
else
- flash[:notice] = "Your changes could not be commited, because the file has been changed"
+ flash[:alert] = result[:error]
render :show
end
end
private
- def edit_requirements
- @blob = @repository.blob_at(@commit.id, @path)
-
- unless @blob
- redirect_to project_blob_path(@project, @id), notice: "You can only edit text files"
- end
-
- allowed = if project.protected_branch? @ref
- can?(current_user, :push_code_to_protected_branches, project)
- else
- can?(current_user, :push_code, project)
- end
-
- return access_denied! unless allowed
+ def blob
+ @blob ||= @repository.blob_at(@commit.id, @path)
end
end
diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb
index 1a94dbab5ea..314d87df034 100644
--- a/app/controllers/projects/hooks_controller.rb
+++ b/app/controllers/projects/hooks_controller.rb
@@ -7,7 +7,7 @@ class Projects::HooksController < Projects::ApplicationController
layout "project_settings"
def index
- @hooks = @project.hooks.all
+ @hooks = @project.hooks
@hook = ProjectHook.new
end
@@ -18,7 +18,7 @@ class Projects::HooksController < Projects::ApplicationController
if @hook.valid?
redirect_to project_hooks_path(@project)
else
- @hooks = @project.hooks.all
+ @hooks = @project.hooks
render :index
end
end
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index e8f845b2d17..c58ac71277e 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -11,7 +11,7 @@ class Projects::IssuesController < Projects::ApplicationController
# Allow modify issue
before_filter :authorize_modify_issue!, only: [:edit, :update]
- respond_to :js, :html
+ respond_to :html
def index
terms = params['issue_search']
@@ -23,11 +23,18 @@ class Projects::IssuesController < Projects::ApplicationController
assignee_id, milestone_id = params[:assignee_id], params[:milestone_id]
@assignee = @project.team.find(assignee_id) if assignee_id.present? && !assignee_id.to_i.zero?
@milestone = @project.milestones.find(milestone_id) if milestone_id.present? && !milestone_id.to_i.zero?
+ sort_param = params[:sort] || 'newest'
+ @sort = sort_param.humanize unless sort_param.empty?
+
respond_to do |format|
- format.html # index.html.erb
- format.js
+ format.html
format.atom { render layout: false }
+ format.json do
+ render json: {
+ html: view_to_html_string("projects/issues/_issues")
+ }
+ end
end
end
@@ -42,13 +49,10 @@ class Projects::IssuesController < Projects::ApplicationController
def show
@note = @project.notes.new(noteable: @issue)
- @target_type = :issue
- @target_id = @issue.id
+ @notes = @issue.notes.inc_author.fresh
+ @noteable = @issue
- respond_to do |format|
- format.html
- format.js
- end
+ respond_with(@issue)
end
def create
@@ -70,6 +74,7 @@ class Projects::IssuesController < Projects::ApplicationController
def update
@issue.update_attributes(params[:issue].merge(author_id_of_changes: current_user.id))
+ @issue.reset_events_cache
respond_to do |format|
format.js
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 0cc09caf1d2..bba08bdd782 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -2,8 +2,8 @@ require 'gitlab/satellite/satellite'
class Projects::MergeRequestsController < Projects::ApplicationController
before_filter :module_enabled
- before_filter :merge_request, only: [:edit, :update, :show, :commits, :diffs, :automerge, :automerge_check, :ci_status]
- before_filter :closes_issues, only: [:edit, :update, :show, :commits, :diffs]
+ before_filter :merge_request, only: [:edit, :update, :show, :diffs, :automerge, :automerge_check, :ci_status]
+ before_filter :closes_issues, only: [:edit, :update, :show, :diffs]
before_filter :validates_merge_request, only: [:show, :diffs]
before_filter :define_show_vars, only: [:show, :diffs]
@@ -26,8 +26,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def show
respond_to do |format|
format.html
- format.js
-
format.diff { render text: @merge_request.to_diff(current_user) }
format.patch { render text: @merge_request.to_patch(current_user) }
end
@@ -44,6 +42,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
diff_line_count = Commit::diff_line_count(@merge_request.diffs)
@suppress_diff = Commit::diff_suppress?(@merge_request.diffs, diff_line_count) && !params[:force_show_diff]
@force_suppress_diff = Commit::diff_force_suppress?(@merge_request.diffs, diff_line_count)
+
+ respond_to do |format|
+ format.html
+ format.json { render json: { html: view_to_html_string("projects/merge_requests/show/_diffs") } }
+ end
end
def new
@@ -76,9 +79,30 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def update
+ # If we close MergeRequest we want to ignore validation
+ # so we can close broken one (Ex. fork project removed)
+ if params[:merge_request] == {"state_event"=>"close"}
+ @merge_request.allow_broken = true
+
+ if @merge_request.close
+ opts = { notice: 'Merge request was successfully closed.' }
+ else
+ opts = { alert: 'Failed to close merge request.' }
+ end
+
+ redirect_to [@merge_request.target_project, @merge_request], opts
+ return
+ end
+
+ # We dont allow change of source/target projects
+ # after merge request was created
+ params[:merge_request].delete(:source_project_id)
+ params[:merge_request].delete(:target_project_id)
+
if @merge_request.update_attributes(params[:merge_request].merge(author_id_of_changes: current_user.id))
@merge_request.reload_code
@merge_request.mark_as_unchecked
+ @merge_request.reset_events_cache
redirect_to [@merge_request.target_project, @merge_request], notice: 'Merge request was successfully updated.'
else
render "edit"
@@ -157,20 +181,26 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def validates_merge_request
+ # If source project was removed (Ex. mr from fork to origin)
+ return invalid_mr unless @merge_request.source_project
+
# Show git not found page
# if there is no saved commits between source & target branch
if @merge_request.commits.blank?
- # and if source target doesn't exist
- return invalid_mr unless @merge_request.target_project.repository.branch_names.include?(@merge_request.target_branch)
+ # and if target branch doesn't exist
+ return invalid_mr unless @merge_request.target_branch_exists?
- # or if source branch doesn't exist
- return invalid_mr unless @merge_request.source_project.repository.branch_names.include?(@merge_request.source_branch)
+ # or if source branch doesn't exist
+ return invalid_mr unless @merge_request.source_branch_exists?
end
end
def define_show_vars
# Build a note object for comment form
@note = @project.notes.new(noteable: @merge_request)
+ @notes = @merge_request.mr_and_commit_notes.inc_author.fresh
+ @discussions = Note.discussions_from_notes(@notes)
+ @noteable = @merge_request
# Get commits from repository
# or from cache if already merged
@@ -178,9 +208,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@allowed_to_merge = allowed_to_merge?
@show_merge_controls = @merge_request.opened? && @commits.any? && @allowed_to_merge
-
- @target_type = :merge_request
- @target_id = @merge_request.id
end
def allowed_to_merge?
diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb
index 3e88656cdf1..ecb1fc1d566 100644
--- a/app/controllers/projects/milestones_controller.rb
+++ b/app/controllers/projects/milestones_controller.rb
@@ -34,11 +34,6 @@ class Projects::MilestonesController < Projects::ApplicationController
@issues = @milestone.issues
@users = @milestone.participants.uniq
@merge_requests = @milestone.merge_requests
-
- respond_to do |format|
- format.html
- format.js
- end
end
def create
diff --git a/app/controllers/projects/new_tree_controller.rb b/app/controllers/projects/new_tree_controller.rb
new file mode 100644
index 00000000000..933a0cb876e
--- /dev/null
+++ b/app/controllers/projects/new_tree_controller.rb
@@ -0,0 +1,19 @@
+class Projects::NewTreeController < Projects::BaseTreeController
+ before_filter :require_branch_head
+
+ def show
+ end
+
+ def update
+ file_path = File.join(@path, File.basename(params[:file_name]))
+ result = Files::CreateContext.new(@project, current_user, params, @ref, file_path).execute
+
+ if result[:status] == :success
+ flash[:notice] = "Your changes have been successfully committed"
+ redirect_to project_blob_path(@project, File.join(@ref, file_path))
+ else
+ flash[:alert] = result[:error]
+ render :show
+ end
+ end
+end
diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb
index 8214163c315..261841f1ea2 100644
--- a/app/controllers/projects/notes_controller.rb
+++ b/app/controllers/projects/notes_controller.rb
@@ -2,62 +2,54 @@ class Projects::NotesController < Projects::ApplicationController
# Authorize
before_filter :authorize_read_note!
before_filter :authorize_write_note!, only: [:create]
-
- respond_to :js
+ before_filter :authorize_admin_note!, only: [:update, :destroy]
def index
@notes = Notes::LoadContext.new(project, current_user, params).execute
- @target_type = params[:target_type].camelize
- @target_id = params[:target_id]
- if params[:target_type] == "merge_request"
- @discussions = discussions_from_notes
+ notes_json = { notes: [] }
+
+ @notes.each do |note|
+ notes_json[:notes] << {
+ id: note.id,
+ html: note_to_html(note)
+ }
end
- respond_with(@notes)
+ render json: notes_json
end
def create
@note = Notes::CreateContext.new(project, current_user, params).execute
- @target_type = params[:target_type].camelize
- @target_id = params[:target_id]
respond_to do |format|
- format.html {redirect_to :back}
- format.js
+ format.json { render_note_json(@note) }
+ format.html { redirect_to :back }
end
end
- def destroy
- @note = @project.notes.find(params[:id])
- return access_denied! unless can?(current_user, :admin_note, @note)
- @note.destroy
+ def update
+ note.update_attributes(params[:note])
+ note.reset_events_cache
respond_to do |format|
- format.js { render nothing: true }
+ format.json { render_note_json(note) }
+ format.html { redirect_to :back }
end
end
- def update
- @note = @project.notes.find(params[:id])
- return access_denied! unless can?(current_user, :admin_note, @note)
-
- @note.update_attributes(params[:note])
+ def destroy
+ note.destroy
+ note.reset_events_cache
respond_to do |format|
- format.js do
- render js: { success: @note.valid?, id: @note.id, note: view_context.markdown(@note.note) }.to_json
- end
- format.html do
- redirect_to :back
- end
+ format.js { render nothing: true }
end
end
def delete_attachment
- @note = @project.notes.find(params[:id])
- @note.remove_attachment!
- @note.update_attribute(:attachment, nil)
+ note.remove_attachment!
+ note.update_attribute(:attachment, nil)
respond_to do |format|
format.js { render nothing: true }
@@ -68,35 +60,40 @@ class Projects::NotesController < Projects::ApplicationController
render text: view_context.markdown(params[:note])
end
- protected
+ private
- def discussion_notes_for(note)
- @notes.select do |other_note|
- note.discussion_id == other_note.discussion_id
- end
+ def note
+ @note ||= @project.notes.find(params[:id])
end
- def discussions_from_notes
- discussion_ids = []
- discussions = []
+ def note_to_html(note)
+ render_to_string(
+ "projects/notes/_note",
+ layout: false,
+ formats: [:html],
+ locals: { note: note }
+ )
+ end
- @notes.each do |note|
- next if discussion_ids.include?(note.discussion_id)
-
- # don't group notes for the main target
- if note_for_main_target?(note)
- discussions << [note]
- else
- discussions << discussion_notes_for(note)
- discussion_ids << note.discussion_id
- end
- end
+ def note_to_discussion_html(note)
+ render_to_string(
+ "projects/notes/_diff_notes_with_reply",
+ layout: false,
+ formats: [:html],
+ locals: { notes: [note] }
+ )
+ end
- discussions
+ def render_note_json(note)
+ render json: {
+ id: note.id,
+ discussion_id: note.discussion_id,
+ html: note_to_html(note),
+ discussion_html: note_to_discussion_html(note)
+ }
end
- # Helps to distinguish e.g. commit notes in mr notes list
- def note_for_main_target?(note)
- (@target_type.camelize == note.noteable_type && !note.for_diff_line?)
+ def authorize_admin_note!
+ return access_denied! unless can?(current_user, :admin_note, note)
end
end
diff --git a/app/controllers/projects/protected_branches_controller.rb b/app/controllers/projects/protected_branches_controller.rb
index 81531bb0ac0..cfc1bd99a20 100644
--- a/app/controllers/projects/protected_branches_controller.rb
+++ b/app/controllers/projects/protected_branches_controller.rb
@@ -6,7 +6,7 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
before_filter :authorize_admin_project!, only: [:destroy, :create]
def index
- @branches = @project.protected_branches.all
+ @branches = @project.protected_branches.to_a
@protected_branch = @project.protected_branches.new
end
diff --git a/app/controllers/projects/repositories_controller.rb b/app/controllers/projects/repositories_controller.rb
index 20e2a9311ee..f686db70bd4 100644
--- a/app/controllers/projects/repositories_controller.rb
+++ b/app/controllers/projects/repositories_controller.rb
@@ -16,7 +16,7 @@ class Projects::RepositoriesController < Projects::ApplicationController
storage_path = Rails.root.join("tmp", "repositories")
- file_path = @repository.archive_repo(params[:ref], storage_path)
+ file_path = @repository.archive_repo(params[:ref], storage_path, params[:format].downcase)
if file_path
# Send file to user
diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb
index dd0c1a57089..f93f2d5f9bb 100644
--- a/app/controllers/projects/snippets_controller.rb
+++ b/app/controllers/projects/snippets_controller.rb
@@ -48,8 +48,8 @@ class Projects::SnippetsController < Projects::ApplicationController
def show
@note = @project.notes.new(noteable: @snippet)
- @target_type = :snippet
- @target_id = @snippet.id
+ @notes = @snippet.notes.fresh
+ @noteable = @snippet
end
def destroy
diff --git a/app/controllers/projects/team_members_controller.rb b/app/controllers/projects/team_members_controller.rb
index b4b318fa59e..2c40b3c75f2 100644
--- a/app/controllers/projects/team_members_controller.rb
+++ b/app/controllers/projects/team_members_controller.rb
@@ -1,6 +1,6 @@
class Projects::TeamMembersController < Projects::ApplicationController
# Authorize
- before_filter :authorize_admin_project!
+ before_filter :authorize_admin_project!, except: :leave
layout "project_settings"
@@ -45,6 +45,15 @@ class Projects::TeamMembersController < Projects::ApplicationController
end
end
+ def leave
+ project.users_projects.find_by_user_id(current_user).destroy
+
+ respond_to do |format|
+ format.html { redirect_to :back }
+ format.js { render nothing: true }
+ end
+ end
+
def apply_import
giver = Project.find(params[:source_project_id])
status = @project.team.import(giver)
diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb
index 1150efbea9d..30c94ec6da0 100644
--- a/app/controllers/projects/tree_controller.rb
+++ b/app/controllers/projects/tree_controller.rb
@@ -1,12 +1,5 @@
# Controller for viewing a repository's file structure
-class Projects::TreeController < Projects::ApplicationController
- include ExtractsPath
-
- # Authorize
- before_filter :authorize_read_project!
- before_filter :authorize_code_access!
- before_filter :require_non_empty_project
-
+class Projects::TreeController < Projects::BaseTreeController
def show
return not_found! if tree.entries.empty?
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 7264128691e..e1c55e7d913 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -5,7 +5,7 @@ class ProjectsController < ApplicationController
# Authorize
before_filter :authorize_read_project!, except: [:index, :new, :create]
- before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer]
+ before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive]
before_filter :require_non_empty_project, only: [:blob, :tree, :graph]
layout 'navless', only: [:new, :create, :fork]
@@ -55,17 +55,13 @@ class ProjectsController < ApplicationController
end
def show
- return authenticate_user! unless @project.public || current_user
+ return authenticate_user! unless @project.public? || current_user
limit = (params[:limit] || 20).to_i
@events = @project.events.recent
@events = event_filter.apply_filter(@events)
@events = @events.limit(limit).offset(params[:offset] || 0)
- # Ensure project default branch is set if it possible
- # Normally it defined on push or during creation
- @project.discover_default_branch
-
respond_to do |format|
format.html do
if @project.empty_repo?
@@ -77,7 +73,7 @@ class ProjectsController < ApplicationController
render :show, layout: user_layout
end
end
- format.js
+ format.json { pager_json("events/_events", @events.count) }
end
end
@@ -120,6 +116,24 @@ class ProjectsController < ApplicationController
end
end
+ def archive
+ return access_denied! unless can?(current_user, :archive_project, project)
+ project.archive!
+
+ respond_to do |format|
+ format.html { redirect_to @project }
+ end
+ end
+
+ def unarchive
+ return access_denied! unless can?(current_user, :archive_project, project)
+ project.unarchive!
+
+ respond_to do |format|
+ format.html { redirect_to @project }
+ end
+ end
+
private
def set_title
diff --git a/app/controllers/public/projects_controller.rb b/app/controllers/public/projects_controller.rb
index 87e903a1d2d..d7297161c22 100644
--- a/app/controllers/public/projects_controller.rb
+++ b/app/controllers/public/projects_controller.rb
@@ -6,8 +6,9 @@ class Public::ProjectsController < ApplicationController
layout 'public'
def index
- @projects = Project.public_only
+ @projects = Project.public_or_internal_only(current_user)
@projects = @projects.search(params[:search]) if params[:search].present?
- @projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]).per(20)
+ @projects = @projects.sort(@sort = params[:sort])
+ @projects = @projects.includes(:namespace).page(params[:page]).per(20)
end
end
diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb
index f5c3bb133ed..2a2748dc1fb 100644
--- a/app/controllers/search_controller.rb
+++ b/app/controllers/search_controller.rb
@@ -14,7 +14,7 @@ class SearchController < ApplicationController
project_ids.select! { |id| id == project_id.to_i}
end
- result = SearchContext.new(project_ids, params).execute
+ result = SearchContext.new(project_ids, current_user, params).execute
@projects = result[:projects]
@merge_requests = result[:merge_requests]
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index e2d97901410..cd3afd5cc32 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -62,7 +62,7 @@ module ApplicationHelper
size = 40 if size.nil? || size <= 0
if !Gitlab.config.gravatar.enabled || user_email.blank?
- 'no_avatar.png'
+ '/assets/no_avatar.png'
else
gravatar_url = request.ssl? || gitlab_config.https ? Gitlab.config.gravatar.ssl_url : Gitlab.config.gravatar.plain_url
user_email.strip!
@@ -72,7 +72,7 @@ module ApplicationHelper
def last_commit(project)
if project.repo_exists?
- time_ago_in_words(project.repository.commit.committed_date) + " ago"
+ time_ago_with_tooltip(project.repository.commit.committed_date)
else
"Never"
end
@@ -84,8 +84,8 @@ module ApplicationHelper
repository = @project.repository
options = [
- ["Branch", repository.branch_names ],
- [ "Tag", repository.tag_names ]
+ ["Branches", repository.branch_names],
+ ["Tags", repository.tag_names]
]
# If reference is commit id -
@@ -126,6 +126,9 @@ module ApplicationHelper
# Skip if user already created appropriate MR
return false if project.merge_requests.where(source_branch: event.branch_name).opened.any?
+ # Skip if user removed branch right after that
+ return false unless project.repository.branch_names.include?(event.branch_name)
+
true
end
@@ -133,14 +136,6 @@ module ApplicationHelper
Digest::SHA1.hexdigest string
end
- def project_last_activity project
- if project.last_activity_at
- time_ago_in_words(project.last_activity_at) + " ago"
- else
- "Never"
- end
- end
-
def authbutton(provider, size = 64)
file_name = "#{provider.to_s.split('_').first}_#{size}.png"
image_tag("authbuttons/#{file_name}",
@@ -184,14 +179,6 @@ module ApplicationHelper
Gitlab.config.extra
end
- def public_icon
- content_tag :i, nil, class: 'icon-globe cblue'
- end
-
- def private_icon
- content_tag :i, nil, class: 'icon-lock cgreen'
- end
-
def search_placeholder
if @project && @project.persisted?
"Search in this project"
@@ -208,4 +195,26 @@ module ApplicationHelper
line += "..." if lines.size > 1
line
end
+
+ def broadcast_message
+ BroadcastMessage.current
+ end
+
+ def highlight_js(&block)
+ string = capture(&block)
+
+ content_tag :div, class: user_color_scheme_class do
+ Pygments::Lexer[:js].highlight(string).html_safe
+ end
+ end
+
+ def time_ago_with_tooltip(date, placement = 'top', html_class = 'time_ago')
+ capture_haml do
+ haml_tag :time, date.to_s,
+ class: html_class, datetime: date.getutc.iso8601, title: date.stamp("Aug 21, 2011 9:23pm"),
+ data: { toggle: 'tooltip', placement: placement }
+
+ haml_tag :script, "$('." + html_class + "').timeago().tooltip()"
+ end.html_safe
+ end
end
diff --git a/app/helpers/broadcast_messages_helper.rb b/app/helpers/broadcast_messages_helper.rb
new file mode 100644
index 00000000000..29ff47663da
--- /dev/null
+++ b/app/helpers/broadcast_messages_helper.rb
@@ -0,0 +1,9 @@
+module BroadcastMessagesHelper
+ def broadcast_styling(broadcast_message)
+ if(broadcast_message.color || broadcast_message.font)
+ "background-color:#{broadcast_message.color};color:#{broadcast_message.font}"
+ else
+ ""
+ end
+ end
+end
diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb
index c340eb30be1..663369e4584 100644
--- a/app/helpers/commits_helper.rb
+++ b/app/helpers/commits_helper.rb
@@ -105,6 +105,10 @@ module CommitsHelper
branches.sort.map { |branch| link_to(branch, project_tree_path(project, branch)) }.join(", ").html_safe
end
+ def get_old_file(project, commit, diff)
+ project.repository.blob_at(commit.parent_id, diff.old_path) if commit.parent_id
+ end
+
protected
# Private: Returns a link to a person. If the person has a matching user and
@@ -125,7 +129,9 @@ module CommitsHelper
source_name
end
- user = User.where('name like ? or email like ?', source_name, source_email).first
+ # Prefer email match over name match
+ user = User.where(email: source_email).first
+ user ||= User.where(name: source_name).first
options = {
class: "commit-#{options[:source]}-link has_tooltip",
diff --git a/app/helpers/compare_helper.rb b/app/helpers/compare_helper.rb
index ea2540bf385..5ff19b88293 100644
--- a/app/helpers/compare_helper.rb
+++ b/app/helpers/compare_helper.rb
@@ -1,6 +1,8 @@
module CompareHelper
def compare_to_mr_button?
- params[:from].present? && params[:to].present? &&
+ @project.merge_requests_enabled &&
+ params[:from].present? &&
+ params[:to].present? &&
@repository.branch_names.include?(params[:from]) &&
@repository.branch_names.include?(params[:to]) &&
params[:from] != params[:to] &&
diff --git a/app/helpers/dashboard_helper.rb b/app/helpers/dashboard_helper.rb
index 35c7bcbd2cf..7f86a833cb0 100644
--- a/app/helpers/dashboard_helper.rb
+++ b/app/helpers/dashboard_helper.rb
@@ -2,6 +2,7 @@ module DashboardHelper
def filter_path(entity, options={})
exist_opts = {
status: params[:status],
+ scope: params[:scope],
project_id: params[:project_id],
}
@@ -12,18 +13,26 @@ module DashboardHelper
path
end
- def entities_per_project project, entity
- items = project.items_for(entity)
+ def entities_per_project(project, entity)
+ case entity.to_sym
+ when :issue then @issues.where(project_id: project.id)
+ when :merge_request then @merge_requests.where(target_project_id: project.id)
+ else
+ []
+ end.count
+ end
+
+ def projects_dashboard_filter_path(options={})
+ exist_opts = {
+ sort: params[:sort],
+ scope: params[:scope],
+ group: params[:group],
+ }
- items = case params[:status]
- when 'closed'
- items.closed
- when 'all'
- items
- else
- items.opened
- end
+ options = exist_opts.merge(options)
- items.cared(current_user).count
+ path = request.path
+ path << "?#{options.to_param}"
+ path
end
end
diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb
index cd8761a6113..929f9a9c381 100644
--- a/app/helpers/events_helper.rb
+++ b/app/helpers/events_helper.rb
@@ -90,27 +90,23 @@ module EventsHelper
if event.note? && event.note_commit?
project_commit_path(event.project, event.note_target)
else
- url_for([event.project, event.note_target])
+ polymorphic_path([event.project, event.note_target], anchor: dom_id(event.target))
end
end
def event_note_title_html(event)
if event.note_target
if event.note_commit?
- link_to project_commit_path(event.project, event.note_commit_id), class: "commit_short_id" do
+ link_to project_commit_path(event.project, event.note_commit_id, anchor: dom_id(event.target)), class: "commit_short_id" do
"#{event.note_target_type} #{event.note_short_commit_id}"
end
elsif event.note_project_snippet?
link_to(project_snippet_path(event.project, event.note_target)) do
- content_tag :strong do
- "#{event.note_target_type} ##{truncate event.note_target_id}"
- end
+ "#{event.note_target_type} ##{truncate event.note_target_id}"
end
else
link_to event_note_target_path(event) do
- content_tag :strong do
- "#{event.note_target_type} ##{truncate event.note_target_iid}"
- end
+ "#{event.note_target_type} ##{truncate event.note_target_iid}"
end
end
elsif event.wall_note?
@@ -127,4 +123,10 @@ module EventsHelper
text = truncate(text, length: 150)
sanitize(markdown(text), tags: %w(a img b pre p))
end
+
+ def event_commit_title(message)
+ escape_once(truncate(message.split("\n").first, length: 70))
+ rescue
+ "--broken encoding"
+ end
end
diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb
index b34f7033536..8894a01eaea 100644
--- a/app/helpers/gitlab_markdown_helper.rb
+++ b/app/helpers/gitlab_markdown_helper.rb
@@ -64,7 +64,9 @@ module GitlabMarkdownHelper
# ref - name of the branch or reference, eg. stable
# requested_path - path of request, eg. doc/api/README.md, used in special case when path is pointing to the .md file were the original request is coming from
# wiki - whether the markdown is from wiki or not
- def create_relative_links(text, project_path_with_namespace, ref, requested_path, wiki = false)
+ def create_relative_links(text, project, ref, requested_path, wiki = false)
+ @path_to_satellite = project.satellite.path
+ project_path_with_namespace = project.path_with_namespace
paths = extract_paths(text)
paths.each do |file_path|
new_path = rebuild_path(project_path_with_namespace, file_path, requested_path, ref)
@@ -145,13 +147,18 @@ module GitlabMarkdownHelper
def file_exists?(path)
return false if path.nil? || path.empty?
- File.exists?(Rails.root.join(path))
+ File.exists?(path_on_fs(path))
end
# Check if the path is pointing to a directory(tree) or a file(blob)
# eg. doc/api is directory and doc/README.md is file
def local_path(path)
- File.directory?(Rails.root.join(path)) ? "tree" : "blob"
+ File.directory?(path_on_fs(path)) ? "tree" : "blob"
+ end
+
+ # Path to the file in the satellites repository on the filesystem
+ def path_on_fs(path)
+ [@path_to_satellite, path].join("/")
end
# We will assume that if no ref exists we can point to master
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index 8573c59dc94..7c09273d53e 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -2,4 +2,23 @@ module GroupsHelper
def remove_user_from_group_message(group, user)
"You are going to remove #{user.name} from #{group.name} Group. Are you sure?"
end
+
+ def group_head_title
+ title = @group.name
+
+ title = if current_action?(:issues)
+ "Issues - " + title
+ elsif current_action?(:merge_requests)
+ "Merge requests - " + title
+ elsif current_action?(:members)
+ "Members - " + title
+ elsif current_action?(:edit)
+ "Settings - " + title
+ else
+ title
+ end
+
+ title
+
+ end
end
diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb
new file mode 100644
index 00000000000..53d4a8f2e6e
--- /dev/null
+++ b/app/helpers/icons_helper.rb
@@ -0,0 +1,21 @@
+module IconsHelper
+ def boolean_to_icon(value)
+ if value.to_s == "true"
+ content_tag :i, nil, class: 'icon-ok cgreen'
+ else
+ content_tag :i, nil, class: 'icon-off clgray'
+ end
+ end
+
+ def public_icon
+ content_tag :i, nil, class: 'icon-globe'
+ end
+
+ def internal_icon
+ content_tag :i, nil, class: 'icon-shield'
+ end
+
+ def private_icon
+ content_tag :i, nil, class: 'icon-lock'
+ end
+end
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index 2221583912d..cdba6ce84dc 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -68,4 +68,20 @@ module IssuesHelper
false
end
end
+
+ def bulk_update_milestone_options
+ options_for_select(["None (backlog)", nil]) + options_from_collection_for_select(project_active_milestones, "id", "title", params[:milestone_id])
+ end
+
+ def bulk_update_assignee_options
+ options_for_select(["None (unassigned)", nil]) + options_from_collection_for_select(@project.team.members, "id", "name", params[:assignee_id])
+ end
+
+ def assignee_options object
+ options_from_collection_for_select(@project.team.members.sort_by(&:name), 'id', 'name', object.assignee_id)
+ end
+
+ def milestone_options object
+ options_from_collection_for_select(@project.milestones.active, 'id', 'title', object.milestone_id)
+ end
end
diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb
index 9a6aea85ee9..d1eb3808f9e 100644
--- a/app/helpers/labels_helper.rb
+++ b/app/helpers/labels_helper.rb
@@ -16,11 +16,11 @@ module LabelsHelper
when *klass.warning_labels
'label-warning'
when *klass.neutral_labels
- 'label-inverse'
+ 'label-primary'
when *klass.positive_labels
'label-success'
when *klass.important_labels
- 'label-important'
+ 'label-danger'
else
'label-info'
end
diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb
index 4ba48aa4339..5e3f82fe9ce 100644
--- a/app/helpers/merge_requests_helper.rb
+++ b/app/helpers/merge_requests_helper.rb
@@ -36,7 +36,7 @@ module MergeRequestsHelper
def merge_path_description(merge_request, separator)
if merge_request.for_fork?
- "Project:Branches: #{@merge_request.source_project.path_with_namespace}:#{@merge_request.source_branch} #{separator} #{@merge_request.target_project.path_with_namespace}:#{@merge_request.target_branch}"
+ "Project:Branches: #{@merge_request.source_project_path}:#{@merge_request.source_branch} #{separator} #{@merge_request.target_project.path_with_namespace}:#{@merge_request.target_branch}"
else
"Branches: #{@merge_request.source_branch} #{separator} #{@merge_request.target_branch}"
end
diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb
index dc88e178360..c363c7ffd74 100644
--- a/app/helpers/namespaces_helper.rb
+++ b/app/helpers/namespaces_helper.rb
@@ -1,7 +1,7 @@
module NamespacesHelper
def namespaces_options(selected = :current_user, scope = :default)
- groups = current_user.owned_groups.select {|n| n.type == 'Group'}
- users = current_user.namespaces.reject {|n| n.type == 'Group'}
+ groups = current_user.owned_groups
+ users = [current_user.namespace]
group_opts = ["Groups", groups.sort_by(&:human_name).map {|g| [g.human_name, g.id]} ]
users_opts = [ "Users", users.sort_by(&:human_name).map {|u| [u.human_name, u.id]} ]
@@ -16,4 +16,13 @@ module NamespacesHelper
grouped_options_for_select(options, selected)
end
+
+ def namespace_select_tag(id, opts = {})
+ css_class = "ajax-namespace-select "
+ css_class << "multiselect " if opts[:multiple]
+ css_class << (opts[:class] || '')
+ value = opts[:selected] || ''
+
+ hidden_field_tag(id, value, class: css_class)
+ end
end
diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb
index a3ec4cca59d..695147cd6db 100644
--- a/app/helpers/notes_helper.rb
+++ b/app/helpers/notes_helper.rb
@@ -1,7 +1,7 @@
module NotesHelper
# Helps to distinguish e.g. commit notes in mr notes list
def note_for_main_target?(note)
- (@target_type.camelize == note.noteable_type && !note.for_diff_line?)
+ (@noteable.class.name == note.noteable_type && !note.for_diff_line?)
end
def note_target_fields
@@ -21,18 +21,25 @@ module NotesHelper
end
end
- def loading_more_notes?
- params[:loading_more].present?
- end
-
- def loading_new_notes?
- params[:loading_new].present?
- end
-
def note_timestamp(note)
# Shows the created at time and the updated at time if different
- ts = "#{time_ago_in_words(note.created_at)} ago"
- ts << content_tag(:small, " (Edited #{time_ago_in_words(note.updated_at)} ago)") if note.updated_at != note.created_at
+ ts = "#{time_ago_with_tooltip(note.created_at, 'bottom', 'note_created_ago')}"
+ if note.updated_at != note.created_at
+ ts << capture_haml do
+ haml_tag :small do
+ haml_concat " (Edited #{time_ago_with_tooltip(note.updated_at, 'bottom', 'note_edited_ago')})"
+ end
+ end
+ end
ts.html_safe
end
+
+ def noteable_json(noteable)
+ {
+ id: noteable.id,
+ class: noteable.class.name,
+ resources: noteable.class.table_name,
+ project_id: noteable.project.id,
+ }.to_json
+ end
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 864f6c9af95..d3c69bdc620 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -45,7 +45,10 @@ module ProjectsHelper
link_to(simple_sanitize(project.group.name), group_path(project.group)) + " / " + project.name
end
else
- project.name
+ owner = project.namespace.owner
+ content_tag :span do
+ link_to(simple_sanitize(owner.name), user_path(owner)) + " / " + project.name
+ end
end
end
@@ -67,6 +70,8 @@ module ProjectsHelper
scope: params[:scope],
label_name: params[:label_name],
milestone_id: params[:milestone_id],
+ assignee_id: params[:assignee_id],
+ sort: params[:sort],
}
options = exist_opts.merge(options)
@@ -77,10 +82,10 @@ module ProjectsHelper
end
def project_active_milestones
- @project.milestones.active.order("due_date, title ASC").all
+ @project.milestones.active.order("due_date, title ASC")
end
- def project_issues_trackers
+ def project_issues_trackers(current_tracker = nil)
values = Project.issues_tracker.values.map do |tracker_key|
if tracker_key.to_sym == :gitlab
['GitLab', tracker_key]
@@ -89,7 +94,7 @@ module ProjectsHelper
end
end
- options_for_select(values)
+ options_for_select(values, current_tracker)
end
private
@@ -132,12 +137,62 @@ module ProjectsHelper
end
end
- def repository_size
- "#{@project.repository.size} MB"
+ def repository_size(project = nil)
+ "#{(project || @project).repository.size} MB"
rescue
# In order to prevent 500 error
# when application cannot allocate memory
# to calculate repo size - just show 'Unknown'
'unknown'
end
+
+ def project_head_title
+ title = @project.name_with_namespace
+
+ title = if current_controller?(:tree)
+ "#{@project.path}\/#{@path} at #{@ref} - " + title
+ elsif current_controller?(:issues)
+ if current_action?(:show)
+ "Issue ##{@issue.iid} - " + title
+ else
+ "Issues - " + title
+ end
+ elsif current_controller?(:blob)
+ "#{@project.path}\/#{@blob.path} at #{@ref} - " + title
+ elsif current_controller?(:commits)
+ "Commits at #{@ref} - " + title
+ elsif current_controller?(:merge_requests)
+ if current_action?(:show)
+ "Merge request ##{@merge_request.iid} - " + title
+ else
+ "Merge requests - " + title
+ end
+ elsif current_controller?(:wikis)
+ "Wiki - " + title
+ elsif current_controller?(:network)
+ "Network graph - " + title
+ elsif current_controller?(:graphs)
+ "Graphs - " + title
+ else
+ title
+ end
+
+ title
+ end
+
+ def default_url_to_repo
+ current_user ? @project.url_to_repo : @project.http_url_to_repo
+ end
+
+ def default_clone_protocol
+ current_user ? "ssh" : "http"
+ end
+
+ def project_last_activity(project)
+ if project.last_activity_at
+ time_ago_with_tooltip(project.last_activity_at, 'bottom', 'last_activity_time_ago')
+ else
+ "Never"
+ end
+ end
end
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index 33c5e4fb9db..f24156e4d85 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -1,14 +1,16 @@
module SearchHelper
def search_autocomplete_source
return unless current_user
-
[
groups_autocomplete,
projects_autocomplete,
+ public_projects_autocomplete,
default_autocomplete,
project_autocomplete,
help_autocomplete
- ].flatten.to_json
+ ].flatten.uniq do |item|
+ item[:label]
+ end.to_json
end
private
@@ -71,7 +73,14 @@ module SearchHelper
# Autocomplete results for the current user's projects
def projects_autocomplete
- current_user.authorized_projects.map do |p|
+ current_user.authorized_projects.non_archived.map do |p|
+ { label: "project: #{simple_sanitize(p.name_with_namespace)}", url: project_path(p) }
+ end
+ end
+
+ # Autocomplete results for the current user's projects
+ def public_projects_autocomplete
+ Project.public_or_internal_only(current_user).non_archived.map do |p|
{ label: "project: #{simple_sanitize(p.name_with_namespace)}", url: project_path(p) }
end
end
diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb
index ce675872264..bd373c5f3cf 100644
--- a/app/helpers/tab_helper.rb
+++ b/app/helpers/tab_helper.rb
@@ -92,7 +92,12 @@ module TabHelper
def nav_tab key, value, &block
o = {}
o[:class] = ""
- o[:class] << " active" if params[key] == value
+
+ if value.nil?
+ o[:class] << " active" if params[key].blank?
+ else
+ o[:class] << " active" if params[key] == value
+ end
if block_given?
content_tag(:li, capture(&block), o)
diff --git a/app/helpers/visibility_level_helper.rb b/app/helpers/visibility_level_helper.rb
new file mode 100644
index 00000000000..81e10f3685c
--- /dev/null
+++ b/app/helpers/visibility_level_helper.rb
@@ -0,0 +1,49 @@
+module VisibilityLevelHelper
+ def visibility_level_color(level)
+ case level
+ when Gitlab::VisibilityLevel::PRIVATE
+ 'cgreen'
+ when Gitlab::VisibilityLevel::INTERNAL
+ 'camber'
+ when Gitlab::VisibilityLevel::PUBLIC
+ 'cblue'
+ end
+ end
+
+ def visibility_level_description(level)
+ capture_haml do
+ haml_tag :span do
+ case level
+ when Gitlab::VisibilityLevel::PRIVATE
+ haml_concat "Project access must be granted explicitly for each user."
+ when Gitlab::VisibilityLevel::INTERNAL
+ haml_concat "The project can be cloned by"
+ haml_concat "any logged in user."
+ when Gitlab::VisibilityLevel::PUBLIC
+ haml_concat "The project can be cloned"
+ haml_concat "without any"
+ haml_concat "authentication."
+ end
+ end
+ end
+ end
+
+ def visibility_level_icon(level)
+ case level
+ when Gitlab::VisibilityLevel::PRIVATE
+ private_icon
+ when Gitlab::VisibilityLevel::INTERNAL
+ internal_icon
+ when Gitlab::VisibilityLevel::PUBLIC
+ public_icon
+ end
+ end
+
+ def visibility_level_label(level)
+ Project.visibility_levels.key(level)
+ end
+
+ def restricted_visibility_levels
+ current_user.is_admin? ? [] : gitlab_config.restricted_visibility_levels
+ end
+end
diff --git a/app/mailers/emails/groups.rb b/app/mailers/emails/groups.rb
index 2e9d28981e3..1c8ae122c46 100644
--- a/app/mailers/emails/groups.rb
+++ b/app/mailers/emails/groups.rb
@@ -5,7 +5,7 @@ module Emails
@group = @membership.group
mail(to: @membership.user.email,
- subject: subject("access to group was granted"))
+ subject: subject("Access to group was granted"))
end
end
end
diff --git a/app/mailers/emails/issues.rb b/app/mailers/emails/issues.rb
index 6eda88c7921..5abdf99529c 100644
--- a/app/mailers/emails/issues.rb
+++ b/app/mailers/emails/issues.rb
@@ -3,14 +3,14 @@ module Emails
def new_issue_email(recipient_id, issue_id)
@issue = Issue.find(issue_id)
@project = @issue.project
- mail(to: recipient(recipient_id), subject: subject("new issue ##{@issue.iid}", @issue.title))
+ mail(to: recipient(recipient_id), subject: subject("New issue ##{@issue.iid}", @issue.title))
end
def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id)
@issue = Issue.find(issue_id)
@previous_assignee = User.find_by_id(previous_assignee_id) if previous_assignee_id
@project = @issue.project
- mail(to: recipient(recipient_id), subject: subject("changed issue ##{@issue.iid}", @issue.title))
+ mail(to: recipient(recipient_id), subject: subject("Changed issue ##{@issue.iid}", @issue.title))
end
def closed_issue_email(recipient_id, issue_id, updated_by_user_id)
@@ -27,7 +27,7 @@ module Emails
@project = @issue.project
@updated_by = User.find updated_by_user_id
mail(to: recipient(recipient_id),
- subject: subject("changed issue ##{@issue.iid}", @issue.title))
+ subject: subject("Changed issue ##{@issue.iid}", @issue.title))
end
end
end
diff --git a/app/mailers/emails/merge_requests.rb b/app/mailers/emails/merge_requests.rb
index 57c1925fa47..25b8bf755e2 100644
--- a/app/mailers/emails/merge_requests.rb
+++ b/app/mailers/emails/merge_requests.rb
@@ -2,24 +2,28 @@ module Emails
module MergeRequests
def new_merge_request_email(recipient_id, merge_request_id)
@merge_request = MergeRequest.find(merge_request_id)
- mail(to: recipient(recipient_id), subject: subject("new merge request !#{@merge_request.iid}", @merge_request.title))
+ @project = @merge_request.project
+ mail(to: recipient(recipient_id), subject: subject("New merge request ##{@merge_request.iid}", @merge_request.title))
end
def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id)
@merge_request = MergeRequest.find(merge_request_id)
@previous_assignee = User.find_by_id(previous_assignee_id) if previous_assignee_id
- mail(to: recipient(recipient_id), subject: subject("changed merge request !#{@merge_request.iid}", @merge_request.title))
+ @project = @merge_request.project
+ mail(to: recipient(recipient_id), subject: subject("Changed merge request ##{@merge_request.iid}", @merge_request.title))
end
def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id)
@merge_request = MergeRequest.find(merge_request_id)
@updated_by = User.find updated_by_user_id
- mail(to: recipient(recipient_id), subject: subject("Closed merge request !#{@merge_request.iid}", @merge_request.title))
+ @project = @merge_request.project
+ mail(to: recipient(recipient_id), subject: subject("Closed merge request ##{@merge_request.iid}", @merge_request.title))
end
def merged_merge_request_email(recipient_id, merge_request_id)
@merge_request = MergeRequest.find(merge_request_id)
- mail(to: recipient(recipient_id), subject: subject("Accepted merge request !#{@merge_request.iid}", @merge_request.title))
+ @project = @merge_request.project
+ mail(to: recipient(recipient_id), subject: subject("Accepted merge request ##{@merge_request.iid}", @merge_request.title))
end
end
diff --git a/app/mailers/emails/notes.rb b/app/mailers/emails/notes.rb
index ba4f0dd862c..e967cf6dc73 100644
--- a/app/mailers/emails/notes.rb
+++ b/app/mailers/emails/notes.rb
@@ -4,27 +4,27 @@ module Emails
@note = Note.find(note_id)
@commit = @note.noteable
@project = @note.project
- mail(to: recipient(recipient_id), subject: subject("note for commit #{@commit.short_id}", @commit.title))
+ mail(to: recipient(recipient_id), subject: subject("Note for commit #{@commit.short_id}", @commit.title))
end
def note_issue_email(recipient_id, note_id)
@note = Note.find(note_id)
@issue = @note.noteable
@project = @note.project
- mail(to: recipient(recipient_id), subject: subject("note for issue ##{@issue.iid}"))
+ mail(to: recipient(recipient_id), subject: subject("Note for issue ##{@issue.iid}"))
end
def note_merge_request_email(recipient_id, note_id)
@note = Note.find(note_id)
@merge_request = @note.noteable
@project = @note.project
- mail(to: recipient(recipient_id), subject: subject("note for merge request ##{@merge_request.iid}"))
+ mail(to: recipient(recipient_id), subject: subject("Note for merge request ##{@merge_request.iid}"))
end
def note_wall_email(recipient_id, note_id)
@note = Note.find(note_id)
@project = @note.project
- mail(to: recipient(recipient_id), subject: subject("note on wall"))
+ mail(to: recipient(recipient_id), subject: subject("Note on wall"))
end
end
end
diff --git a/app/mailers/emails/projects.rb b/app/mailers/emails/projects.rb
index 4d5fe9ef614..df21d7b5b02 100644
--- a/app/mailers/emails/projects.rb
+++ b/app/mailers/emails/projects.rb
@@ -4,14 +4,24 @@ module Emails
@users_project = UsersProject.find user_project_id
@project = @users_project.project
mail(to: @users_project.user.email,
- subject: subject("access to project was granted"))
+ subject: subject("Access to project was granted"))
end
def project_was_moved_email(project_id, user_id)
@user = User.find user_id
@project = Project.find project_id
mail(to: @user.email,
- subject: subject("project was moved"))
+ subject: subject("Project was moved"))
+ end
+
+ def repository_push_email(project_id, recipient, author_id, branch, compare)
+ @project = Project.find(project_id)
+ @author = User.find(author_id)
+ @commits = Commit.decorate(compare.commits)
+ @diffs = compare.diffs
+ @branch = branch
+
+ mail(to: recipient, subject: subject("New push to repository"))
end
end
end
diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb
index 2f7be00c33e..aec4c57cba8 100644
--- a/app/mailers/notify.rb
+++ b/app/mailers/notify.rb
@@ -16,6 +16,7 @@ class Notify < ActionMailer::Base
default_url_options[:script_name] = Gitlab.config.gitlab.relative_url_root
default from: Gitlab.config.gitlab.email_from
+ default reply_to: "noreply@#{Gitlab.config.gitlab.host}"
# Just send email with 3 seconds delay
def self.delay
diff --git a/app/models/ability.rb b/app/models/ability.rb
index 85476089145..cf925141f2d 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -29,7 +29,7 @@ class Ability
nil
end
- if project && project.public
+ if project && project.public?
[
:read_project,
:read_wiki,
@@ -59,37 +59,41 @@ class Ability
# Rules based on role in project
if team.masters.include?(user)
- rules << project_master_rules
+ rules += project_master_rules
elsif team.developers.include?(user)
- rules << project_dev_rules
+ rules += project_dev_rules
elsif team.reporters.include?(user)
- rules << project_report_rules
+ rules += project_report_rules
elsif team.guests.include?(user)
- rules << project_guest_rules
+ rules += project_guest_rules
end
- if project.public?
- rules << public_project_rules
+ if project.public? || project.internal?
+ rules += public_project_rules
end
if project.owner == user || user.admin?
- rules << project_admin_rules
+ rules += project_admin_rules
end
if project.group && project.group.has_owner?(user)
- rules << project_admin_rules
+ rules += project_admin_rules
end
- rules.flatten
+ if project.archived?
+ rules -= project_archived_rules
+ end
+
+ rules
end
def public_project_rules
project_guest_rules + [
:download_code,
- :fork_project,
+ :fork_project
]
end
@@ -125,6 +129,16 @@ class Ability
]
end
+ def project_archived_rules
+ [
+ :write_merge_request,
+ :push_code,
+ :push_code_to_protected_branches,
+ :modify_merge_request,
+ :admin_merge_request
+ ]
+ end
+
def project_master_rules
project_dev_rules + [
:push_code_to_protected_branches,
@@ -145,9 +159,10 @@ class Ability
def project_admin_rules
project_master_rules + [
:change_namespace,
- :change_public_mode,
+ :change_visibility_level,
:rename_project,
- :remove_project
+ :remove_project,
+ :archive_project
]
end
@@ -160,7 +175,7 @@ class Ability
# Only group owner and administrators can manage group
if group.has_owner?(user) || user.admin?
- rules << [
+ rules += [
:manage_group,
:manage_namespace
]
@@ -174,7 +189,7 @@ class Ability
# Only namespace owner and administrators can manage it
if namespace.owner == user || user.admin?
- rules << [
+ rules += [
:manage_namespace
]
end
diff --git a/app/models/broadcast_message.rb b/app/models/broadcast_message.rb
new file mode 100644
index 00000000000..bebe0da9c61
--- /dev/null
+++ b/app/models/broadcast_message.rb
@@ -0,0 +1,29 @@
+# == Schema Information
+#
+# Table name: broadcast_messages
+#
+# id :integer not null, primary key
+# message :text default(""), not null
+# starts_at :datetime
+# ends_at :datetime
+# alert_type :integer
+# created_at :datetime not null
+# updated_at :datetime not null
+# color :string(255)
+# font :string(255)
+#
+
+class BroadcastMessage < ActiveRecord::Base
+ attr_accessible :alert_type, :color, :ends_at, :font, :message, :starts_at
+
+ validates :message, presence: true
+ validates :starts_at, presence: true
+ validates :ends_at, presence: true
+
+ validates :color, format: { with: /\A\#[0-9A-Fa-f]{6}+\Z/ }, allow_blank: true
+ validates :font, format: { with: /\A\#[0-9A-Fa-f]{6}+\Z/ }, allow_blank: true
+
+ def self.current
+ where("ends_at > :now AND starts_at < :now", now: Time.zone.now).last
+ end
+end
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 7f820f950b0..58bf621f91b 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -111,4 +111,11 @@ module Issuable
end
users.concat(mentions.reduce([], :|)).uniq
end
+
+ def to_hook_data
+ {
+ object_kind: self.class.name.underscore,
+ object_attributes: self.attributes
+ }
+ end
end
diff --git a/app/models/event.rb b/app/models/event.rb
index 095a06c956b..ddb863c1be2 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -18,7 +18,7 @@ class Event < ActiveRecord::Base
attr_accessible :project, :action, :data, :author_id, :project_id,
:target_id, :target_type
- default_scope where("author_id IS NOT NULL")
+ default_scope { where.not(author_id: nil) }
CREATED = 1
UPDATED = 2
@@ -168,7 +168,7 @@ class Event < ActiveRecord::Base
end
def valid_push?
- data[:ref]
+ data[:ref] && ref_name.present?
rescue => ex
false
end
@@ -223,7 +223,7 @@ class Event < ActiveRecord::Base
# Max 20 commits from push DESC
def commits
- @commits ||= data[:commits].reverse
+ @commits ||= (data[:commits] || []).reverse
end
def commits_count
diff --git a/app/models/gollum_wiki.rb b/app/models/gollum_wiki.rb
index 05fc2a745b4..7ebaaff61cb 100644
--- a/app/models/gollum_wiki.rb
+++ b/app/models/gollum_wiki.rb
@@ -33,7 +33,7 @@ class GollumWiki
end
def http_url_to_repo
- http_url = [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('')
+ [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('')
end
# Returns the Gollum::Wiki object.
diff --git a/app/models/group.rb b/app/models/group.rb
index 0ee058be15d..0b64d5b4f7f 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -26,7 +26,7 @@ class Group < Namespace
def add_users(user_ids, group_access)
user_ids.compact.each do |user_id|
- user = self.users_groups.find_or_initialize_by_user_id(user_id)
+ user = self.users_groups.find_or_initialize_by(user_id: user_id)
user.update_attributes(group_access: group_access)
end
end
diff --git a/app/models/issue.rb b/app/models/issue.rb
index f3ec322126f..6580c5004af 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -21,12 +21,14 @@ class Issue < ActiveRecord::Base
include Issuable
include InternalId
+ ActsAsTaggableOn.strict_case_match = true
+
belongs_to :project
validates :project, presence: true
scope :of_group, ->(group) { where(project_id: group.project_ids) }
scope :of_user_team, ->(team) { where(project_id: team.project_ids, assignee_id: team.member_ids) }
- scope :opened, -> { with_state(:opened) }
+ scope :opened, -> { with_state(:opened, :reopened) }
scope :closed, -> { with_state(:closed) }
attr_accessible :title, :assignee_id, :position, :description,
@@ -54,12 +56,23 @@ class Issue < ActiveRecord::Base
state :closed
end
- # Both open and reopened issues should be listed as opened
- scope :opened, -> { with_state(:opened, :reopened) }
-
# Mentionable overrides.
def gfm_reference
"issue ##{iid}"
end
+
+ # Reset issue events cache
+ #
+ # Since we do cache @event we need to reset cache in special cases:
+ # * when an issue is updated
+ # Events cache stored like events/23-20130109142513.
+ # The cache key includes updated_at timestamp.
+ # Thus it will automatically generate a new fragment
+ # when the event is updated because the key changes.
+ def reset_events_cache
+ Event.where(target_id: self.id, target_type: 'Issue').
+ order('id DESC').limit(100).
+ update_all(updated_at: Time.now)
+ end
end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index b164ea11073..e59aee8b445 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -35,6 +35,10 @@ class MergeRequest < ActiveRecord::Base
attr_accessor :should_remove_source_branch
+ # When this attribute is true some MR validation is ignored
+ # It allows us to close or modify broken merge requests
+ attr_accessor :allow_broken
+
state_machine :state, initial: :opened do
event :close do
transition [:reopened, :opened] => :closed
@@ -80,7 +84,7 @@ class MergeRequest < ActiveRecord::Base
serialize :st_commits
serialize :st_diffs
- validates :source_project, presence: true
+ validates :source_project, presence: true, unless: :allow_broken
validates :source_branch, presence: true
validates :target_project, presence: true
validates :target_branch, presence: true
@@ -262,7 +266,7 @@ class MergeRequest < ActiveRecord::Base
# Return the set of issues that will be closed if this merge request is accepted.
def closes_issues
if target_branch == project.default_branch
- unmerged_commits.map { |c| c.closes_issues(project) }.flatten.uniq.sort_by(&:id)
+ commits.map { |c| c.closes_issues(project) }.flatten.uniq.sort_by(&:id)
else
[]
end
@@ -273,6 +277,48 @@ class MergeRequest < ActiveRecord::Base
"merge request !#{iid}"
end
+ def target_project_path
+ if target_project
+ target_project.path_with_namespace
+ else
+ "(removed)"
+ end
+ end
+
+ def source_project_path
+ if source_project
+ source_project.path_with_namespace
+ else
+ "(removed)"
+ end
+ end
+
+ def source_branch_exists?
+ return false unless self.source_project
+
+ self.source_project.repository.branch_names.include?(self.source_branch)
+ end
+
+ def target_branch_exists?
+ return false unless self.target_project
+
+ self.target_project.repository.branch_names.include?(self.target_branch)
+ end
+
+ # Reset merge request events cache
+ #
+ # Since we do cache @event we need to reset cache in special cases:
+ # * when a merge request is updated
+ # Events cache stored like events/23-20130109142513.
+ # The cache key includes updated_at timestamp.
+ # Thus it will automatically generate a new fragment
+ # when the event is updated because the key changes.
+ def reset_events_cache
+ Event.where(target_id: self.id, target_type: 'MergeRequest').
+ order('id DESC').limit(100).
+ update_all(updated_at: Time.now)
+ end
+
private
def dump_commits(commits)
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index fde06649c78..8f837c72ff5 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -87,4 +87,8 @@ class Namespace < ActiveRecord::Base
def send_update_instructions
projects.each(&:send_move_instructions)
end
+
+ def kind
+ type == 'Group' ? 'group' : 'user'
+ end
end
diff --git a/app/models/note.rb b/app/models/note.rb
index 7e7387abed6..48b36bcafdf 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -56,29 +56,52 @@ class Note < ActiveRecord::Base
serialize :st_diff
before_create :set_diff, if: ->(n) { n.line_code.present? }
- def self.create_status_change_note(noteable, project, author, status, source)
- body = "_Status changed to #{status}#{' by ' + source.gfm_reference if source}_"
-
- create({
- noteable: noteable,
- project: project,
- author: author,
- note: body,
- system: true
- }, without_protection: true)
- end
-
- # +noteable+ was referenced from +mentioner+, by including GFM in either +mentioner+'s description or an associated Note.
- # Create a system Note associated with +noteable+ with a GFM back-reference to +mentioner+.
- def self.create_cross_reference_note(noteable, mentioner, author, project)
- create({
- noteable: noteable,
- commit_id: (noteable.sha if noteable.respond_to? :sha),
- project: project,
- author: author,
- note: "_mentioned in #{mentioner.gfm_reference}_",
- system: true
- }, without_protection: true)
+ class << self
+ def create_status_change_note(noteable, project, author, status, source)
+ body = "_Status changed to #{status}#{' by ' + source.gfm_reference if source}_"
+
+ create({
+ noteable: noteable,
+ project: project,
+ author: author,
+ note: body,
+ system: true
+ }, without_protection: true)
+ end
+
+ # +noteable+ was referenced from +mentioner+, by including GFM in either +mentioner+'s description or an associated Note.
+ # Create a system Note associated with +noteable+ with a GFM back-reference to +mentioner+.
+ def create_cross_reference_note(noteable, mentioner, author, project)
+ create({
+ noteable: noteable,
+ commit_id: (noteable.sha if noteable.respond_to? :sha),
+ project: project,
+ author: author,
+ note: "_mentioned in #{mentioner.gfm_reference}_",
+ system: true
+ }, without_protection: true)
+ end
+
+ def discussions_from_notes(notes)
+ discussion_ids = []
+ discussions = []
+
+ notes.each do |note|
+ next if discussion_ids.include?(note.discussion_id)
+
+ # don't group notes for the main target
+ if !note.for_diff_line? && note.noteable_type == "MergeRequest"
+ discussions << [note]
+ else
+ discussions << notes.select do |other_note|
+ note.discussion_id == other_note.discussion_id
+ end
+ discussion_ids << note.discussion_id
+ end
+ end
+
+ discussions
+ end
end
# Determine whether or not a cross-reference note already exists.
@@ -89,7 +112,7 @@ class Note < ActiveRecord::Base
def commit_author
@commit_author ||=
project.users.find_by_email(noteable.author_email) ||
- project.users.find_by_name(noteable.author_name)
+ project.users.find_by_name(noteable.author_name)
rescue
nil
end
@@ -157,7 +180,8 @@ class Note < ActiveRecord::Base
# otherwise false is returned
def downvote?
votable? && (note.start_with?('-1') ||
- note.start_with?(':-1:')
+ note.start_with?(':-1:') ||
+ note.start_with?(':thumbsdown:')
)
end
@@ -206,7 +230,8 @@ class Note < ActiveRecord::Base
# otherwise false is returned
def upvote?
votable? && (note.start_with?('+1') ||
- note.start_with?(':+1:')
+ note.start_with?(':+1:') ||
+ note.start_with?(':thumbsup:')
)
end
@@ -237,4 +262,19 @@ class Note < ActiveRecord::Base
def noteable_type=(sType)
super(sType.to_s.classify.constantize.base_class.to_s)
end
+
+ # Reset notes events cache
+ #
+ # Since we do cache @event we need to reset cache in special cases:
+ # * when a note is updated
+ # * when a note is removed
+ # Events cache stored like events/23-20130109142513.
+ # The cache key includes updated_at timestamp.
+ # Thus it will automatically generate a new fragment
+ # when the event is updated because the key changes.
+ def reset_events_cache
+ Event.where(target_id: self.id, target_type: 'Note').
+ order('id DESC').limit(100).
+ update_all(updated_at: Time.now)
+ end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index d2dcf26ac69..a55f7a65b0b 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -9,52 +9,62 @@
# created_at :datetime not null
# updated_at :datetime not null
# creator_id :integer
-# default_branch :string(255)
# issues_enabled :boolean default(TRUE), not null
# wall_enabled :boolean default(TRUE), not null
# merge_requests_enabled :boolean default(TRUE), not null
# wiki_enabled :boolean default(TRUE), not null
# namespace_id :integer
-# public :boolean default(FALSE), not null
# issues_tracker :string(255) default("gitlab"), not null
# issues_tracker_id :string(255)
# snippets_enabled :boolean default(TRUE), not null
# last_activity_at :datetime
# imported :boolean default(FALSE), not null
# import_url :string(255)
+# visibility_level :integer default(0), not null
#
class Project < ActiveRecord::Base
include Gitlab::ShellAdapter
+ include Gitlab::VisibilityLevel
extend Enumerize
- attr_accessible :name, :path, :description, :default_branch, :issues_tracker, :label_list,
+ ActsAsTaggableOn.strict_case_match = true
+
+ attr_accessible :name, :path, :description, :issues_tracker, :label_list,
:issues_enabled, :wall_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id,
- :wiki_enabled, :public, :import_url, :last_activity_at, as: [:default, :admin]
+ :wiki_enabled, :visibility_level, :import_url, :last_activity_at, as: [:default, :admin]
attr_accessible :namespace_id, :creator_id, as: :admin
acts_as_taggable_on :labels, :issues_default_labels
+ attr_accessor :new_default_branch
+
# Relations
belongs_to :creator, foreign_key: "creator_id", class_name: "User"
- belongs_to :group, foreign_key: "namespace_id", conditions: "type = 'Group'"
+ belongs_to :group, -> { where(type: Group) }, foreign_key: "namespace_id"
belongs_to :namespace
- has_one :last_event, class_name: 'Event', order: 'events.created_at DESC', foreign_key: 'project_id'
+ has_one :last_event, -> {order 'events.created_at DESC'}, class_name: 'Event', foreign_key: 'project_id'
has_one :gitlab_ci_service, dependent: :destroy
has_one :campfire_service, dependent: :destroy
+ has_one :emails_on_push_service, dependent: :destroy
has_one :pivotaltracker_service, dependent: :destroy
has_one :hipchat_service, dependent: :destroy
has_one :flowdock_service, dependent: :destroy
+ has_one :assembla_service, dependent: :destroy
has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
has_one :forked_from_project, through: :forked_project_link
+ # Merge Requests for target project should be removed with it
+ has_many :merge_requests, dependent: :destroy, foreign_key: "target_project_id"
+
+ # Merge requests from source project should be kept when source project was removed
+ has_many :fork_merge_requests, foreign_key: "source_project_id", class_name: MergeRequest
+
+ has_many :issues, -> { order "state DESC, created_at DESC" }, dependent: :destroy
has_many :services, dependent: :destroy
has_many :events, dependent: :destroy
- has_many :merge_requests, dependent: :destroy, foreign_key: "target_project_id"
- has_many :fork_merge_requests,dependent: :destroy, foreign_key: "source_project_id", class_name: MergeRequest
- has_many :issues, dependent: :destroy, order: "state DESC, created_at DESC"
has_many :milestones, dependent: :destroy
has_many :notes, dependent: :destroy
has_many :snippets, dependent: :destroy, class_name: "ProjectSnippet"
@@ -71,8 +81,8 @@ class Project < ActiveRecord::Base
delegate :members, to: :team, prefix: true
# Validations
- validates :creator, presence: true
- validates :description, length: { within: 0..2000 }
+ validates :creator, presence: true, on: :create
+ validates :description, length: { maximum: 2000 }, allow_blank: true
validates :name, presence: true, length: { within: 0..255 },
format: { with: Gitlab::Regex.project_name_regex,
message: "only letters, digits, spaces & '_' '-' '.' allowed. Letter or digit should be first" }
@@ -82,7 +92,7 @@ class Project < ActiveRecord::Base
message: "only letters, digits & '_' '-' '.' allowed. Letter or digit should be first" }
validates :issues_enabled, :wall_enabled, :merge_requests_enabled,
:wiki_enabled, inclusion: { in: [true, false] }
- validates :issues_tracker_id, length: { within: 0..255 }
+ validates :issues_tracker_id, length: { maximum: 255 }, allow_blank: true
validates :namespace, presence: true
validates_uniqueness_of :name, scope: :namespace_id
@@ -104,7 +114,10 @@ class Project < ActiveRecord::Base
scope :sorted_by_activity, -> { reorder("projects.last_activity_at DESC") }
scope :personal, ->(user) { where(namespace_id: user.namespace_id) }
scope :joined, ->(user) { where("namespace_id != ?", user.namespace_id) }
- scope :public_only, -> { where(public: true) }
+ scope :public_only, -> { where(visibility_level: PUBLIC) }
+ scope :public_or_internal_only, ->(user) { where("visibility_level IN (:levels)", levels: user ? [ INTERNAL, PUBLIC ] : [ PUBLIC ]) }
+
+ scope :non_archived, -> { where(archived: false) }
enumerize :issues_tracker, in: (Gitlab.config.issues_tracker.keys).append(:gitlab), default: :gitlab
@@ -122,7 +135,7 @@ class Project < ActiveRecord::Base
end
def search query
- where("projects.name LIKE :query OR projects.path LIKE :query", query: "%#{query}%")
+ joins(:namespace).where("projects.archived = ?", false).where("projects.name LIKE :query OR projects.path LIKE :query OR namespaces.name LIKE :query OR projects.description LIKE :query", query: "%#{query}%")
end
def find_with_namespace(id)
@@ -136,6 +149,20 @@ class Project < ActiveRecord::Base
where(path: id, namespace_id: nil).last
end
end
+
+ def visibility_levels
+ Gitlab::VisibilityLevel.options
+ end
+
+ def sort(method)
+ case method.to_s
+ when 'newest' then reorder('projects.created_at DESC')
+ when 'oldest' then reorder('projects.created_at ASC')
+ when 'recently_updated' then reorder('projects.updated_at DESC')
+ when 'last_updated' then reorder('projects.updated_at ASC')
+ else reorder("namespaces.path, projects.name ASC")
+ end
+ end
end
def team
@@ -143,7 +170,7 @@ class Project < ActiveRecord::Base
end
def repository
- @repository ||= Repository.new(path_with_namespace, default_branch)
+ @repository ||= Repository.new(path_with_namespace)
end
def saved?
@@ -221,7 +248,7 @@ class Project < ActiveRecord::Base
end
def available_services_names
- %w(gitlab_ci campfire hipchat pivotaltracker flowdock)
+ %w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push)
end
def gitlab_ci?
@@ -288,8 +315,10 @@ class Project < ActiveRecord::Base
ProjectTransferService.new.transfer(self, new_namespace)
end
- def execute_hooks(data)
- hooks.each { |hook| hook.async_execute(data) }
+ def execute_hooks(data, hooks_scope = :push_hooks)
+ hooks.send(hooks_scope).each do |hook|
+ hook.async_execute(data)
+ end
end
def execute_services(data)
@@ -300,28 +329,20 @@ class Project < ActiveRecord::Base
end
end
- def discover_default_branch
- # Discover the default branch, but only if it hasn't already been set to
- # something else
- if repository.exists? && default_branch.nil?
- update_attributes(default_branch: self.repository.discover_default_branch)
- end
- end
-
def update_merge_requests(oldrev, newrev, ref, user)
return true unless ref =~ /heads/
branch_name = ref.gsub("refs/heads/", "")
c_ids = self.repository.commits_between(oldrev, newrev).map(&:id)
# Update code for merge requests into project between project branches
- mrs = self.merge_requests.opened.by_branch(branch_name).all
+ mrs = self.merge_requests.opened.by_branch(branch_name).to_a
# Update code for merge requests between project and project fork
- mrs += self.fork_merge_requests.opened.by_branch(branch_name).all
-
+ mrs += self.fork_merge_requests.opened.by_branch(branch_name).to_a
+
mrs.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked }
# Close merge requests
- mrs = self.merge_requests.opened.where(target_branch: branch_name).all
+ mrs = self.merge_requests.opened.where(target_branch: branch_name).to_a
mrs = mrs.select(&:last_commit).select { |mr| c_ids.include?(mr.last_commit.id) }
mrs.each { |merge_request| merge_request.merge!(user.id) }
@@ -390,7 +411,7 @@ class Project < ActiveRecord::Base
end
def http_url_to_repo
- http_url = [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('')
+ [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('')
end
# Check if current branch name is marked as protected in the system
@@ -447,4 +468,34 @@ class Project < ActiveRecord::Base
order('id DESC').limit(100).
update_all(updated_at: Time.now)
end
+
+ def project_member(user)
+ users_projects.where(user_id: user).first
+ end
+
+ def default_branch
+ @default_branch ||= repository.root_ref if repository.exists?
+ end
+
+ def reload_default_branch
+ @default_branch = nil
+ default_branch
+ end
+
+ def visibility_level_field
+ visibility_level
+ end
+
+ def archive!
+ update_attribute(:archived, true)
+ end
+
+ def unarchive!
+ update_attribute(:archived, false)
+ end
+
+ def change_head(branch)
+ gitlab_shell.update_repository_head(self.path_with_namespace, branch)
+ reload_default_branch
+ end
end
diff --git a/app/models/project_hook.rb b/app/models/project_hook.rb
index 2576fc979d4..e1c9ed01bc5 100644
--- a/app/models/project_hook.rb
+++ b/app/models/project_hook.rb
@@ -2,15 +2,24 @@
#
# Table name: web_hooks
#
-# id :integer not null, primary key
-# url :string(255)
-# project_id :integer
-# created_at :datetime not null
-# updated_at :datetime not null
-# type :string(255) default("ProjectHook")
-# service_id :integer
+# id :integer not null, primary key
+# url :string(255)
+# project_id :integer
+# created_at :datetime not null
+# updated_at :datetime not null
+# type :string(255) default("ProjectHook")
+# service_id :integer
+# push_events :boolean default(TRUE), not null
+# issues_events :boolean default(FALSE), not null
+# merge_requests_events :boolean default(FALSE), not null
#
class ProjectHook < WebHook
belongs_to :project
+
+ attr_accessible :push_events, :issues_events, :merge_requests_events
+
+ scope :push_hooks, -> { where(push_events: true) }
+ scope :issue_hooks, -> { where(issues_events: true) }
+ scope :merge_request_hooks, -> { where(merge_requests_events: true) }
end
diff --git a/app/models/project_services/assembla_service.rb b/app/models/project_services/assembla_service.rb
new file mode 100644
index 00000000000..66ecf394784
--- /dev/null
+++ b/app/models/project_services/assembla_service.rb
@@ -0,0 +1,45 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# token :string(255)
+# project_id :integer not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# active :boolean default(FALSE), not null
+# project_url :string(255)
+# subdomain :string(255)
+# room :string(255)
+#
+
+class AssemblaService < Service
+ include HTTParty
+
+ validates :token, presence: true, if: :activated?
+
+ def title
+ 'Assembla'
+ end
+
+ def description
+ 'Project Management Software (Source Commits Endpoint)'
+ end
+
+ def to_param
+ 'assembla'
+ end
+
+ def fields
+ [
+ { type: 'text', name: 'token', placeholder: '' }
+ ]
+ end
+
+ def execute(push)
+ url = "https://atlas.assembla.com/spaces/ouposp/github_tool?secret_key=#{token}"
+ AssemblaService.post(url, body: { payload: push }.to_json, headers: { 'Content-Type' => 'application/json' })
+ end
+end
diff --git a/app/models/campfire_service.rb b/app/models/project_services/campfire_service.rb
index fb2a49fd586..fb2a49fd586 100644
--- a/app/models/campfire_service.rb
+++ b/app/models/project_services/campfire_service.rb
diff --git a/app/models/project_services/emails_on_push_service.rb b/app/models/project_services/emails_on_push_service.rb
new file mode 100644
index 00000000000..2a46eff7846
--- /dev/null
+++ b/app/models/project_services/emails_on_push_service.rb
@@ -0,0 +1,44 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# token :string(255)
+# project_id :integer not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# active :boolean default(FALSE), not null
+# project_url :string(255)
+# subdomain :string(255)
+# room :string(255)
+#
+
+class EmailsOnPushService < Service
+ attr_accessible :recipients
+
+ validates :recipients, presence: true, if: :activated?
+
+ def title
+ 'Emails on push'
+ end
+
+ def description
+ 'Email the commits and diff of each push to a list of recipients.'
+ end
+
+ def to_param
+ 'emails_on_push'
+ end
+
+ def execute(push_data)
+ EmailsOnPushWorker.perform_async(project_id, recipients, push_data)
+ end
+
+ def fields
+ [
+ { type: 'textarea', name: 'recipients', placeholder: 'Emails separated by whitespace' },
+ ]
+ end
+end
diff --git a/app/models/flowdock_service.rb b/app/models/project_services/flowdock_service.rb
index 6ec431d4a10..f72d9fa9015 100644
--- a/app/models/flowdock_service.rb
+++ b/app/models/project_services/flowdock_service.rb
@@ -11,6 +11,8 @@
# updated_at :datetime not null
# active :boolean default(FALSE), not null
# project_url :string(255)
+# subdomain :string(255)
+# room :string(255)
#
require "flowdock-git-hook"
diff --git a/app/models/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb
index 7f5380a4551..7f5380a4551 100644
--- a/app/models/gitlab_ci_service.rb
+++ b/app/models/project_services/gitlab_ci_service.rb
diff --git a/app/models/hipchat_service.rb b/app/models/project_services/hipchat_service.rb
index 7fec5c4fbe8..ea2169fb168 100644
--- a/app/models/hipchat_service.rb
+++ b/app/models/project_services/hipchat_service.rb
@@ -25,7 +25,7 @@ class HipchatService < Service
end
def description
- 'Simple web-based real-time group chat'
+ 'Private group chat and IM'
end
def to_param
diff --git a/app/models/pivotaltracker_service.rb b/app/models/project_services/pivotaltracker_service.rb
index c5b1b9ab8d3..c5b1b9ab8d3 100644
--- a/app/models/pivotaltracker_service.rb
+++ b/app/models/project_services/pivotaltracker_service.rb
diff --git a/app/models/project_team.rb b/app/models/project_team.rb
index bc35c4041ba..5630f280aea 100644
--- a/app/models/project_team.rb
+++ b/app/models/project_team.rb
@@ -64,6 +64,10 @@ class ProjectTeam
UsersProject.truncate_team(project)
end
+ def users
+ members
+ end
+
def members
@members ||= fetch_members
end
@@ -87,9 +91,8 @@ class ProjectTeam
def import(source_project)
target_project = project
- source_team = source_project.users_projects.all
- target_team = target_project.users_projects.all
- target_user_ids = target_team.map(&:user_id)
+ source_team = source_project.users_projects.to_a
+ target_user_ids = target_project.users_projects.pluck(:user_id)
source_team.reject! do |tm|
# Skip if user already present in team
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 97b4330092a..1255b814533 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -3,7 +3,7 @@ class Repository
attr_accessor :raw_repository, :path_with_namespace
- def initialize(path_with_namespace, default_branch)
+ def initialize(path_with_namespace, default_branch = nil)
@path_with_namespace = path_with_namespace
@raw_repository = Gitlab::Git::Repository.new(path_to_repo) if path_with_namespace
rescue Gitlab::Git::Repository::NoRepository
@@ -57,7 +57,7 @@ class Repository
def recent_branches(limit = 20)
branches.sort do |a, b|
- a.commit.committed_date <=> b.commit.committed_date
+ b.commit.committed_date <=> a.commit.committed_date
end[0..limit]
end
@@ -133,6 +133,7 @@ class Repository
Rails.cache.delete(cache_key(:tag_names))
Rails.cache.delete(cache_key(:commit_count))
Rails.cache.delete(cache_key(:graph_log))
+ Rails.cache.delete(cache_key(:readme))
end
def graph_log
@@ -159,4 +160,10 @@ class Repository
def blob_at(sha, path)
Gitlab::Git::Blob.find(self, sha, path)
end
+
+ def readme
+ Rails.cache.fetch(cache_key(:readme)) do
+ Tree.new(self, self.root_ref).readme
+ end
+ end
end
diff --git a/app/models/service_hook.rb b/app/models/service_hook.rb
index 4cd2b272eec..6f22a863d98 100644
--- a/app/models/service_hook.rb
+++ b/app/models/service_hook.rb
@@ -2,13 +2,16 @@
#
# Table name: web_hooks
#
-# id :integer not null, primary key
-# url :string(255)
-# project_id :integer
-# created_at :datetime not null
-# updated_at :datetime not null
-# type :string(255) default("ProjectHook")
-# service_id :integer
+# id :integer not null, primary key
+# url :string(255)
+# project_id :integer
+# created_at :datetime not null
+# updated_at :datetime not null
+# type :string(255) default("ProjectHook")
+# service_id :integer
+# push_events :boolean default(TRUE), not null
+# issues_events :boolean default(FALSE), not null
+# merge_requests_events :boolean default(FALSE), not null
#
class ServiceHook < WebHook
diff --git a/app/models/system_hook.rb b/app/models/system_hook.rb
index 5cdf046644f..bffcbbf00f4 100644
--- a/app/models/system_hook.rb
+++ b/app/models/system_hook.rb
@@ -2,13 +2,16 @@
#
# Table name: web_hooks
#
-# id :integer not null, primary key
-# url :string(255)
-# project_id :integer
-# created_at :datetime not null
-# updated_at :datetime not null
-# type :string(255) default("ProjectHook")
-# service_id :integer
+# id :integer not null, primary key
+# url :string(255)
+# project_id :integer
+# created_at :datetime not null
+# updated_at :datetime not null
+# type :string(255) default("ProjectHook")
+# service_id :integer
+# push_events :boolean default(TRUE), not null
+# issues_events :boolean default(FALSE), not null
+# merge_requests_events :boolean default(FALSE), not null
#
class SystemHook < WebHook
diff --git a/app/models/user.rb b/app/models/user.rb
index df14cf34e85..f2cd554f9c3 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -36,6 +36,12 @@
# notification_level :integer default(1), not null
# password_expires_at :datetime
# created_by_id :integer
+# avatar :string(255)
+# confirmation_token :string(255)
+# confirmed_at :datetime
+# confirmation_sent_at :datetime
+# unconfirmed_email :string(255)
+# hide_no_ssh_key :boolean default(FALSE), not null
#
require 'carrierwave/orm/activerecord'
@@ -47,7 +53,7 @@ class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, :name, :username,
:skype, :linkedin, :twitter, :color_scheme_id, :theme_id, :force_random_password,
- :extern_uid, :provider, :password_expires_at, :avatar,
+ :extern_uid, :provider, :password_expires_at, :avatar, :hide_no_ssh_key,
as: [:default, :admin]
attr_accessible :projects_limit, :can_create_group,
@@ -67,44 +73,38 @@ class User < ActiveRecord::Base
#
# Namespace for personal projects
- has_one :namespace, dependent: :destroy, foreign_key: :owner_id, class_name: "Namespace", conditions: 'type IS NULL'
-
- # Namespaces (owned groups and own namespace)
- has_many :namespaces, foreign_key: :owner_id
+ has_one :namespace, -> { where type: nil }, dependent: :destroy, foreign_key: :owner_id, class_name: "Namespace"
# Profile
has_many :keys, dependent: :destroy
# Groups
- has_many :own_groups, class_name: "Group", foreign_key: :owner_id
- has_many :owned_groups, through: :users_groups, source: :group, conditions: { users_groups: { group_access: UsersGroup::OWNER } }
-
has_many :users_groups, dependent: :destroy
has_many :groups, through: :users_groups
-
+ has_many :owned_groups, -> { where users_groups: { group_access: UsersGroup::OWNER } }, through: :users_groups, source: :group
# Projects
+ has_many :groups_projects, through: :groups, source: :projects
+ has_many :personal_projects, through: :namespace, source: :projects
+ has_many :projects, through: :users_projects
+ has_many :created_projects, foreign_key: :creator_id, class_name: 'Project'
+
has_many :snippets, dependent: :destroy, foreign_key: :author_id, class_name: "Snippet"
has_many :users_projects, dependent: :destroy
has_many :issues, dependent: :destroy, foreign_key: :author_id
has_many :notes, dependent: :destroy, foreign_key: :author_id
has_many :merge_requests, dependent: :destroy, foreign_key: :author_id
has_many :events, dependent: :destroy, foreign_key: :author_id, class_name: "Event"
- has_many :recent_events, foreign_key: :author_id, class_name: "Event", order: "id DESC"
+ has_many :recent_events, -> { order "id DESC" }, foreign_key: :author_id, class_name: "Event"
has_many :assigned_issues, dependent: :destroy, foreign_key: :assignee_id, class_name: "Issue"
has_many :assigned_merge_requests, dependent: :destroy, foreign_key: :assignee_id, class_name: "MergeRequest"
- has_many :groups_projects, through: :groups, source: :projects
- has_many :personal_projects, through: :namespace, source: :projects
- has_many :projects, through: :users_projects
- has_many :own_projects, foreign_key: :creator_id, class_name: 'Project'
- has_many :owned_projects, through: :namespaces, source: :projects
#
# Validations
#
validates :name, presence: true
- validates :email, presence: true, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/ }
- validates :bio, length: { within: 0..255 }
+ validates :email, presence: true, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/ }, uniqueness: true
+ validates :bio, length: { maximum: 255 }, allow_blank: true
validates :extern_uid, allow_blank: true, uniqueness: {scope: :provider}
validates :projects_limit, presence: true, numericality: {greater_than_or_equal_to: 0}
validates :username, presence: true, uniqueness: true,
@@ -113,9 +113,8 @@ class User < ActiveRecord::Base
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
validates :notification_level, inclusion: { in: Notification.notification_levels }, presence: true
-
validate :namespace_uniq, if: ->(user) { user.username_changed? }
-
+ validate :avatar_type, if: ->(user) { user.avatar_changed? }
validates :avatar, file_size: { maximum: 100.kilobytes.to_i }
before_validation :generate_password, on: :create
@@ -164,7 +163,7 @@ class User < ActiveRecord::Base
scope :alphabetically, -> { order('name ASC') }
scope :in_team, ->(team){ where(id: team.member_ids) }
scope :not_in_team, ->(team){ where('users.id NOT IN (:ids)', ids: team.member_ids) }
- scope :not_in_project, ->(project) { project.users.present? ? where("id not in (:ids)", ids: project.users.map(&:id) ) : scoped }
+ scope :not_in_project, ->(project) { project.users.present? ? where("id not in (:ids)", ids: project.users.map(&:id) ) : all }
scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM users_projects)') }
scope :ldap, -> { where(provider: 'ldap') }
@@ -199,7 +198,7 @@ class User < ActiveRecord::Base
end
def by_username_or_id(name_or_id)
- where('username = ? OR id = ?', name_or_id, name_or_id).first
+ where('users.username = ? OR users.id = ?', name_or_id.to_s, name_or_id.to_i).first
end
def build_user(attrs = {}, options= {})
@@ -244,10 +243,16 @@ class User < ActiveRecord::Base
end
end
+ def avatar_type
+ unless self.avatar.image?
+ self.errors.add :avatar, "only images allowed"
+ end
+ end
+
# Groups user has access to
def authorized_groups
@authorized_groups ||= begin
- group_ids = (groups.pluck(:id) + own_groups.pluck(:id) + authorized_projects.pluck(:namespace_id))
+ group_ids = (groups.pluck(:id) + authorized_projects.pluck(:namespace_id))
Group.where(id: group_ids).order('namespaces.name ASC')
end
end
@@ -256,11 +261,17 @@ class User < ActiveRecord::Base
# Projects user has access to
def authorized_projects
@authorized_projects ||= begin
- project_ids = (owned_projects.pluck(:id) + groups_projects.pluck(:id) + projects.pluck(:id)).uniq
+ project_ids = (personal_projects.pluck(:id) + groups_projects.pluck(:id) + projects.pluck(:id)).uniq
Project.where(id: project_ids).joins(:namespace).order('namespaces.name ASC')
end
end
+ def owned_projects
+ @owned_projects ||= begin
+ Project.where(namespace_id: owned_groups.pluck(:id).push(namespace.id)).joins(:namespace)
+ end
+ end
+
# Team membership in authorized projects
def tm_in_authorized_projects
UsersProject.where(project_id: authorized_projects.map(&:id), user_id: self.id)
@@ -333,7 +344,7 @@ class User < ActiveRecord::Base
end
def several_namespaces?
- namespaces.many? || owned_groups.any?
+ owned_groups.any?
end
def namespace_id
@@ -367,7 +378,7 @@ class User < ActiveRecord::Base
end
def accessible_deploy_keys
- DeployKey.in_projects(self.authorized_projects).uniq
+ DeployKey.in_projects(self.authorized_projects.pluck(:id)).uniq
end
def created_by
@@ -394,4 +405,23 @@ class User < ActiveRecord::Base
self
end
+
+ def can_leave_project?(project)
+ project.namespace != namespace &&
+ project.project_member(self)
+ end
+
+ # Reset project events cache related to this user
+ #
+ # Since we do cache @event we need to reset cache in special cases:
+ # * when the user changes their avatar
+ # Events cache stored like events/23-20130109142513.
+ # The cache key includes updated_at timestamp.
+ # Thus it will automatically generate a new fragment
+ # when the event is updated because the key changes.
+ def reset_events_cache
+ Event.where(author_id: self.id).
+ order('id DESC').limit(1000).
+ update_all(updated_at: Time.now)
+ end
end
diff --git a/app/models/web_hook.rb b/app/models/web_hook.rb
index 3f22b1082fb..c0aa3734917 100644
--- a/app/models/web_hook.rb
+++ b/app/models/web_hook.rb
@@ -2,13 +2,16 @@
#
# Table name: web_hooks
#
-# id :integer not null, primary key
-# url :string(255)
-# project_id :integer
-# created_at :datetime not null
-# updated_at :datetime not null
-# type :string(255) default("ProjectHook")
-# service_id :integer
+# id :integer not null, primary key
+# url :string(255)
+# project_id :integer
+# created_at :datetime not null
+# updated_at :datetime not null
+# type :string(255) default("ProjectHook")
+# service_id :integer
+# push_events :boolean default(TRUE), not null
+# issues_events :boolean default(FALSE), not null
+# merge_requests_events :boolean default(FALSE), not null
#
class WebHook < ActiveRecord::Base
diff --git a/app/observers/issue_observer.rb b/app/observers/issue_observer.rb
index 886d8b776fb..1575cf0f19f 100644
--- a/app/observers/issue_observer.rb
+++ b/app/observers/issue_observer.rb
@@ -1,18 +1,19 @@
class IssueObserver < BaseObserver
def after_create(issue)
notification.new_issue(issue, current_user)
-
issue.create_cross_references!(issue.project, current_user)
+ execute_hooks(issue)
end
def after_close(issue, transition)
notification.close_issue(issue, current_user)
-
create_note(issue)
+ execute_hooks(issue)
end
def after_reopen(issue, transition)
create_note(issue)
+ execute_hooks(issue)
end
def after_update(issue)
@@ -21,6 +22,7 @@ class IssueObserver < BaseObserver
end
issue.notice_added_references(issue.project, current_user)
+ execute_hooks(issue)
end
protected
@@ -29,4 +31,8 @@ class IssueObserver < BaseObserver
def create_note(issue)
Note.create_status_change_note(issue, issue.project, current_user, issue.state, current_commit)
end
+
+ def execute_hooks(issue)
+ issue.project.execute_hooks(issue.to_hook_data, :issue_hooks)
+ end
end
diff --git a/app/observers/merge_request_observer.rb b/app/observers/merge_request_observer.rb
index d70da514cd2..0ac555fce7c 100644
--- a/app/observers/merge_request_observer.rb
+++ b/app/observers/merge_request_observer.rb
@@ -7,15 +7,15 @@ class MergeRequestObserver < ActivityObserver
end
notification.new_merge_request(merge_request, current_user)
-
merge_request.create_cross_references!(merge_request.project, current_user)
+ execute_hooks(merge_request)
end
def after_close(merge_request, transition)
create_event(merge_request, Event::CLOSED)
- Note.create_status_change_note(merge_request, merge_request.target_project, current_user, merge_request.state, nil)
-
notification.close_mr(merge_request, current_user)
+ create_note(merge_request)
+ execute_hooks(merge_request)
end
def after_merge(merge_request, transition)
@@ -31,17 +31,21 @@ class MergeRequestObserver < ActivityObserver
action: Event::MERGED,
author_id: merge_request.author_id_of_changes
)
+
+ execute_hooks(merge_request)
end
def after_reopen(merge_request, transition)
create_event(merge_request, Event::REOPENED)
- Note.create_status_change_note(merge_request, merge_request.target_project, current_user, merge_request.state, nil)
+ create_note(merge_request)
+ execute_hooks(merge_request)
end
def after_update(merge_request)
notification.reassigned_merge_request(merge_request, current_user) if merge_request.is_being_reassigned?
merge_request.notice_added_references(merge_request.project, current_user)
+ execute_hooks(merge_request)
end
def create_event(record, status)
@@ -53,4 +57,17 @@ class MergeRequestObserver < ActivityObserver
author_id: current_user.id
)
end
+
+ private
+
+ # Create merge request note with service comment like 'Status changed to closed'
+ def create_note(merge_request)
+ Note.create_status_change_note(merge_request, merge_request.target_project, current_user, merge_request.state, nil)
+ end
+
+ def execute_hooks(merge_request)
+ if merge_request.project
+ merge_request.project.execute_hooks(merge_request.to_hook_data, :merge_request_hooks)
+ end
+ end
end
diff --git a/app/observers/project_observer.rb b/app/observers/project_observer.rb
index f301f306458..4e3deec01bf 100644
--- a/app/observers/project_observer.rb
+++ b/app/observers/project_observer.rb
@@ -30,12 +30,6 @@ class ProjectObserver < BaseObserver
def after_update(project)
project.send_move_instructions if project.namespace_id_changed?
project.rename_repo if project.path_changed?
-
- GitlabShellWorker.perform_async(
- :update_repository_head,
- project.path_with_namespace,
- project.default_branch
- ) if project.default_branch_changed?
end
def before_destroy(project)
diff --git a/app/observers/users_group_observer.rb b/app/observers/users_group_observer.rb
index ecdbede89d9..42a05b5e177 100644
--- a/app/observers/users_group_observer.rb
+++ b/app/observers/users_group_observer.rb
@@ -4,6 +4,6 @@ class UsersGroupObserver < BaseObserver
end
def after_update(membership)
- notification.update_group_member(membership)
+ notification.update_group_member(membership) if membership.group_access_changed?
end
end
diff --git a/app/observers/users_project_observer.rb b/app/observers/users_project_observer.rb
index ca9649c76ab..93233898cc8 100644
--- a/app/observers/users_project_observer.rb
+++ b/app/observers/users_project_observer.rb
@@ -1,8 +1,4 @@
class UsersProjectObserver < BaseObserver
- def after_commit(users_project)
- return if users_project.destroyed?
- end
-
def after_create(users_project)
Event.create(
project_id: users_project.project.id,
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
index 3ad41de397f..e54f88e42de 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -24,7 +24,6 @@ class GitPushService
create_push_event
project.ensure_satellite_exists
- project.discover_default_branch
project.repository.expire_cache
if push_to_existing_branch?(ref, oldrev)
@@ -33,7 +32,7 @@ class GitPushService
end
if push_to_branch?(ref)
- project.execute_hooks(@push_data.dup)
+ project.execute_hooks(@push_data.dup, :push_hooks)
project.execute_services(@push_data.dup)
end
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index 750a71aea6b..eb42cac3f83 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -19,7 +19,7 @@ class NotificationService
# When create an issue we should send next emails:
#
- # * issue assignee if his notification level is not Disabled
+ # * issue assignee if their notification level is not Disabled
# * project team members with notification level higher then Participating
#
def new_issue(issue, current_user)
@@ -28,8 +28,8 @@ class NotificationService
# When we close an issue we should send next emails:
#
- # * issue author if his notification level is not Disabled
- # * issue assignee if his notification level is not Disabled
+ # * issue author if their notification level is not Disabled
+ # * issue assignee if their notification level is not Disabled
# * project team members with notification level higher then Participating
#
def close_issue(issue, current_user)
@@ -38,8 +38,8 @@ class NotificationService
# When we reassign an issue we should send next emails:
#
- # * issue old assignee if his notification level is not Disabled
- # * issue new assignee if his notification level is not Disabled
+ # * issue old assignee if their notification level is not Disabled
+ # * issue new assignee if their notification level is not Disabled
#
def reassigned_issue(issue, current_user)
reassign_resource_email(issue, issue.project, current_user, 'reassigned_issue_email')
@@ -48,7 +48,7 @@ class NotificationService
# When create a merge request we should send next emails:
#
- # * mr assignee if his notification level is not Disabled
+ # * mr assignee if their notification level is not Disabled
#
def new_merge_request(merge_request, current_user)
new_resource_email(merge_request, merge_request.target_project, 'new_merge_request_email')
@@ -56,8 +56,8 @@ class NotificationService
# When we reassign a merge_request we should send next emails:
#
- # * merge_request old assignee if his notification level is not Disabled
- # * merge_request assignee if his notification level is not Disabled
+ # * merge_request old assignee if their notification level is not Disabled
+ # * merge_request assignee if their notification level is not Disabled
#
def reassigned_merge_request(merge_request, current_user)
reassign_resource_email(merge_request, merge_request.target_project, current_user, 'reassigned_merge_request_email')
@@ -65,8 +65,8 @@ class NotificationService
# When we close a merge request we should send next emails:
#
- # * merge_request author if his notification level is not Disabled
- # * merge_request assignee if his notification level is not Disabled
+ # * merge_request author if their notification level is not Disabled
+ # * merge_request assignee if their notification level is not Disabled
# * project team members with notification level higher then Participating
#
def close_mr(merge_request, current_user)
@@ -75,8 +75,8 @@ class NotificationService
# When we merge a merge request we should send next emails:
#
- # * merge_request author if his notification level is not Disabled
- # * merge_request assignee if his notification level is not Disabled
+ # * merge_request author if their notification level is not Disabled
+ # * merge_request assignee if their notification level is not Disabled
# * project team members with notification level higher then Participating
#
def merge_mr(merge_request)
diff --git a/app/services/project_transfer_service.rb b/app/services/project_transfer_service.rb
index 7150c1c78c0..7055ef32aee 100644
--- a/app/services/project_transfer_service.rb
+++ b/app/services/project_transfer_service.rb
@@ -18,6 +18,10 @@ class ProjectTransferService
raise TransferError.new("Project with same path in target namespace already exists")
end
+ # Remove old satellite
+ project.satellite.destroy
+
+ # Apply new namespace id
project.namespace = new_namespace
project.save!
@@ -29,8 +33,8 @@ class ProjectTransferService
# Move wiki repo also if present
gitlab_shell.mv_repository("#{old_path}.wiki", "#{new_path}.wiki")
- # create satellite repo
- project.ensure_satellite_exists
+ # Create a new satellite (reload project from DB)
+ Project.find(project.id).ensure_satellite_exists
# clear project cached events
project.reset_events_cache
diff --git a/app/uploaders/attachment_uploader.rb b/app/uploaders/attachment_uploader.rb
index 98794c9470b..b122b6c8658 100644
--- a/app/uploaders/attachment_uploader.rb
+++ b/app/uploaders/attachment_uploader.rb
@@ -3,6 +3,8 @@
class AttachmentUploader < CarrierWave::Uploader::Base
storage :file
+ after :store, :reset_events_cache
+
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
@@ -27,4 +29,8 @@ class AttachmentUploader < CarrierWave::Uploader::Base
def file_storage?
self.class.storage == CarrierWave::Storage::File
end
+
+ def reset_events_cache(file)
+ model.reset_events_cache if model.is_a?(User)
+ end
end
diff --git a/app/views/admin/background_jobs/show.html.haml b/app/views/admin/background_jobs/show.html.haml
index 431a9cbcd36..e5af56ffc5c 100644
--- a/app/views/admin/background_jobs/show.html.haml
+++ b/app/views/admin/background_jobs/show.html.haml
@@ -1,11 +1,11 @@
%h3.page-title Background Jobs
-%p.light GitLab use #{link_to "sidekiq", "http://sidekiq.org/"} library for async job processing
+%p.light GitLab uses #{link_to "sidekiq", "http://sidekiq.org/"} library for async job processing
%hr
.ui-box
.title Sidekiq running processes
- .ui-box-body
+ .body
- if @sidekiq_processes.empty?
%h4.cred
%i.icon-warning-sign
diff --git a/app/views/admin/broadcast_messages/index.html.haml b/app/views/admin/broadcast_messages/index.html.haml
new file mode 100644
index 00000000000..f28dadfb659
--- /dev/null
+++ b/app/views/admin/broadcast_messages/index.html.haml
@@ -0,0 +1,61 @@
+%h3.page-title
+ Broadcast Messages
+%p.light
+ Broadcast messages are displayed for every user and can be used to notify users about scheduled maintenance, recent upgrades and more.
+.broadcast-message-preview
+ %i.icon-bullhorn
+ %span Your message here
+
+= form_for [:admin, @broadcast_message], html: { class: 'broadcast-message-form form-horizontal'} do |f|
+ -if @broadcast_message.errors.any?
+ .alert.alert-danger
+ - @broadcast_message.errors.full_messages.each do |msg|
+ %p= msg
+ .form-group
+ = f.label :message, class: 'control-label'
+ .col-sm-10
+ = f.text_area :message, class: "form-control", rows: 2, required: true
+ %div
+ = link_to '#', class: 'js-toggle-colors-link' do
+ Customize colors
+ .form-group.js-toggle-colors-container.hide
+ = f.label :color, "Background Color", class: 'control-label'
+ .col-sm-10
+ = f.text_field :color, placeholder: "#AA33EE", class: "form-control"
+ .light Hex values as 3 double digit numbers, starting with a # sign.
+ .form-group.js-toggle-colors-container.hide
+ = f.label :font, "Font Color", class: 'control-label'
+ .col-sm-10
+ = f.text_field :font, placeholder: "#224466", class: "form-control"
+ .light Hex values as 3 double digit numbers, starting with a # sign.
+ .form-group
+ = f.label :starts_at, class: 'control-label'
+ .col-sm-10.datetime-controls
+ = f.datetime_select :starts_at
+ .form-group
+ = f.label :ends_at, class: 'control-label'
+ .col-sm-10.datetime-controls
+ = f.datetime_select :ends_at
+ .form-actions
+ = f.submit "Add broadcast message", class: "btn btn-create"
+
+-if @broadcast_messages.any?
+ %ul.bordered-list.broadcast-messages
+ - @broadcast_messages.each do |broadcast_message|
+ %li
+ .pull-right
+ - if broadcast_message.starts_at
+ %strong
+ #{broadcast_message.starts_at.to_s(:short)}
+ \...
+ - if broadcast_message.ends_at
+ %strong
+ #{broadcast_message.ends_at.to_s(:short)}
+ &nbsp;
+ = link_to [:admin, broadcast_message], method: :delete, remote: true, class: 'remove-row btn btn-tiny' do
+ %i.icon-remove.cred
+
+ .message= broadcast_message.message
+
+
+ = paginate @broadcast_messages
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index 3064763b993..dd663945ea9 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -4,7 +4,7 @@
You can manage projects, users and other GitLab data from here.
%hr
.admin_dash.row
- .span4
+ .col-sm-4
.light-well
%h4 Projects
.data
@@ -12,7 +12,7 @@
%h1= Project.count
%hr
= link_to 'New Project', new_project_path, class: "btn btn-new"
- .span4
+ .col-sm-4
.light-well
%h4 Users
.data
@@ -20,7 +20,7 @@
%h1= User.count
%hr
= link_to 'New User', new_admin_user_path, class: "btn btn-new"
- .span4
+ .col-sm-4
.light-well
%h4 Groups
.data
@@ -30,17 +30,16 @@
= link_to 'New Group', new_admin_group_path, class: "btn btn-new"
.row.prepend-top-10
- .span4
+ .col-md-4
%h4 Latest projects
%hr
- @projects.each do |project|
%p
= link_to project.name_with_namespace, [:admin, project]
%span.light.pull-right
- = time_ago_in_words project.created_at
- ago
+ #{time_ago_with_tooltip(project.created_at)}
- .span4
+ .col-md-4
%h4 Latest users
%hr
- @users.each do |user|
@@ -48,10 +47,21 @@
= link_to [:admin, user] do
= user.name
%span.light.pull-right
- = time_ago_in_words user.created_at
- ago
+ #{time_ago_with_tooltip(user.created_at)}
- .span4
+ .col-md-4
+ %h4 Latest groups
+ %hr
+ - @groups.each do |group|
+ %p
+ = link_to [:admin, group] do
+ = group.name
+ %span.light.pull-right
+ #{time_ago_with_tooltip(group.created_at)}
+
+%br
+.row
+ .col-md-4
%h4 Stats
%hr
%p
@@ -82,3 +92,47 @@
Milestones
%span.light.pull-right
= Milestone.count
+ .col-md-4
+ %h4
+ Features
+ %hr
+ %p
+ Sign up
+ %span.light.pull-right
+ = boolean_to_icon gitlab_config.signup_enabled
+ %p
+ LDAP
+ %span.light.pull-right
+ = boolean_to_icon Gitlab.config.ldap.enabled
+ %p
+ Gravatar
+ %span.light.pull-right
+ = boolean_to_icon Gitlab.config.gravatar.enabled
+ %p
+ OmniAuth
+ %span.light.pull-right
+ = boolean_to_icon Gitlab.config.omniauth.enabled
+ .col-md-4
+ %h4 Components
+ %hr
+ %p
+ GitLab
+ %span.pull-right
+ = Gitlab::VERSION
+ %p
+ GitLab Shell
+ %span.pull-right
+ = Gitlab::Shell.new.version
+ %p
+ GitLab API
+ %span.pull-right
+ = API::API::version
+ %p
+ Ruby
+ %span.pull-right
+ #{RUBY_VERSION}p#{RUBY_PATCHLEVEL}
+
+ %p
+ Rails
+ %span.pull-right
+ #{Rails::VERSION::STRING}
diff --git a/app/views/admin/groups/edit.html.haml b/app/views/admin/groups/edit.html.haml
index 3918bf981a2..e9807d7473a 100644
--- a/app/views/admin/groups/edit.html.haml
+++ b/app/views/admin/groups/edit.html.haml
@@ -2,24 +2,24 @@
%hr
= form_for [:admin, @group] do |f|
- if @group.errors.any?
- .alert.alert-error
+ .alert.alert-danger
%span= @group.errors.full_messages.first
- .control-group.group_name_holder
+ .form-group.group_name_holder
= f.label :name do
Group name
- .controls
- = f.text_field :name, placeholder: "Example Group", class: "input-xxlarge"
+ .col-sm-10
+ = f.text_field :name, placeholder: "Example Group", class: "form-control"
- .control-group.group-description-holder
+ .form-group.group-description-holder
= f.label :description, "Details"
- .controls
- = f.text_area :description, maxlength: 250, class: "input-xxlarge js-gfm-input", rows: 4
+ .col-sm-10
+ = f.text_area :description, maxlength: 250, class: "form-control js-gfm-input", rows: 4
- .control-group.group_name_holder
+ .form-group.group_name_holder
= f.label :path do
%span.cred Group path
- .controls
- = f.text_field :path, placeholder: "example-group", class: "input-xxlarge danger"
+ .col-sm-10
+ = f.text_field :path, placeholder: "example-group", class: "form-control danger"
%ul.cred
%li Changing group path can have unintended side effects.
%li Renaming group path will rename directory for all related projects
diff --git a/app/views/admin/groups/index.html.haml b/app/views/admin/groups/index.html.haml
index c9d7c24f204..71a0004179c 100644
--- a/app/views/admin/groups/index.html.haml
+++ b/app/views/admin/groups/index.html.haml
@@ -7,7 +7,8 @@
= link_to 'New Group', new_admin_group_path, class: "btn btn-new pull-right"
%br
= form_tag admin_groups_path, method: :get, class: 'form-inline' do
- = text_field_tag :name, params[:name], class: "span6"
+ .form-group
+ = text_field_tag :name, params[:name], class: "form-control input-xpadding"
= submit_tag "Search", class: "btn submit btn-primary"
%hr
@@ -18,7 +19,7 @@
.clearfix
.pull-right.prepend-top-10
= link_to 'Edit', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: "btn btn-small"
- = link_to 'Destroy', [:admin, group], confirm: "REMOVE #{group.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove"
+ = link_to 'Destroy', [:admin, group], data: {confirm: "REMOVE #{group.name}? Are you sure?"}, method: :delete, class: "btn btn-small btn-remove"
%h4
= link_to [:admin, group] do
diff --git a/app/views/admin/groups/new.html.haml b/app/views/admin/groups/new.html.haml
index cfbe6b5ee5a..2689b47fbc5 100644
--- a/app/views/admin/groups/new.html.haml
+++ b/app/views/admin/groups/new.html.haml
@@ -2,17 +2,17 @@
%hr
= form_for [:admin, @group] do |f|
- if @group.errors.any?
- .alert.alert-error
+ .alert.alert-danger
%span= @group.errors.full_messages.first
- .control-group
+ .form-group
= f.label :name do
Group name
- .controls
- = f.text_field :name, placeholder: "Ex. OpenSource", class: "input-xxlarge left"
- .control-group.group-description-holder
+ .col-sm-10
+ = f.text_field :name, placeholder: "Ex. OpenSource", class: "form-control left"
+ .form-group.group-description-holder
= f.label :description, "Details"
- .controls
- = f.text_area :description, maxlength: 250, class: "input-xxlarge js-gfm-input", rows: 4
+ .col-sm-10
+ = f.text_area :description, maxlength: 250, class: "form-control js-gfm-input", rows: 4
.form-actions
= f.submit 'Create group', class: "btn btn-create"
diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml
index ca51a57000a..235588fe367 100644
--- a/app/views/admin/groups/show.html.haml
+++ b/app/views/admin/groups/show.html.haml
@@ -6,7 +6,7 @@
Edit
%hr
.row
- .span6
+ .col-md-6
.ui-box
.title
Group info:
@@ -39,14 +39,16 @@
%li
%strong
= link_to project.name_with_namespace, [:admin, project]
+ %span.label.label-gray
+ = repository_size(project)
%span.pull-right.light
%span.monospace= project.path_with_namespace + ".git"
- .span6
+ .col-md-6
.ui-box
.title
Add user(s) to the group:
- .ui-box-body.form-holder
+ .body.form-holder
%p.light
Read more about project permissions
%strong= link_to "here", help_permissions_path, class: "vlink"
@@ -72,5 +74,5 @@
= link_to user.name, admin_user_path(user)
%span.pull-right.light
= member.human_access
- = link_to group_users_group_path(@group, member), confirm: remove_user_from_group_message(@group, user), method: :delete, remote: true, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do
+ = link_to group_users_group_path(@group, member), data: { confirm: remove_user_from_group_message(@group, user) }, method: :delete, remote: true, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do
%i.icon-minus.icon-white
diff --git a/app/views/admin/hooks/index.html.haml b/app/views/admin/hooks/index.html.haml
index 1bd7b8eac62..ff90d513ca1 100644
--- a/app/views/admin/hooks/index.html.haml
+++ b/app/views/admin/hooks/index.html.haml
@@ -1,5 +1,5 @@
%h3.page-title
- System Hooks
+ System hooks
%p.light
#{link_to "System hooks ", help_system_hooks_path, class: "vlink"} can be
@@ -8,35 +8,28 @@
%hr
-= form_for @hook, as: :hook, url: admin_hooks_path, html: { class: 'form-inline' } do |f|
+= form_for @hook, as: :hook, url: admin_hooks_path, html: { class: 'form-horizontal' } do |f|
-if @hook.errors.any?
- .alert.alert-error
+ .alert.alert-danger
- @hook.errors.full_messages.each do |msg|
%p= msg
- .control-group
- = f.label :url, "URL:"
- .controls
- = f.text_field :url, class: "text_field input-xxlarge input-xpadding"
- &nbsp;
- = f.submit "Add System Hook", class: "btn btn-create"
+ .form-group
+ = f.label :url, "URL:", class: 'control-label'
+ .col-sm-10
+ = f.text_field :url, class: "form-control"
+ .form-actions
+ = f.submit "Add System Hook", class: "btn btn-create"
%hr
-if @hooks.any?
- %h3
- Hooks
- %small (#{@hooks.count})
- %br
- %table
- %tr
- %th URL
- %th Method
- %th
- - @hooks.each do |hook|
- %tr
- %td
+ .ui-box
+ .title
+ System hooks (#{@hooks.count})
+ %ul.well-list
+ - @hooks.each do |hook|
+ %li
+ .pull-right
+ = link_to 'Test Hook', admin_hook_test_path(hook), class: "btn btn-small"
+ = link_to 'Remove', admin_hook_path(hook), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-remove btn-small"
= link_to admin_hook_path(hook) do
%strong= hook.url
- = link_to 'Test Hook', admin_hook_test_path(hook), class: "btn btn-small pull-right"
- %td POST
- %td
- = link_to 'Remove', admin_hook_path(hook), confirm: 'Are you sure?', method: :delete, class: "btn btn-remove btn-small pull-right"
diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml
index bc297209ae5..940a539d5e1 100644
--- a/app/views/admin/projects/index.html.haml
+++ b/app/views/admin/projects/index.html.haml
@@ -1,39 +1,41 @@
.row
- .span4
+ .col-md-4
.admin-filter
- = form_tag admin_projects_path, method: :get, class: 'form-inline' do
- .control-group
- = label_tag :name, 'Name:', class: 'control-label'
- .controls
- = text_field_tag :name, params[:name], class: "span2"
-
- .control-group
- = label_tag :owner_id, 'Owner:', class: 'control-label'
- .controls
- = users_select_tag :owner_id, selected: params[:owner_id], class: 'input-large'
- .control-group
- = label_tag :public_only, 'Public Only', class: 'control-label'
- .controls
- = check_box_tag :public_only, 1, params[:public_only]
- .control-group
- = label_tag :with_push, 'Not empty', class: 'control-label'
- .controls
- = check_box_tag :with_push, 1, params[:with_push]
- &nbsp;
- %span.light Projects with push events
- .control-group
- = label_tag :abandoned, 'Abandoned', class: 'control-label'
- .controls
- = check_box_tag :abandoned, 1, params[:abandoned]
- &nbsp;
- %span.light No activity over 6 month
-
+ = form_tag admin_projects_path, method: :get, class: '' do
+ .form-group
+ = label_tag :name, 'Name:'
+ = text_field_tag :name, params[:name], class: "form-control"
+ .form-group
+ = label_tag :owner_id, 'Owner:'
+ %div
+ = users_select_tag :owner_id, selected: params[:owner_id], class: 'input-large input-clamp'
+ .checkbox
+ = label_tag :with_push, 'Not empty'
+ = check_box_tag :with_push, 1, params[:with_push]
+ &nbsp;
+ %span.light Projects with push events
+ .checkbox
+ = label_tag :abandoned, 'Abandoned'
+ = check_box_tag :abandoned, 1, params[:abandoned]
+ &nbsp;
+ %span.light No activity over 6 month
+ %fieldset
+ %strong Visibility level:
+ .visibility-levels
+ - Project.visibility_levels.each do |label, level|
+ .checkbox
+ %label
+ = check_box_tag 'visibility_levels[]', level, params[:visibility_levels].present? && params[:visibility_levels].include?(level.to_s)
+ %span.descr
+ = visibility_level_icon(level)
+ = label
.form-actions
= submit_tag "Search", class: "btn submit btn-primary"
= link_to "Reset", admin_projects_path, class: "btn"
- .span8
+
+ .col-md-8
.ui-box
.title
Projects (#{@projects.total_count})
@@ -42,14 +44,14 @@
%ul.well-list
- @projects.each do |project|
%li
- - if project.public
- = public_icon
- - else
- = private_icon
+ %span{ class: visibility_level_color(project.visibility_level) }
+ = visibility_level_icon(project.visibility_level)
= link_to project.name_with_namespace, [:admin, project]
.pull-right
+ %span.label.label-gray
+ = repository_size(project)
= link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small"
- = link_to 'Destroy', [project], confirm: remove_project_message(project), method: :delete, class: "btn btn-small btn-remove"
+ = link_to 'Destroy', [project], data: { confirm: remove_project_message(project) }, method: :delete, class: "btn btn-small btn-remove"
- if @projects.blank?
%p.nothing_here_message 0 projects matches
= paginate @projects, theme: "gitlab"
diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml
index 65b9911dc46..786482eee2e 100644
--- a/app/views/admin/projects/show.html.haml
+++ b/app/views/admin/projects/show.html.haml
@@ -5,7 +5,7 @@
Edit
%hr
.row
- .span6
+ .col-md-6
.ui-box
.title
Project info:
@@ -54,6 +54,11 @@
= @repository.path_to_repo
%li
+ %span.light Size
+ %strong
+ = repository_size(@project)
+
+ %li
%span.light last commit:
%strong
= last_commit(@project)
@@ -66,15 +71,25 @@
%li
%span.light access:
%strong
- - if @project.public
- %span.cblue
- %i.icon-share
- Public
- - else
- %span.cgreen
- %i.icon-lock
- Private
- .span6
+ %span{ class: visibility_level_color(@project.visibility_level) }
+ = visibility_level_icon(@project.visibility_level)
+ = visibility_level_label(@project.visibility_level)
+
+ .ui-box
+ .title
+ Transfer project
+ .body
+ = form_for @project, url: transfer_admin_project_path(@project), method: :put do |f|
+ .form-group
+ = f.label :namespace_id, "Namespace"
+ .col-sm-10
+ = namespace_select_tag :namespace_id, selected: params[:namespace_id], class: 'input-large'
+
+ .form-group
+ .col-sm-10
+ = f.submit 'Transfer', class: 'btn btn-primary'
+
+ .col-md-6
- if @group
.ui-box
.title
@@ -107,5 +122,5 @@
%span.light Owner
- else
%span.light= users_project.human_access
- = link_to project_team_member_path(@project, user), confirm: remove_from_project_team_message(@project, user), method: :delete, remote: true, class: "btn btn-small btn-remove" do
+ = link_to project_team_member_path(@project, user), data: { confirm: remove_from_project_team_message(@project, user)}, method: :delete, remote: true, class: "btn btn-small btn-remove" do
%i.icon-remove
diff --git a/app/views/admin/users/_form.html.haml b/app/views/admin/users/_form.html.haml
index 3f930c45fa6..a9fb62c9b96 100644
--- a/app/views/admin/users/_form.html.haml
+++ b/app/views/admin/users/_form.html.haml
@@ -2,34 +2,34 @@
= form_for [:admin, @user] do |f|
-if @user.errors.any?
#error_explanation
- %ul.unstyled.alert.alert-error
+ %ul.unstyled.alert.alert-danger
- @user.errors.full_messages.each do |msg|
%li= msg
%fieldset
%legend Account
- .control-group
+ .form-group
= f.label :name
- .controls
+ .col-sm-10
= f.text_field :name, required: true, autocomplete: "off"
%span.help-inline * required
- .control-group
+ .form-group
= f.label :username
- .controls
+ .col-sm-10
= f.text_field :username, required: true, autocomplete: "off"
%span.help-inline * required
- .control-group
+ .form-group
= f.label :email
- .controls
+ .col-sm-10
= f.text_field :email, required: true, autocomplete: "off"
%span.help-inline * required
- if @user.new_record?
%fieldset
%legend Password
- .control-group
+ .form-group
= f.label :password
- .controls
+ .col-sm-10
%strong
A temporary password will be generated and sent to user.
%br
@@ -37,49 +37,49 @@
- else
%fieldset
%legend Password
- .control-group
+ .form-group
= f.label :password
- .controls= f.password_field :password, disabled: f.object.force_random_password
- .control-group
+ .col-sm-10= f.password_field :password, disabled: f.object.force_random_password
+ .form-group
= f.label :password_confirmation
- .controls= f.password_field :password_confirmation, disabled: f.object.force_random_password
+ .col-sm-10= f.password_field :password_confirmation, disabled: f.object.force_random_password
%fieldset
%legend Access
.row
- .span8
- .control-group
+ .col-md-8
+ .form-group
= f.label :projects_limit
- .controls= f.number_field :projects_limit
+ .col-sm-10= f.number_field :projects_limit
- .control-group
+ .form-group
= f.label :can_create_group
- .controls= f.check_box :can_create_group
+ .col-sm-10= f.check_box :can_create_group
- .control-group
+ .form-group
= f.label :admin do
%strong.cred Administrator
- .controls= f.check_box :admin
- .span4
+ .col-sm-10= f.check_box :admin
+ .col-md-4
- unless @user.new_record?
- .alert.alert-error
+ .alert.alert-danger
- if @user.blocked?
%p This user is blocked and is not able to login to GitLab
= link_to 'Unblock User', unblock_admin_user_path(@user), method: :put, class: "btn btn-small"
- else
%p Blocked users will be removed from all projects &amp; will not be able to login to GitLab.
- = link_to 'Block User', block_admin_user_path(@user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn btn-small btn-remove"
+ = link_to 'Block User', block_admin_user_path(@user), data: {confirm: 'USER WILL BE BLOCKED! Are you sure?'}, method: :put, class: "btn btn-small btn-remove"
%fieldset
%legend Profile
- .control-group
+ .form-group
= f.label :skype
- .controls= f.text_field :skype
- .control-group
+ .col-sm-10= f.text_field :skype
+ .form-group
= f.label :linkedin
- .controls= f.text_field :linkedin
- .control-group
+ .col-sm-10= f.text_field :linkedin
+ .form-group
= f.label :twitter
- .controls= f.text_field :twitter
+ .col-sm-10= f.text_field :twitter
.form-actions
- if @user.new_record?
diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml
index b32f0ae87cc..1fa6fdfaff1 100644
--- a/app/views/admin/users/index.html.haml
+++ b/app/views/admin/users/index.html.haml
@@ -1,10 +1,12 @@
.row
- .span3
+ .col-md-3
.admin-filter
= form_tag admin_users_path, method: :get, class: 'form-inline' do
- = search_field_tag :name, params[:name], placeholder: 'Name, email or username', class: 'search-text-input span2'
- = button_tag type: 'submit', class: 'btn' do
- %i.icon-search
+ .append-bottom-10
+ .form-group
+ = search_field_tag :name, params[:name], placeholder: 'Name, email or username', class: 'form-control'
+ = button_tag type: 'submit', class: 'btn btn-primary' do
+ %i.icon-search
%ul.nav.nav-pills.nav-stacked
%li{class: "#{'active' unless params[:filter]}"}
= link_to admin_users_path do
@@ -25,7 +27,7 @@
%hr
= link_to 'Reset', admin_users_path, class: "btn btn-cancel"
- .span9
+ .col-md-9
.ui-box
.title
Users (#{@users.total_count})
@@ -53,6 +55,6 @@
- if user.blocked?
= link_to 'Unblock', unblock_admin_user_path(user), method: :put, class: "btn btn-small success"
- else
- = link_to 'Block', block_admin_user_path(user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn btn-small btn-remove"
- = link_to 'Destroy', [:admin, user], confirm: "USER #{user.name} WILL BE REMOVED! Are you sure?", method: :delete, class: "btn btn-small btn-remove"
+ = link_to 'Block', block_admin_user_path(user), data: {confirm: 'USER WILL BE BLOCKED! Are you sure?'}, method: :put, class: "btn btn-small btn-remove"
+ = link_to 'Destroy', [:admin, user], data: { confirm: "USER #{user.name} WILL BE REMOVED! All tickets linked to this user will also be removed! Maybe block the user instead? Are you sure?" }, method: :delete, class: "btn btn-small btn-remove"
= paginate @users, theme: "gitlab"
diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml
index 655ed5f7f79..bd276f22221 100644
--- a/app/views/admin/users/show.html.haml
+++ b/app/views/admin/users/show.html.haml
@@ -13,7 +13,7 @@
%hr
.row
- .span6
+ .col-md-6
.ui-box
.title
Account:
@@ -43,6 +43,16 @@
%span.light Member since:
%strong
= @user.created_at.stamp("Nov 12, 2031")
+ - if @user.confirmed_at
+ %li
+ %span.light Confirmed at:
+ %strong
+ = @user.confirmed_at.stamp("Nov 12, 2031")
+ - else
+ %li
+ %span.light Confirmed:
+ %strong.cred
+ No
%li
%span.light Last sign-in at:
@@ -76,7 +86,7 @@
%li User will be removed from joined projects and groups
%li Personal projects will be left
%li Owned groups will be left
- = link_to 'Unblock user', unblock_admin_user_path(@user), method: :put, class: "btn btn-new", confirm: 'Are you sure?'
+ = link_to 'Unblock user', unblock_admin_user_path(@user), method: :put, class: "btn btn-new", data: { confirm: 'Are you sure?' }
- else
.alert
%h4 Block this user
@@ -88,9 +98,9 @@
%li User will be removed from joined projects and groups
%li Personal projects will be left
%li Owned groups will be left
- = link_to 'Block user', block_admin_user_path(@user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn btn-remove"
+ = link_to 'Block user', block_admin_user_path(@user), data: { confirm: 'USER WILL BE BLOCKED! Are you sure?' }, method: :put, class: "btn btn-remove"
- .alert.alert-error
+ .alert.alert-danger
%h4
Remove user
%br
@@ -104,9 +114,9 @@
%li
Next groups with all content will be removed:
%strong #{@user.solo_owned_groups.map(&:name).join(', ')}
- = link_to 'Remove user', [:admin, @user], confirm: "USER #{@user.name} WILL BE REMOVED! Are you sure?", method: :delete, class: "btn btn-remove"
+ = link_to 'Remove user', [:admin, @user], data: { confirm: "USER #{@user.name} WILL BE REMOVED! Are you sure?" }, method: :delete, class: "btn btn-remove"
- .span6
+ .col-md-6
- if @user.users_groups.present?
.ui-box
.title Groups:
@@ -118,7 +128,7 @@
.pull-right
%span.light= user_group.human_access
- unless user_group.owner?
- = link_to group_users_group_path(group, user_group), confirm: remove_user_from_group_message(group, @user), method: :delete, remote: true, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do
+ = link_to group_users_group_path(group, user_group), data: { confirm: remove_user_from_group_message(group, @user) }, method: :delete, remote: true, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do
%i.icon-remove.icon-white
.ui-box
@@ -138,7 +148,7 @@
%span.light= tm.human_access
- if tm.respond_to? :project
- = link_to project_team_member_path(project, @user), confirm: remove_from_project_team_message(project, @user), remote: true, method: :delete, class: "btn-tiny btn btn-remove", title: 'Remove user from project' do
+ = link_to project_team_member_path(project, @user), data: { confirm: remove_from_project_team_message(project, @user) }, remote: true, method: :delete, class: "btn-tiny btn btn-remove", title: 'Remove user from project' do
%i.icon-remove
diff --git a/app/views/dashboard/_groups.html.haml b/app/views/dashboard/_groups.html.haml
index b4f3866228d..d6937ca4813 100644
--- a/app/views/dashboard/_groups.html.haml
+++ b/app/views/dashboard/_groups.html.haml
@@ -1,6 +1,6 @@
.ui-box
.title.clearfix
- = search_field_tag :filter_group, nil, placeholder: 'Filter by name', class: 'dash-filter'
+ = search_field_tag :filter_group, nil, placeholder: 'Filter by name', class: 'dash-filter form-control'
- if current_user.can_create_group?
%span.pull-right
= link_to new_group_path, class: "btn btn-new" do
diff --git a/app/views/dashboard/_project.html.haml b/app/views/dashboard/_project.html.haml
index 50b833f3d82..db445eed233 100644
--- a/app/views/dashboard/_project.html.haml
+++ b/app/views/dashboard/_project.html.haml
@@ -1,4 +1,6 @@
= link_to project_path(project), class: dom_class(project) do
+ .dash-project-access-icon
+ = visibility_level_icon(project.visibility_level)
%span.namespace-name
- if project.namespace
= project.namespace.human_name
@@ -7,6 +9,3 @@
= truncate(project.name, length: 25)
%span.arrow
%i.icon-angle-right
- %span.last-activity
- %span Last activity:
- %span.date= project_last_activity(project)
diff --git a/app/views/dashboard/_projects.html.haml b/app/views/dashboard/_projects.html.haml
index b79b27fc95a..8313cc07b5e 100644
--- a/app/views/dashboard/_projects.html.haml
+++ b/app/views/dashboard/_projects.html.haml
@@ -1,6 +1,6 @@
.ui-box
.title.clearfix
- = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'dash-filter'
+ = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'dash-filter form-control'
- if current_user.can_create_project?
%span.pull-right
= link_to new_project_path, class: "btn btn-new" do
diff --git a/app/views/dashboard/issues.html.haml b/app/views/dashboard/issues.html.haml
index 82880d5ef63..19bd4e7bd54 100644
--- a/app/views/dashboard/issues.html.haml
+++ b/app/views/dashboard/issues.html.haml
@@ -1,13 +1,13 @@
%h3.page-title
- Issues assigned to me
+ Issues
%span.pull-right #{@issues.total_count} issues
%p.light
- For all issues you should visit the project's issues page, or use the search panel to find a specific issue.
+ List all issues from all project's you have access to.
%hr
.row
- .span3
+ .col-md-3
= render 'shared/filter', entity: 'issue'
- .span9
+ .col-md-9
= render 'shared/issues'
diff --git a/app/views/dashboard/merge_requests.html.haml b/app/views/dashboard/merge_requests.html.haml
index 9c96edeefd5..b487a4d6666 100644
--- a/app/views/dashboard/merge_requests.html.haml
+++ b/app/views/dashboard/merge_requests.html.haml
@@ -4,10 +4,10 @@
%p.light
- Only merge requests created by you or assigned to you are listed here.
+ List all merge requests from all project's you have access to.
%hr
.row
- .span3
+ .col-md-3
= render 'shared/filter', entity: 'merge_request'
- .span9
+ .col-md-9
= render 'shared/merge_requests'
diff --git a/app/views/dashboard/projects.html.haml b/app/views/dashboard/projects.html.haml
index 785eb613334..e6a69195105 100644
--- a/app/views/dashboard/projects.html.haml
+++ b/app/views/dashboard/projects.html.haml
@@ -1,31 +1,62 @@
-%h3.page-title My Projects
+%h3.page-title
+ My Projects
+.pull-right
+ .dropdown.inline
+ %a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"}
+ %span.light sort:
+ - if @sort.present?
+ = @sort.humanize
+ - else
+ Name
+ %b.caret
+ %ul.dropdown-menu
+ %li
+ = link_to projects_dashboard_filter_path(sort: nil) do
+ Name
+ = link_to projects_dashboard_filter_path(sort: 'newest') do
+ Newest
+ = link_to projects_dashboard_filter_path(sort: 'oldest') do
+ Oldest
+ = link_to projects_dashboard_filter_path(sort: 'recently_updated') do
+ Recently updated
+ = link_to projects_dashboard_filter_path(sort: 'last_updated') do
+ Last updated
%p.light
All projects you have access to are listed here. Public projects are not included here unless you are a member
%hr
.row
- .span3
- %ul.nav.nav-pills.nav-stacked
- = nav_tab :scope, nil do
- = link_to projects_dashboard_path do
- All
- %span.pull-right
- = current_user.authorized_projects.count
- = nav_tab :scope, 'personal' do
- = link_to projects_dashboard_path(scope: 'personal') do
- Personal
- %span.pull-right
- = current_user.personal_projects.count
- = nav_tab :scope, 'joined' do
- = link_to projects_dashboard_path(scope: 'joined') do
- Joined
- %span.pull-right
- = current_user.authorized_projects.joined(current_user).count
- = nav_tab :scope, 'owned' do
- = link_to projects_dashboard_path(scope: 'owned') do
- Owned
- %span.pull-right
- = current_user.owned_projects.count
+ .col-md-3.hidden-sm.hidden-xs.side-filters
+ %fieldset
+ %ul.nav.nav-pills.nav-stacked
+ = nav_tab :scope, nil do
+ = link_to projects_dashboard_filter_path(scope: nil) do
+ All
+ %span.pull-right
+ = current_user.authorized_projects.count
+ = nav_tab :scope, 'personal' do
+ = link_to projects_dashboard_filter_path(scope: 'personal') do
+ Personal
+ %span.pull-right
+ = current_user.personal_projects.count
+ = nav_tab :scope, 'joined' do
+ = link_to projects_dashboard_filter_path(scope: 'joined') do
+ Joined
+ %span.pull-right
+ = current_user.authorized_projects.joined(current_user).count
+ = nav_tab :scope, 'owned' do
+ = link_to projects_dashboard_filter_path(scope: 'owned') do
+ Owned
+ %span.pull-right
+ = current_user.owned_projects.count
+ %fieldset
+ %legend Visibility
+ %ul.bordered-list.visibility-filter
+ - Gitlab::VisibilityLevel.values.each do |level|
+ %li{ class: (level.to_s == params[:visibility_level]) ? 'active' : 'light' }
+ = link_to projects_dashboard_filter_path(visibility_level: level) do
+ = visibility_level_icon(level)
+ = visibility_level_label(level)
- if @groups.present?
%fieldset
@@ -33,7 +64,7 @@
%ul.bordered-list
- @groups.each do |group|
%li{ class: (group.name == params[:group]) ? 'active' : 'light' }
- = link_to projects_dashboard_path(group: group.name) do
+ = link_to projects_dashboard_filter_path(group: group.name) do
%i.icon-folder-close-alt
= group.name
%small.pull-right
@@ -47,21 +78,25 @@
%ul.bordered-list
- @labels.each do |label|
%li{ class: (label.name == params[:label]) ? 'active' : 'light' }
- = link_to projects_dashboard_path(scope: params[:scope], label: label.name) do
+ = link_to projects_dashboard_filter_path(scope: params[:scope], label: label.name) do
%i.icon-tag
= label.name
- .span9
+ .col-md-9
%ul.bordered-list.my-projects.top-list
- @projects.each do |project|
- %li
+ %li.my-project-row
%h4.project-title
+ .project-access-icon
+ = visibility_level_icon(project.visibility_level)
= link_to project_path(project), class: dom_class(project) do
= project.name_with_namespace
- - if project.public
- %small.access-icon
- = public_icon
- Public
+
+ - if current_user.can_leave_project?(project)
+ .pull-right
+ = link_to leave_project_team_members_path(project), data: { confirm: "Leave project?"}, method: :delete, remote: true, class: "btn-tiny btn remove-row", title: 'Leave project' do
+ %i.icon-signout
+ Leave
- if project.forked_from_project
%small.pull-right
@@ -70,6 +105,10 @@
= link_to project.forked_from_project.name_with_namespace, project_path(project.forked_from_project)
.project-info
.pull-right
+ - if project.archived?
+ %span.label
+ %i.icon-archive
+ Archived
- project.labels.each do |label|
%span.label.label-info
%i.icon-tag
@@ -80,6 +119,7 @@
%span.light Last activity:
%span.date= project_last_activity(project)
+
- if @projects.blank?
%li
%h3.nothing_here_message There are no projects here.
diff --git a/app/views/dashboard/show.html.haml b/app/views/dashboard/show.html.haml
index 2305eae1f71..c4018d4b6d8 100644
--- a/app/views/dashboard/show.html.haml
+++ b/app/views/dashboard/show.html.haml
@@ -1,8 +1,8 @@
- if @has_authorized_projects
- .dashboard
- .activities.span8
+ .dashboard.row
+ .activities.col-md-8
= render 'activities'
- .side.span4
+ .side.col-md-4.hidden-sm
= render 'sidebar'
- else
diff --git a/app/views/dashboard/show.js.haml b/app/views/dashboard/show.js.haml
deleted file mode 100644
index 7e5a148e5ef..00000000000
--- a/app/views/dashboard/show.js.haml
+++ /dev/null
@@ -1,2 +0,0 @@
-:plain
- Pager.append(#{@events.count}, "#{escape_javascript(render(@events))}");
diff --git a/app/views/devise/confirmations/new.html.erb b/app/views/devise/confirmations/new.html.erb
deleted file mode 100644
index adc9b672092..00000000000
--- a/app/views/devise/confirmations/new.html.erb
+++ /dev/null
@@ -1,12 +0,0 @@
-<h2>Resend confirmation instructions</h2>
-
-<%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %>
- <%= devise_error_messages! %>
-
- <div><%= f.label :email %><br />
- <%= f.email_field :email %></div>
-
- <div><%= f.submit "Resend confirmation instructions" %></div>
-<% end %>
-
-<%= render partial: "devise/shared/links" %>
diff --git a/app/views/devise/confirmations/new.html.haml b/app/views/devise/confirmations/new.html.haml
new file mode 100644
index 00000000000..dd63a232fe2
--- /dev/null
+++ b/app/views/devise/confirmations/new.html.haml
@@ -0,0 +1,14 @@
+.login-box
+ %h3.page-title Resend confirmation instructions
+ = form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f|
+ .devise-errors
+ = devise_error_messages!
+ = f.email_field :email, placeholder: 'Email', class: "form-control", required: true
+ .clearfix.append-bottom-10
+ = f.submit "Resend confirmation instructions", class: 'btn btn-success'
+ %hr
+ %p
+ %span.light
+ Already have login and password?
+ %strong
+ = link_to "Sign in", new_session_path(resource_name)
diff --git a/app/views/devise/mailer/confirmation_instructions.html.erb b/app/views/devise/mailer/confirmation_instructions.html.erb
index 7b4fd526964..553d08369e9 100644
--- a/app/views/devise/mailer/confirmation_instructions.html.erb
+++ b/app/views/devise/mailer/confirmation_instructions.html.erb
@@ -1,5 +1,9 @@
-<p>Welcome <%= @resource.email %>!</p>
+<p>Welcome <%= @resource.name %>!</p>
-<p>You can confirm your account through the link below:</p>
+<% if @resource.unconfirmed_email.present? %>
+ <p>You can confirm your email (<%= @resource.unconfirmed_email %>) through the link below:</p>
+<% else %>
+ <p>You can confirm your account through the link below:</p>
+<% end %>
<p><%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @resource.confirmation_token) %></p>
diff --git a/app/views/devise/passwords/edit.html.haml b/app/views/devise/passwords/edit.html.haml
index 3e4a4a0f1c8..95c52608e1f 100644
--- a/app/views/devise/passwords/edit.html.haml
+++ b/app/views/devise/passwords/edit.html.haml
@@ -1,11 +1,12 @@
= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put, class: "login-box" }) do |f|
%h3 Change your password
- = devise_error_messages!
+ .devise-errors
+ = devise_error_messages!
= f.hidden_field :reset_password_token
%div
- = f.password_field :password, class: "text top", placeholder: "New password"
+ = f.password_field :password, class: "form-control top", placeholder: "New password", required: true
%div
- = f.password_field :password_confirmation, class: "text bottom", placeholder: "Confirm new password"
+ = f.password_field :password_confirmation, class: "form-control bottom", placeholder: "Confirm new password", required: true
%div
.clearfix.append-bottom-10
= f.submit "Change my password", class: "btn btn-primary"
diff --git a/app/views/devise/passwords/new.html.haml b/app/views/devise/passwords/new.html.haml
index 3df65d037af..a14ef2995c8 100644
--- a/app/views/devise/passwords/new.html.haml
+++ b/app/views/devise/passwords/new.html.haml
@@ -1,10 +1,13 @@
= form_for(resource, as: resource_name, url: password_path(resource_name), html: { class: "login-box", method: :post }) do |f|
%h3.page-title Reset password
- = devise_error_messages!
- = f.email_field :email, placeholder: "Email", class: "text"
- %br/
- %br/
- = f.submit "Reset password", class: "btn-primary btn"
- .pull-right
- = link_to "Sign in", new_session_path(resource_name), class: "btn"
- %br/
+ .devise-errors
+ = devise_error_messages!
+ = f.email_field :email, placeholder: "Email", class: "form-control", required: true
+ .clearfix.append-bottom-10
+ = f.submit "Reset password", class: "btn-primary btn"
+ %hr
+ %p
+ %span.light
+ Already have login and password?
+ %strong
+ = link_to "Sign in", new_session_path(resource_name)
diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb
index 139acf28a9f..b11817af95d 100644
--- a/app/views/devise/registrations/edit.html.erb
+++ b/app/views/devise/registrations/edit.html.erb
@@ -23,6 +23,6 @@
<h3>Cancel my account</h3>
-<p>Unhappy? <%= link_to "Cancel my account", registration_path(resource_name), confirm: "Are you sure?", method: :delete %>.</p>
+<p>Unhappy? <%= link_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %>.</p>
<%= link_to "Back", :back %>
diff --git a/app/views/devise/registrations/new.html.haml b/app/views/devise/registrations/new.html.haml
index d749d7bac09..24bc0406544 100644
--- a/app/views/devise/registrations/new.html.haml
+++ b/app/views/devise/registrations/new.html.haml
@@ -1,19 +1,24 @@
= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { class: "login-box" }) do |f|
%h3.page-title Sign Up
- %br
- = devise_error_messages!
+ .devise-errors
+ = devise_error_messages!
%div
- = f.text_field :name, class: "text top", placeholder: "Name", required: true
+ = f.text_field :name, class: "form-control top", placeholder: "Name", required: true
%div
- = f.text_field :username, class: "text middle", placeholder: "Username", required: true
+ = f.text_field :username, class: "form-control middle", placeholder: "Username", required: true
%div
- = f.email_field :email, class: "text middle", placeholder: "Email", required: true
+ = f.email_field :email, class: "form-control middle", placeholder: "Email", required: true
%div
- = f.password_field :password, class: "text middle", placeholder: "Password", required: true
+ = f.password_field :password, class: "form-control middle", placeholder: "Password", required: true
%div
- = f.password_field :password_confirmation, class: "text bottom", placeholder: "Confirm password", required: true
+ = f.password_field :password_confirmation, class: "form-control bottom", placeholder: "Confirm password", required: true
%div
= f.submit "Sign up", class: "btn-create btn"
%hr
- = link_to "Sign in", new_session_path(resource_name)
- = link_to "Forgot your password?", new_password_path(resource_name), class: "pull-right"
+ %p
+ %span.light
+ Have an account?
+ %strong
+ = link_to "Sign in", new_session_path(resource_name)
+ %p
+ = link_to "Forgot your password?", new_password_path(resource_name)
diff --git a/app/views/devise/sessions/_new_base.html.haml b/app/views/devise/sessions/_new_base.html.haml
index 0c8be9d5c48..a2f85fa3fe2 100644
--- a/app/views/devise/sessions/_new_base.html.haml
+++ b/app/views/devise/sessions/_new_base.html.haml
@@ -1,6 +1,6 @@
= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f|
- = f.text_field :login, class: "text top", placeholder: "Username or Email", autofocus: "autofocus"
- = f.password_field :password, class: "text bottom", placeholder: "Password"
+ = f.text_field :login, class: "form-control top", placeholder: "Username or Email", autofocus: "autofocus"
+ = f.password_field :password, class: "form-control bottom", placeholder: "Password"
- if devise_mapping.rememberable?
.clearfix.append-bottom-10
%label.checkbox.remember_me{for: "user_remember_me"}
diff --git a/app/views/devise/sessions/_new_ldap.html.haml b/app/views/devise/sessions/_new_ldap.html.haml
index 575d33949b6..bb1d0a4001f 100644
--- a/app/views/devise/sessions/_new_ldap.html.haml
+++ b/app/views/devise/sessions/_new_ldap.html.haml
@@ -1,5 +1,5 @@
= form_tag(user_omniauth_callback_path(:ldap), id: 'new_ldap_user' ) do
- = text_field_tag :username, nil, {class: "text top", placeholder: "LDAP Login", autofocus: "autofocus"}
- = password_field_tag :password, nil, {class: "text bottom", placeholder: "Password"}
+ = text_field_tag :username, nil, {class: "form-control top", placeholder: "LDAP Login", autofocus: "autofocus"}
+ = password_field_tag :password, nil, {class: "form-control bottom", placeholder: "Password"}
%br/
= submit_tag "LDAP Sign in", class: "btn-create btn"
diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml
index 15854effbfa..938f61d2093 100644
--- a/app/views/devise/sessions/new.html.haml
+++ b/app/views/devise/sessions/new.html.haml
@@ -1,7 +1,7 @@
.login-box
%h3.page-title Sign in
- if ldap_enabled?
- %ul.nav.nav-tabs
+ %ul.nav.nav-tabs.append-bottom-20
%li.active
= link_to 'LDAP', '#tab-ldap', 'data-toggle' => 'tab'
%li
@@ -17,14 +17,20 @@
= render 'devise/sessions/oauth_providers' if devise_mapping.omniauthable?
+ %hr
- if gitlab_config.signup_enabled
- %hr
- %div
- Don't have an account?
+ %p
+ %span.light
+ Don't have an account?
%strong
= link_to "Sign up", new_registration_path(resource_name)
+ %p
+ %span.light Did not receive confirmation email?
+ = link_to "Send again", new_confirmation_path(resource_name)
+
+
- if extra_config.has_key?('sign_in_text')
%hr
= markdown(extra_config.sign_in_text)
diff --git a/app/views/events/_commit.html.haml b/app/views/events/_commit.html.haml
index 4d4b24009f4..135320da57e 100644
--- a/app/views/events/_commit.html.haml
+++ b/app/views/events/_commit.html.haml
@@ -2,4 +2,4 @@
.commit-row-title
= link_to commit[:id][0..8], project_commit_path(project, commit[:id]), class: "commit_short_id", alt: ''
&nbsp;
- = gfm escape_once(truncate(commit[:message], length: 70)) rescue "--broken encoding"
+ = gfm event_commit_title(commit[:message])
diff --git a/app/views/events/_event.html.haml b/app/views/events/_event.html.haml
index 892dacafa62..8cf26671e3b 100644
--- a/app/views/events/_event.html.haml
+++ b/app/views/events/_event.html.haml
@@ -1,10 +1,10 @@
- if event.proper?
.event-item{class: "#{event.body? ? "event-block" : "event-inline" }"}
%span.cgray.pull-right
- #{time_ago_in_words(event.created_at)} ago.
+ #{time_ago_with_tooltip(event.created_at)}
= cache event do
- = image_tag avatar_icon(event.author_email), class: "avatar s24", alt:''
+ = image_tag avatar_icon(event.author_email, 24), class: "avatar s24", alt:''
- if event.push?
= render "events/event/push", event: event
diff --git a/app/views/events/_event_last_push.html.haml b/app/views/events/_event_last_push.html.haml
index de5634d3c55..6db05a1a5a6 100644
--- a/app/views/events/_event_last_push.html.haml
+++ b/app/views/events/_event_last_push.html.haml
@@ -5,9 +5,8 @@
%strong= truncate(event.ref_name, length: 28)
at
%strong= link_to_project event.project
- %span
- = time_ago_in_words(event.created_at)
- ago.
+ #{time_ago_with_tooltip(event.created_at)}
+
.pull-right
= link_to new_mr_path_from_push_event(event), title: "New Merge Request", class: "btn btn-create btn-small" do
Create Merge Request
diff --git a/app/views/events/_events.html.haml b/app/views/events/_events.html.haml
new file mode 100644
index 00000000000..3d62d478869
--- /dev/null
+++ b/app/views/events/_events.html.haml
@@ -0,0 +1 @@
+= render @events
diff --git a/app/views/groups/_new_group_member.html.haml b/app/views/groups/_new_group_member.html.haml
index 234392c03e1..a0dd4fbb595 100644
--- a/app/views/groups/_new_group_member.html.haml
+++ b/app/views/groups/_new_group_member.html.haml
@@ -1,20 +1,18 @@
-= form_for @users_group, url: group_users_groups_path(@group) do |f|
- %fieldset
- %legend
- New member(s) for
- %strong #{@group.name}
- group
+= form_for @users_group, url: group_users_groups_path(@group), html: { class: 'form-horizontal' } do |f|
+ %h4.append-bottom-20
+ New member(s) for
+ %strong #{@group.name}
+ group
- %p 1. Choose users you want in the group
- .control-group
- = f.label :user_ids, "People"
- .controls= users_select_tag(:user_ids, multiple: true, class: 'input-large')
+ %p 1. Choose users you want in the group
+ .form-group
+ = f.label :user_ids, "People", class: 'control-label'
+ .col-sm-10= users_select_tag(:user_ids, multiple: true, class: 'input-large')
- %p 2. Set access level for them
- .control-group
- = f.label :group_access, "Group Access"
- .controls= select_tag :group_access, options_for_select(UsersGroup.group_access_roles, @users_group.group_access), class: "project-access-select chosen"
-
- .form-actions
- = f.submit 'Add users into group', class: "btn btn-create"
+ %p 2. Set access level for them
+ .form-group
+ = f.label :group_access, "Group Access", class: 'control-label'
+ .col-sm-10= select_tag :group_access, options_for_select(UsersGroup.group_access_roles, @users_group.group_access), class: "project-access-select chosen"
+ .form-actions
+ = f.submit 'Add users into group', class: "btn btn-create"
diff --git a/app/views/groups/_projects.html.haml b/app/views/groups/_projects.html.haml
index 16a3c60f660..98b7b88865b 100644
--- a/app/views/groups/_projects.html.haml
+++ b/app/views/groups/_projects.html.haml
@@ -12,10 +12,9 @@
- projects.each do |project|
%li.project-row
= link_to project_path(project), class: dom_class(project) do
+ .dash-project-access-icon
+ = visibility_level_icon(project.visibility_level)
%span.project-name
= truncate(project.name, length: 25)
%span.arrow
%i.icon-angle-right
- %span.last-activity
- %span Last activity:
- %span.date= project_last_activity(project)
diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml
index 10b974ea222..77734815b8a 100644
--- a/app/views/groups/edit.html.haml
+++ b/app/views/groups/edit.html.haml
@@ -1,5 +1,5 @@
.row
- .span2
+ .col-md-2
%ul.nav.nav-pills.nav-stacked.nav-stacked-menu
%li.active
= link_to '#tab-edit', 'data-toggle' => 'tab' do
@@ -12,7 +12,7 @@
%li
= link_to 'Remove', '#tab-remove', 'data-toggle' => 'tab'
- .span10
+ .col-md-10
.tab-content
.tab-pane.active#tab-edit
.ui-box
@@ -20,20 +20,20 @@
%strong= @group.name
group settings:
%div.form-holder
- = form_for @group do |f|
+ = form_for @group, html: { class: "form-horizontal" } do |f|
- if @group.errors.any?
- .alert.alert-error
+ .alert.alert-danger
%span= @group.errors.full_messages.first
- .control-group
- = f.label :name do
+ .form-group
+ = f.label :name, class: 'control-label' do
Group name
- .controls
- = f.text_field :name, placeholder: "Ex. OpenSource", class: "input-xxlarge left"
+ .col-sm-10
+ = f.text_field :name, placeholder: "Ex. OpenSource", class: "form-control left"
- .control-group.group-description-holder
- = f.label :description, "Details"
- .controls
- = f.text_area :description, maxlength: 250, class: "input-xxlarge js-gfm-input", rows: 4
+ .form-group.group-description-holder
+ = f.label :description, "Details", class: 'control-label'
+ .col-sm-10
+ = f.text_area :description, maxlength: 250, class: "form-control js-gfm-input", rows: 4
.form-actions
= f.submit 'Save group', class: "btn btn-save"
@@ -51,25 +51,22 @@
%ul.well-list
- @group.projects.each do |project|
%li
- - if project.public
- = public_icon
- - else
- = private_icon
+ = visibility_level_icon(project.visibility_level)
= link_to project.name_with_namespace, project
.pull-right
= link_to 'Members', project_team_index_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small"
= link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small"
- = link_to 'Remove', project, confirm: remove_project_message(project), method: :delete, class: "btn btn-small btn-remove"
+ = link_to 'Remove', project, data: { confirm: remove_project_message(project)}, method: :delete, class: "btn btn-small btn-remove"
- if @group.projects.blank?
%p.nothing_here_message This group has no projects yet
.tab-pane#tab-remove
.ui-box.ui-box-danger
.title Remove group
- .ui-box-body
+ .body
%p
Removing group will cause all child projects and resources to be removed.
%p
%strong Removed group can not be restored!
- = link_to 'Remove Group', @group, confirm: 'Removed group can not be restored! Are you sure?', method: :delete, class: "btn btn-remove"
+ = link_to 'Remove Group', @group, data: {confirm: 'Removed group can not be restored! Are you sure?'}, method: :delete, class: "btn btn-remove"
diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml
index 482613f172d..e24df310910 100644
--- a/app/views/groups/issues.html.haml
+++ b/app/views/groups/issues.html.haml
@@ -1,5 +1,5 @@
%h3.page-title
- Issues assigned to me
+ Issues
%span.pull-right #{@issues.total_count} issues
%p.light
@@ -9,7 +9,7 @@
%hr
.row
- .span3
+ .col-md-3
= render 'shared/filter', entity: 'issue'
- .span9
+ .col-md-9
= render 'shared/issues'
diff --git a/app/views/groups/merge_requests.html.haml b/app/views/groups/merge_requests.html.haml
index 8a9b03535bc..eaf85bbdbc8 100644
--- a/app/views/groups/merge_requests.html.haml
+++ b/app/views/groups/merge_requests.html.haml
@@ -3,12 +3,12 @@
%span.pull-right #{@merge_requests.total_count} merge requests
%p.light
- Authored or assigned to you from
+ Only merge requests from
%strong #{@group.name}
- group. To see all merge requests you should visit #{link_to 'dashboard', merge_requests_dashboard_path} page.
+ group are listed here. To see all merge requests you should visit #{link_to 'dashboard', merge_requests_dashboard_path} page.
%hr
.row
- .span3
+ .col-md-3
= render 'shared/filter', entity: 'merge_request'
- .span9
+ .col-md-9
= render 'shared/merge_requests'
diff --git a/app/views/groups/new.html.haml b/app/views/groups/new.html.haml
index d6d514e9b18..14b60687ed2 100644
--- a/app/views/groups/new.html.haml
+++ b/app/views/groups/new.html.haml
@@ -1,20 +1,20 @@
= form_for @group do |f|
- if @group.errors.any?
- .alert.alert-error
+ .alert.alert-danger
%span= @group.errors.full_messages.first
- .control-group
+ .form-group
= f.label :name do
Group name
- .controls
- = f.text_field :name, placeholder: "Ex. OpenSource", class: "input-xxlarge left"
+ .col-sm-10
+ = f.text_field :name, placeholder: "Ex. OpenSource", class: "form-control left"
- .control-group.group-description-holder
+ .form-group.group-description-holder
= f.label :description, "Details"
- .controls
- = f.text_area :description, maxlength: 250, class: "input-xxlarge js-gfm-input", rows: 4
+ .col-sm-10
+ = f.text_area :description, maxlength: 250, class: "form-control js-gfm-input", rows: 4
- .control-group
- .controls
+ .form-group
+ .col-sm-10
%ul
%li A group is a collection of several projects
%li Groups are private by default
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index e613ed3eaa3..5332d383c4c 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -1,5 +1,5 @@
.dashboard
- .activities.span8
+ .activities.col-md-8.hidden-sm
= render "events/event_last_push", event: @last_push
= link_to dashboard_path, class: 'btn btn-tiny' do
&larr; To dashboard
@@ -12,7 +12,7 @@
- else
%p.nothing_here_message Project activity will be displayed here
.loading.hide
- .side.span4
+ .side.col-md-4
- if @group.description.present?
.description-block
= @group.description
diff --git a/app/views/groups/show.js.haml b/app/views/groups/show.js.haml
deleted file mode 100644
index 7e5a148e5ef..00000000000
--- a/app/views/groups/show.js.haml
+++ /dev/null
@@ -1,2 +0,0 @@
-:plain
- Pager.append(#{@events.count}, "#{escape_javascript(render(@events))}");
diff --git a/app/views/help/_api_layout.html.haml b/app/views/help/_api_layout.html.haml
index 502cc31a80c..c211b658410 100644
--- a/app/views/help/_api_layout.html.haml
+++ b/app/views/help/_api_layout.html.haml
@@ -1,5 +1,5 @@
.row
- .span3
+ .col-md-3
.append-bottom-20
= link_to help_path, class: 'btn btn-small' do
%i.icon-angle-left
@@ -9,5 +9,5 @@
%li{class: file == @category ? 'active' : nil}
= link_to file.titleize, help_api_file_path(file)
- .span9.pull-right
+ .col-md-9.pull-right
= yield
diff --git a/app/views/help/_layout.html.haml b/app/views/help/_layout.html.haml
index da917888eee..fc0ac277a48 100644
--- a/app/views/help/_layout.html.haml
+++ b/app/views/help/_layout.html.haml
@@ -1,37 +1,11 @@
.row
- .span3{:"data-spy" => 'affix'}
- .ui-box
- .title
- Help
- %ul.well-list
- %li
- %strong= link_to "Workflow", help_workflow_path
- %li
- %strong= link_to "SSH keys", help_ssh_path
-
- %li
- %strong= link_to "GitLab Markdown", help_markdown_path
-
- %li
- %strong= link_to "Permissions", help_permissions_path
-
- %li
- %strong= link_to "API", help_api_path
-
- %li
- %strong= link_to "Web Hooks", help_web_hooks_path
-
- %li
- %strong= link_to "Rake Tasks", help_raketasks_path
-
- %li
- %strong= link_to "System Hooks", help_system_hooks_path
-
- %li
- %strong= link_to "Public Access", help_public_access_path
-
- %li
- %strong= link_to "Security", help_security_path
-
- .span9.pull-right
+ .col-md-3{:"data-spy" => 'affix'}
+ %h3.page-title Help
+ %ul.nav.nav-pills.nav-stacked
+ - links = {:"Workflow" => help_workflow_path, :"SSH Keys" => help_ssh_path, :"GitLab Markdown" => help_markdown_path, :"Permissions" => help_permissions_path, :"API" => help_api_path, :"Web Hooks" => help_web_hooks_path, :"Rake Tasks" => help_raketasks_path, :"System Hooks" => help_system_hooks_path, :"Public Access" => help_public_access_path, :"Security" => help_security_path}
+ - links.each do |title,path|
+ %li{class: current_page?(path) ? 'active' : nil}
+ = link_to title, path
+
+ .col-md-9.pull-right
= yield
diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml
index e979e7c0d07..5f80a456cf7 100644
--- a/app/views/help/_shortcuts.html.haml
+++ b/app/views/help/_shortcuts.html.haml
@@ -1,30 +1,32 @@
#modal-shortcuts.modal.hide
- .modal-header
- %a.close{href: "#", "data-dismiss" => "modal"} ×
- %h3 Keyboard Shortcuts
- .modal-body
- %h5 Global Shortcuts
- %p
- %span.label.label-inverse s
- &ndash;
- Focus Search
- %p
- %span.label.label-inverse ?
- &ndash;
- Show this dialog
+ .modal-dialog
+ .modal-content
+ .modal-header
+ %a.close{href: "#", "data-dismiss" => "modal"} ×
+ %h3 Keyboard Shortcuts
+ .modal-body
+ %h5 Global Shortcuts
+ %p
+ %span.label.label-inverse s
+ &ndash;
+ Focus Search
+ %p
+ %span.label.label-inverse ?
+ &ndash;
+ Show this dialog
- %h5 Project Files browsing
- %p
- %span.label.label-inverse
- %i.icon-arrow-up
- &ndash;
- Move selection up
- %p
- %span.label.label-inverse
- %i.icon-arrow-down
- &ndash;
- Move selection down
- %p
- %span.label.label-inverse Enter
- &ndash;
- Open selection
+ %h5 Project Files browsing
+ %p
+ %span.label.label-inverse
+ %i.icon-arrow-up
+ &ndash;
+ Move selection up
+ %p
+ %span.label.label-inverse
+ %i.icon-arrow-down
+ &ndash;
+ Move selection down
+ %p
+ %span.label.label-inverse Enter
+ &ndash;
+ Open selection
diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml
index 869f87df4db..f1cb723ebac 100644
--- a/app/views/help/index.html.haml
+++ b/app/views/help/index.html.haml
@@ -1,4 +1,4 @@
-.hero-unit
+.jumbotron
%h2
GitLab
%span= Gitlab::VERSION
@@ -11,7 +11,7 @@
Read more about GitLab at #{link_to "gitlab.org", "http://gitlab.org/", target: "_blank"}.
.row
- .span4
+ .col-md-4
.ui-box
.title
Quick help
@@ -33,9 +33,9 @@
= link_to "Stack Overflow", "http://stackoverflow.com/questions/tagged/gitlab"
%li
Browse our
- = link_to "issue tracker", "https://github.com/gitlabhq/gitlabhq/issues"
+ = link_to "issue tracker", "https://gitlab.com/gitlab-org/gitlab-ce/issues"
- .span4
+ .col-md-4
.ui-box
.title
User documentation
@@ -64,7 +64,7 @@
%strong= link_to "Web Hooks", help_web_hooks_path
%p Let GitLab notify you when new code has been pushed to your project.
- .span4
+ .col-md-4
.ui-box
.title
Admin documentation
diff --git a/app/views/help/permissions.html.haml b/app/views/help/permissions.html.haml
index df35f41fc90..15e3bf3a135 100644
--- a/app/views/help/permissions.html.haml
+++ b/app/views/help/permissions.html.haml
@@ -143,7 +143,7 @@
%td.permission-x &#10003;
%td.permission-x &#10003;
%tr
- %td Switch public mode
+ %td Switch visibility level
%td
%td
%td
diff --git a/app/views/help/public_access.html.haml b/app/views/help/public_access.html.haml
index c67402ee319..ba2df8c3553 100644
--- a/app/views/help/public_access.html.haml
+++ b/app/views/help/public_access.html.haml
@@ -1,15 +1,46 @@
= render layout: 'help/layout' do
%h3.page-title Public Access
- %p
- GitLab allows you to open selected projects to be accessed publicly.
- These projects will be cloneable
- %em without any
- authentication.
- Also they will be listed on the #{link_to "public access directory", public_root_path}.
+ %p.slead
+ GitLab allows you to open selected projects to be accessed
+ %strong publicly
+ or
+ %strong internally
+ \.
+ %br
+ Projects with either of these visibility levels will be listed in the #{link_to "public access directory", public_root_path}.
+ %br
+ Internal projects will only be available to authenticated users.
+ .clearfix
+ .dashboard-intro-icon
+ = public_icon
+ %h4
+ Public projects
+ %p
+ Public project can be cloned
+ %strong without any
+ authentication.
+ %br
+ It will also be listed on the #{link_to "public access directory", public_root_path}.
+ %br
+ %strong Any logged in user
+ will have #{link_to "Guest", help_permissions_path} permissions on the repository.
+
+ .clearfix
+ .dashboard-intro-icon
+ = internal_icon
+ %h4
+ Internal projects
+ %p
+ Internal project can be cloned by any logged in user.
+ %br
+ It will also be listed on the #{link_to "public access directory", public_root_path} for logged in users.
+ %br
+ Any logged in user will have #{link_to "Guest", help_permissions_path} permissions on the repository.
+
+ %h4 How to change project visibility
%ol
%li Go to your project dashboard
%li Click on the "Edit" tab
- %li Select "Public clone access"
-
+ %li Change "Visibility Level"
diff --git a/app/views/help/ssh.html.haml b/app/views/help/ssh.html.haml
index 72a21b89cab..773e490ef80 100644
--- a/app/views/help/ssh.html.haml
+++ b/app/views/help/ssh.html.haml
@@ -5,18 +5,13 @@
SSH key allows you to establish a secure connection between your computer and GitLab
%p.slead
- To generate a new SSH key just open your terminal and use code below.
+ To generate a new SSH key just open your terminal and use code below. Press enter to accept the defaults when generating the key.
%pre.dark
ssh-keygen -t rsa -C "#{current_user.email}"
- \# Creates a new ssh key using the provided email
- \# Generating public/private rsa key pair...
-
%p.slead
Next just use code below to dump your public key and add to GitLab SSH Keys
%pre.dark
cat ~/.ssh/id_rsa.pub
-
- \# ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC6eNtGpNGwstc....
diff --git a/app/views/help/web_hooks.html.haml b/app/views/help/web_hooks.html.haml
index 25865251de3..7bde7fcc3d0 100644
--- a/app/views/help/web_hooks.html.haml
+++ b/app/views/help/web_hooks.html.haml
@@ -1,12 +1,115 @@
= render layout: 'help/layout' do
- %h3.page-title Web hooks
+ %h3.page-title Project web hooks
+ %p.light
+ Project web hooks allow you to trigger url if new code is pushed or new issue is created
+ %hr
%p.slead
- Every GitLab project can trigger a web server whenever the repo is pushed to.
+ You can configure web hook to listen for specific events like pushes, issues, merge requests.
+ %br
+ GitLab will send POST request with data to web hook url.
%br
Web Hooks can be used to update an external issue tracker, trigger CI builds, update a backup mirror, or even deploy to your production server.
+ %hr
+
+ %h4 Push events
+ %p.light
+ Triggered when you push to the repository except pushing tags.
%br
- GitLab will send POST request with commits information on every push.
- %h5 Hooks request example:
- = render "projects/hooks/data_ex"
+ Request body:
+ = highlight_js do
+ :erb
+ {
+ "before": "95790bf891e76fee5e1747ab589903a6a1f80f22",
+ "after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
+ "ref": "refs/heads/master",
+ "user_id": 4,
+ "user_name": "John Smith",
+ "project_id": 15,
+ "repository": {
+ "name": "Diaspora",
+ "url": "git@localhost:diaspora.git",
+ "description": "",
+ "homepage": "http://localhost/diaspora",
+ },
+ "commits": [
+ {
+ "id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
+ "message": "Update Catalan translation to e38cb41.",
+ "timestamp": "2011-12-12T14:27:31+02:00",
+ "url": "http://localhost/diaspora/commits/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
+ "author": {
+ "name": "Jordi Mallach",
+ "email": "jordi@softcatala.org",
+ }
+ },
+ // ...
+ {
+ "id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
+ "message": "fixed readme",
+ "timestamp": "2012-01-03T23:36:29+02:00",
+ "url": "http://localhost/diaspora/commits/da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
+ "author": {
+ "name": "GitLab dev user",
+ "email": "gitlabdev@dv6700.(none)",
+ },
+ },
+ ],
+ "total_commits_count": 4,
+ };
+
+ %h4.prepend-top-20 Issues events
+ %p.light
+ Triggered when new issue created or existing issue was updated/closed/reopened.
+ %br
+ Request body:
+ = highlight_js do
+ :erb
+ {
+ "object_kind":"issue",
+ "object_attributes":{
+ "id":301,
+ "title":"New API: create/update/delete file",
+ "assignee_id":51,
+ "author_id":51,
+ "project_id":14,
+ "created_at":"2013-12-03T17:15:43Z",
+ "updated_at":"2013-12-03T17:15:43Z",
+ "position":0,
+ "branch_name":null,
+ "description":"Create new API for manipulations with repository",
+ "milestone_id":null,
+ "state":"opened",
+ "iid":23
+ }
+ }
+ %h4.prepend-top-20 Merge request events
+ %p.light
+ Triggered when new merge request created or existing merge request was updated/merged/closed.
+ %br
+ Request body:
+ = highlight_js do
+ :erb
+ {
+ "object_kind":"merge_request",
+ "object_attributes":{
+ "id":99,
+ "target_branch":"master",
+ "source_branch":"ms-viewport",
+ "source_project_id":14,
+ "author_id":51,
+ "assignee_id":6,
+ "title":"MS-Viewport",
+ "created_at":"2013-12-03T17:23:34Z",
+ "updated_at":"2013-12-03T17:23:34Z",
+ "st_commits":null,
+ "st_diffs":null,
+ "milestone_id":null,
+ "state":"opened",
+ "merge_status":"unchecked",
+ "target_project_id":14,
+ "iid":1,
+ "description":""
+ }
+ }
diff --git a/app/views/kaminari/gitlab/_paginator.html.haml b/app/views/kaminari/gitlab/_paginator.html.haml
index 6f9fb332261..4f7996e4996 100644
--- a/app/views/kaminari/gitlab/_paginator.html.haml
+++ b/app/views/kaminari/gitlab/_paginator.html.haml
@@ -6,8 +6,8 @@
-# remote: data-remote
-# paginator: the paginator that renders the pagination tags inside
= paginator.render do
- %div.pagination
- %ul
+ %div.gl-pagination
+ %ul.pagination
= prev_page_tag unless current_page.first?
- each_page do |page|
- if page.left_outer? || page.right_outer? || page.inside_window?
diff --git a/app/views/layouts/_broadcast.html.haml b/app/views/layouts/_broadcast.html.haml
new file mode 100644
index 00000000000..5794e3de338
--- /dev/null
+++ b/app/views/layouts/_broadcast.html.haml
@@ -0,0 +1,4 @@
+- if broadcast_message.present?
+ .broadcast-message{ style: broadcast_styling(broadcast_message) }
+ %i.icon-bullhorn
+ = broadcast_message.message
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
index 0775abea3dd..68146e661eb 100644
--- a/app/views/layouts/_head.html.haml
+++ b/app/views/layouts/_head.html.haml
@@ -8,6 +8,8 @@
= javascript_include_tag "application"
= csrf_meta_tags
= include_gon
+ :erb
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
= render 'layouts/google_analytics' if extra_config.has_key?('google_analytics_id')
diff --git a/app/views/layouts/_head_panel.html.haml b/app/views/layouts/_head_panel.html.haml
index 80c42fe6387..ee97f865371 100644
--- a/app/views/layouts/_head_panel.html.haml
+++ b/app/views/layouts/_head_panel.html.haml
@@ -7,34 +7,36 @@
%h1 GITLAB
%span.separator
%h1.project_name= title
- %ul.nav
- %li
- %a
- %div.hide.turbolink-spinner
- %i.icon-refresh.icon-spin
- Loading...
- %li
- = render "layouts/search"
- %li
- = link_to public_root_path, title: "Public area", class: 'has_bottom_tooltip', 'data-original-title' => 'Public area' do
- %i.icon-globe
- %li
- = link_to user_snippets_path(current_user), title: "My snippets", class: 'has_bottom_tooltip', 'data-original-title' => 'Public area' do
- %i.icon-paste
- - if current_user.is_admin?
+
+ .navbar-collapse.collapse
+ %ul.nav.navbar-nav
%li
- = link_to admin_root_path, title: "Admin area", class: 'has_bottom_tooltip', 'data-original-title' => 'Admin area' do
- %i.icon-cogs
- - if current_user.can_create_project?
+ %a
+ %div.hide.turbolink-spinner
+ %i.icon-refresh.icon-spin
+ Loading...
+ %li.hidden-sm
+ = render "layouts/search"
%li
- = link_to new_project_path, title: "New project", class: 'has_bottom_tooltip', 'data-original-title' => 'New project' do
- %i.icon-plus
- %li
- = link_to profile_path, title: "Profile settings", class: 'has_bottom_tooltip', 'data-original-title' => 'Profile settings"' do
- %i.icon-user
- %li
- = link_to destroy_user_session_path, class: "logout", method: :delete, title: "Logout", class: 'has_bottom_tooltip', 'data-original-title' => 'Logout' do
- %i.icon-signout
- %li
- = link_to current_user, class: "profile-pic", id: 'profile-pic' do
- = image_tag avatar_icon(current_user.email, 26), alt: 'User activity'
+ = link_to public_root_path, title: "Public area", class: 'has_bottom_tooltip', 'data-original-title' => 'Public area' do
+ %i.icon-globe
+ %li
+ = link_to user_snippets_path(current_user), title: "My snippets", class: 'has_bottom_tooltip', 'data-original-title' => 'Public area' do
+ %i.icon-paste
+ - if current_user.is_admin?
+ %li
+ = link_to admin_root_path, title: "Admin area", class: 'has_bottom_tooltip', 'data-original-title' => 'Admin area' do
+ %i.icon-cogs
+ - if current_user.can_create_project?
+ %li
+ = link_to new_project_path, title: "New project", class: 'has_bottom_tooltip', 'data-original-title' => 'New project' do
+ %i.icon-plus
+ %li
+ = link_to profile_path, title: "Profile settings", class: 'has_bottom_tooltip', 'data-original-title' => 'Profile settings"' do
+ %i.icon-user
+ %li
+ = link_to destroy_user_session_path, class: "logout", method: :delete, title: "Logout", class: 'has_bottom_tooltip', 'data-original-title' => 'Logout' do
+ %i.icon-signout
+ %li
+ = link_to current_user, class: "profile-pic", id: 'profile-pic' do
+ = image_tag avatar_icon(current_user.email, 26), alt: 'User activity'
diff --git a/app/views/layouts/_init_auto_complete.html.haml b/app/views/layouts/_init_auto_complete.html.haml
index 7d16a75af6e..6a20dedf62f 100644
--- a/app/views/layouts/_init_auto_complete.html.haml
+++ b/app/views/layouts/_init_auto_complete.html.haml
@@ -1,4 +1,4 @@
:javascript
GitLab.GfmAutoComplete.dataSource = "#{autocomplete_sources_project_path(@project)}"
- GitLab.GfmAutoComplete.Emoji.assetBase = '#{image_path("emoji")}'
+ GitLab.GfmAutoComplete.Emoji.assetBase = "#{Gitlab.config.gitlab.relative_url_root + '/assets/emoji'}"
GitLab.GfmAutoComplete.setup();
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
index 792fe5e4a28..92edc718235 100644
--- a/app/views/layouts/application.html.haml
+++ b/app/views/layouts/application.html.haml
@@ -2,6 +2,7 @@
%html{ lang: "en"}
= render "layouts/head", title: "Dashboard"
%body{class: "#{app_theme} application", :'data-page' => body_data_page }
+ = render "layouts/broadcast"
= render "layouts/head_panel", title: "Dashboard"
= render "layouts/flash"
%nav.main-nav
diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml
index c4729836faa..c5041dd71b8 100644
--- a/app/views/layouts/devise.html.haml
+++ b/app/views/layouts/devise.html.haml
@@ -11,5 +11,7 @@
GitLab is open source software to collaborate on code.
%br
#{link_to "Sign in", new_user_session_path} or browse for #{link_to "public projects", public_projects_path}.
- %hr
+ %hr
+ .container
+ .content
= yield
diff --git a/app/views/layouts/group.html.haml b/app/views/layouts/group.html.haml
index 0e955d59ff8..b546a9fa84f 100644
--- a/app/views/layouts/group.html.haml
+++ b/app/views/layouts/group.html.haml
@@ -1,7 +1,8 @@
!!! 5
%html{ lang: "en"}
- = render "layouts/head", title: "#{@group.name}"
+ = render "layouts/head", title: group_head_title
%body{class: "#{app_theme} application", :'data-page' => body_data_page}
+ = render "layouts/broadcast"
= render "layouts/head_panel", title: "group: #{@group.name}"
= render "layouts/flash"
%nav.main-nav
diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml
index 73946f99889..48c569f8684 100644
--- a/app/views/layouts/nav/_admin.html.haml
+++ b/app/views/layouts/nav/_admin.html.haml
@@ -10,6 +10,8 @@
= link_to "Users", admin_users_path
= nav_link(controller: :logs) do
= link_to "Logs", admin_logs_path
+ = nav_link(controller: :broadcast_messages) do
+ = link_to "Messages", admin_broadcast_messages_path
= nav_link(controller: :hooks) do
= link_to "Hooks", admin_hooks_path
= nav_link(controller: :background_jobs) do
diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml
index cae24c3170d..12fd49e609f 100644
--- a/app/views/layouts/nav/_dashboard.html.haml
+++ b/app/views/layouts/nav/_dashboard.html.haml
@@ -12,7 +12,7 @@
= nav_link(path: 'dashboard#merge_requests') do
= link_to merge_requests_dashboard_path do
Merge Requests
- %span.count= current_user.cared_merge_requests.opened.count
+ %span.count= current_user.assigned_merge_requests.opened.count
= nav_link(controller: :help) do
= link_to "Help", help_path
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index e9d535f6972..1f70cf17987 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -4,7 +4,7 @@
%i.icon-home
- if project_nav_tab? :files
- = nav_link(controller: %w(tree blob blame edit_tree)) do
+ = nav_link(controller: %w(tree blob blame edit_tree new_tree)) do
= link_to 'Files', project_tree_path(@project, @ref || @repository.root_ref)
- if project_nav_tab? :commits
diff --git a/app/views/layouts/profile.html.haml b/app/views/layouts/profile.html.haml
index 30a0532bc2b..72849639571 100644
--- a/app/views/layouts/profile.html.haml
+++ b/app/views/layouts/profile.html.haml
@@ -2,6 +2,7 @@
%html{ lang: "en"}
= render "layouts/head", title: "Profile"
%body{class: "#{app_theme} profile", :'data-page' => body_data_page}
+ = render "layouts/broadcast"
= render "layouts/head_panel", title: "Profile"
= render "layouts/flash"
%nav.main-nav
diff --git a/app/views/layouts/project_settings.html.haml b/app/views/layouts/project_settings.html.haml
index ea739da73d8..a55f043f2cf 100644
--- a/app/views/layouts/project_settings.html.haml
+++ b/app/views/layouts/project_settings.html.haml
@@ -2,6 +2,7 @@
%html{ lang: "en"}
= render "layouts/head", title: @project.name_with_namespace
%body{class: "#{app_theme} project", :'data-page' => body_data_page, :'data-project-id' => @project.id }
+ = render "layouts/broadcast"
= render "layouts/head_panel", title: project_title(@project)
= render "layouts/init_auto_complete"
= render "layouts/flash"
@@ -14,7 +15,7 @@
.container
.content
.row
- .span2
+ .col-md-2
= render "projects/settings_nav"
- .span10
+ .col-md-10
= yield
diff --git a/app/views/layouts/projects.html.haml b/app/views/layouts/projects.html.haml
index 6d8bf9b710b..55214c6a5c9 100644
--- a/app/views/layouts/projects.html.haml
+++ b/app/views/layouts/projects.html.haml
@@ -1,7 +1,8 @@
!!! 5
%html{ lang: "en"}
- = render "layouts/head", title: @project.name_with_namespace
+ = render "layouts/head", title: project_head_title
%body{class: "#{app_theme} project", :'data-page' => body_data_page, :'data-project-id' => @project.id }
+ = render "layouts/broadcast"
= render "layouts/head_panel", title: project_title(@project)
= render "layouts/init_auto_complete"
= render "layouts/flash"
diff --git a/app/views/layouts/public.html.haml b/app/views/layouts/public.html.haml
index f922dcc4203..8f490f61a9c 100644
--- a/app/views/layouts/public.html.haml
+++ b/app/views/layouts/public.html.haml
@@ -1,7 +1,7 @@
!!! 5
%html{ lang: "en"}
= render "layouts/head", title: "Public Projects"
- %body{class: "ui_mars application", :'data-page' => body_data_page}
+ %body{class: "#{app_theme} application", :'data-page' => body_data_page}
- if current_user
= render "layouts/head_panel", title: "Public Projects"
- else
diff --git a/app/views/layouts/public_projects.html.haml b/app/views/layouts/public_projects.html.haml
index cfe6a63055a..1e8814134f5 100644
--- a/app/views/layouts/public_projects.html.haml
+++ b/app/views/layouts/public_projects.html.haml
@@ -1,7 +1,7 @@
!!! 5
%html{ lang: "en"}
= render "layouts/head", title: @project.name_with_namespace
- %body{class: "ui_mars application", :'data-page' => body_data_page}
+ %body{class: "#{app_theme} application", :'data-page' => body_data_page}
= render "layouts/public_head_panel"
%nav.main-nav
.container= render 'layouts/nav/project'
diff --git a/app/views/notify/_note_message.html.haml b/app/views/notify/_note_message.html.haml
index 88c4df55b7f..9e329af2d47 100644
--- a/app/views/notify/_note_message.html.haml
+++ b/app/views/notify/_note_message.html.haml
@@ -1,6 +1,6 @@
%p
%strong #{@note.author_name}
- left next message:
+ wrote:
%cite{style: 'color: #666'}
= markdown(@note.note)
diff --git a/app/views/notify/new_merge_request_email.html.haml b/app/views/notify/new_merge_request_email.html.haml
index 6b2782ebb0b..321f9418ded 100644
--- a/app/views/notify/new_merge_request_email.html.haml
+++ b/app/views/notify/new_merge_request_email.html.haml
@@ -1,5 +1,5 @@
%p
- = "New Merge Request !#{@merge_request.iid}"
+ = "New Merge Request ##{@merge_request.iid}"
%p
= link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.target_project, @merge_request)
%p
diff --git a/app/views/notify/new_merge_request_email.text.erb b/app/views/notify/new_merge_request_email.text.erb
index 2d27350486e..16be4bb619f 100644
--- a/app/views/notify/new_merge_request_email.text.erb
+++ b/app/views/notify/new_merge_request_email.text.erb
@@ -1,4 +1,4 @@
-New Merge Request <%= @merge_request.iid %>
+New Merge Request #<%= @merge_request.iid %>
<%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request)) %>
diff --git a/app/views/notify/reassigned_merge_request_email.html.haml b/app/views/notify/reassigned_merge_request_email.html.haml
index 3a615f86e69..d2d82d36c48 100644
--- a/app/views/notify/reassigned_merge_request_email.html.haml
+++ b/app/views/notify/reassigned_merge_request_email.html.haml
@@ -1,5 +1,5 @@
%p
- = "Reassigned Merge Request !#{@merge_request.iid}"
+ = "Reassigned Merge Request ##{@merge_request.iid}"
= link_to_gfm truncate(@merge_request.title, length: 30), project_merge_request_url(@merge_request.target_project, @merge_request)
%p
Assignee changed
diff --git a/app/views/notify/reassigned_merge_request_email.text.erb b/app/views/notify/reassigned_merge_request_email.text.erb
index eecf055ff6f..87a7847e06d 100644
--- a/app/views/notify/reassigned_merge_request_email.text.erb
+++ b/app/views/notify/reassigned_merge_request_email.text.erb
@@ -1,4 +1,4 @@
-Reassigned Merge Request <%= @merge_request.iid %>
+Reassigned Merge Request #<%= @merge_request.iid %>
<%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request)) %>
diff --git a/app/views/notify/repository_push_email.html.haml b/app/views/notify/repository_push_email.html.haml
new file mode 100644
index 00000000000..d0b30c08338
--- /dev/null
+++ b/app/views/notify/repository_push_email.html.haml
@@ -0,0 +1,23 @@
+%h3 #{@author.name} pushed to #{@branch} at #{@project.name_with_namespace}
+
+%h4 Commits:
+
+%ul
+ - @commits.each do |commit|
+ %li
+ #{commit.short_id} - #{commit.title}
+
+%h4 Diff:
+- @diffs.each do |diff|
+ %li
+ %strong
+ - if diff.old_path == diff.new_path
+ = diff.new_path
+ - elsif diff.new_path && diff.old_path
+ #{diff.old_path} &rarr; #{diff.new_path}
+ - else
+ = diff.new_path || diff.old_path
+ %hr
+ %pre
+ = diff.diff
+ %br
diff --git a/app/views/notify/repository_push_email.text.haml b/app/views/notify/repository_push_email.text.haml
new file mode 100644
index 00000000000..6718ca68359
--- /dev/null
+++ b/app/views/notify/repository_push_email.text.haml
@@ -0,0 +1,20 @@
+#{@author.name} pushed to #{@branch} at #{@project.name_with_namespace}
+
+\
+Commits:
+- @commits.each do |commit|
+ #{commit.short_id} - #{truncate(commit.title, length: 40)}
+\
+\
+Diff:
+- @diffs.each do |diff|
+ \
+ \=====================================
+ - if diff.old_path == diff.new_path
+ = diff.new_path
+ - elsif diff.new_path && diff.old_path
+ #{diff.old_path} &rarr; #{diff.new_path}
+ - else
+ = diff.new_path || diff.old_path
+ \=====================================
+ = diff.diff
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
index b76339027af..3220736b078 100644
--- a/app/views/profiles/accounts/show.html.haml
+++ b/app/views/profiles/accounts/show.html.haml
@@ -23,8 +23,9 @@
%p.cgray
- if current_user.private_token
- = text_field_tag "token", current_user.private_token, class: "input-xlarge input-xpadding pull-left"
- = f.submit 'Reset', confirm: "Are you sure?", class: "btn btn-primary btn-build-token prepend-left-10"
+ = text_field_tag "token", current_user.private_token, class: "form-control"
+ %div
+ = f.submit 'Reset', data: { confirm: "Are you sure?" }, class: "btn btn-primary btn-build-token"
- else
%span You don`t have one yet. Click generate to fix it.
= f.submit 'Generate', class: "btn success btn-build-token"
@@ -47,7 +48,7 @@
%p
Changing your username will change path to all personal projects!
%div
- = f.text_field :username, required: true, class: 'input-xlarge input-xpadding'
+ = f.text_field :username, required: true, class: 'form-control'
&nbsp;
%span.loading-gif.hide= image_tag "ajax_loader.gif"
%p.light
@@ -70,4 +71,4 @@
%li
The following groups will be abandoned. You should transfer or remove them:
%strong #{current_user.solo_owned_groups.map(&:name).join(', ')}
- = link_to 'Delete account', user_registration_path, confirm: "REMOVE #{current_user.name}? Are you sure?", method: :delete, class: "btn btn-remove"
+ = link_to 'Delete account', user_registration_path, data: { confirm: "REMOVE #{current_user.name}? Are you sure?" }, method: :delete, class: "btn btn-remove"
diff --git a/app/views/profiles/groups/index.html.haml b/app/views/profiles/groups/index.html.haml
index 6fc27be5db4..1d24e636bf4 100644
--- a/app/views/profiles/groups/index.html.haml
+++ b/app/views/profiles/groups/index.html.haml
@@ -22,7 +22,7 @@
%i.icon-cogs
Settings
- = link_to leave_profile_group_path(group), confirm: "Are you sure you want to leave #{group.name} group?", method: :delete, class: "btn-small btn grouped", title: 'Remove user from group' do
+ = link_to leave_profile_group_path(group), data: { confirm: "Are you sure you want to leave #{group.name} group?"}, method: :delete, class: "btn-small btn grouped", title: 'Remove user from group' do
%i.icon-signout
Leave
diff --git a/app/views/profiles/keys/_form.html.haml b/app/views/profiles/keys/_form.html.haml
index 158979c0ee5..f905417f0e2 100644
--- a/app/views/profiles/keys/_form.html.haml
+++ b/app/views/profiles/keys/_form.html.haml
@@ -1,20 +1,18 @@
%div
- = form_for [:profile, @key] do |f|
+ = form_for [:profile, @key], html: { class: 'form-horizontal' } do |f|
- if @key.errors.any?
- .alert.alert-error
+ .alert.alert-danger
%ul
- @key.errors.full_messages.each do |msg|
%li= msg
- .control-group
- = f.label :title
- .controls= f.text_field :title, class: "input-xlarge"
- .control-group
- = f.label :key
- .controls
- %p.light
- Paste your public key here. Read more about how to generate a key on #{link_to "the SSH help page", help_ssh_path}.
- = f.text_area :key, class: "input-xxlarge thin_area"
+ .form-group
+ = f.label :title, class: 'control-label'
+ .col-sm-10= f.text_field :title, class: "form-control"
+ .form-group
+ = f.label :key, class: 'control-label'
+ .col-sm-10
+ = f.text_area :key, class: "form-control", rows: 8
.form-actions
diff --git a/app/views/profiles/keys/_key.html.haml b/app/views/profiles/keys/_key.html.haml
index d0a3fe32c35..81411a7565e 100644
--- a/app/views/profiles/keys/_key.html.haml
+++ b/app/views/profiles/keys/_key.html.haml
@@ -4,8 +4,6 @@
%span
(#{key.fingerprint})
%span.cgray
- added
- = time_ago_in_words(key.created_at)
- ago
+ added #{time_ago_with_tooltip(key.created_at)}
- = link_to 'Remove', profile_key_path(key), confirm: 'Are you sure?', method: :delete, class: "btn btn-small btn-remove delete-key pull-right"
+ = link_to 'Remove', profile_key_path(key), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-small btn-remove delete-key pull-right"
diff --git a/app/views/profiles/keys/new.html.haml b/app/views/profiles/keys/new.html.haml
index f1b8fe08d5c..3d5a947948d 100644
--- a/app/views/profiles/keys/new.html.haml
+++ b/app/views/profiles/keys/new.html.haml
@@ -1,4 +1,6 @@
%h3.page-title Add an SSH Key
+%p.light
+ Paste your public key here. Read more about how to generate a key on #{link_to "the SSH help page", help_ssh_path}.
%hr
= render 'form'
diff --git a/app/views/profiles/keys/show.html.haml b/app/views/profiles/keys/show.html.haml
index 2852c338af2..b6724a7cb5d 100644
--- a/app/views/profiles/keys/show.html.haml
+++ b/app/views/profiles/keys/show.html.haml
@@ -1,5 +1,5 @@
.row
- .span4
+ .col-md-4
.ui-box
.title
SSH Key
@@ -11,7 +11,7 @@
%span.light Created on:
%strong= @key.created_at.stamp("Aug 21, 2011")
- .span8
+ .col-md-8
%p
%span.light Fingerprint:
%strong= @key.fingerprint
@@ -19,4 +19,4 @@
= @key.key
.pull-right
- = link_to 'Remove', profile_key_path(@key), confirm: 'Are you sure?', method: :delete, class: "btn btn-remove delete-key"
+ = link_to 'Remove', profile_key_path(@key), data: {confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove delete-key"
diff --git a/app/views/profiles/notifications/_settings.html.haml b/app/views/profiles/notifications/_settings.html.haml
index 5f62c8099d0..ab64d8303a4 100644
--- a/app/views/profiles/notifications/_settings.html.haml
+++ b/app/views/profiles/notifications/_settings.html.haml
@@ -1,6 +1,6 @@
%li
.row
- .span4
+ .col-md-4
%span
= notification_icon(notification)
@@ -8,24 +8,24 @@
= link_to membership.group.name, membership.group
- else
= link_to_project(membership.project)
- .span7
+ .col-md-7
= form_tag profile_notifications_path, method: :put, remote: true, class: 'update-notifications' do
= hidden_field_tag :notification_type, type, id: dom_id(membership, 'notification_type')
= hidden_field_tag :notification_id, membership.id, id: dom_id(membership, 'notification_id')
- = label_tag do
+ = label_tag nil, class: 'radio-inline' do
= radio_button_tag :notification_level, Notification::N_GLOBAL, notification.global?, id: dom_id(membership, 'notification_level'), class: 'trigger-submit'
%span Use global setting
- = label_tag do
+ = label_tag nil, class: 'radio-inline' do
= radio_button_tag :notification_level, Notification::N_DISABLED, notification.disabled?, id: dom_id(membership, 'notification_level'), class: 'trigger-submit'
%span Disabled
- = label_tag do
+ = label_tag nil, class: 'radio-inline' do
= radio_button_tag :notification_level, Notification::N_PARTICIPATING, notification.participating?, id: dom_id(membership, 'notification_level'), class: 'trigger-submit'
%span Participating
- = label_tag do
+ = label_tag nil, class: 'radio-inline' do
= radio_button_tag :notification_level, Notification::N_WATCH, notification.watch?, id: dom_id(membership, 'notification_level'), class: 'trigger-submit'
%span Watch
diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml
index 8353b2f5f23..ecec8f7b384 100644
--- a/app/views/profiles/notifications/show.html.haml
+++ b/app/views/profiles/notifications/show.html.haml
@@ -18,23 +18,23 @@
&ndash; You will receive all notifications from projects in which you participate
.row
- .span4
+ .col-md-4
%h4
= notification_icon(@notification)
Global setting
- .span7
+ .col-md-7
= form_tag profile_notifications_path, method: :put, remote: true, class: 'update-notifications' do
= hidden_field_tag :notification_type, 'global'
- = label_tag do
+ = label_tag nil, class: 'radio-inline' do
= radio_button_tag :notification_level, Notification::N_DISABLED, @notification.disabled?, class: 'trigger-submit'
%span Disabled
- = label_tag do
+ = label_tag nil, class: 'radio-inline' do
= radio_button_tag :notification_level, Notification::N_PARTICIPATING, @notification.participating?, class: 'trigger-submit'
%span Participating
- = label_tag do
+ = label_tag nil, class: 'radio-inline' do
= radio_button_tag :notification_level, Notification::N_WATCH, @notification.watch?, class: 'trigger-submit'
%span Watch
diff --git a/app/views/profiles/passwords/edit.html.haml b/app/views/profiles/passwords/edit.html.haml
index a5fa6e7f186..fd3d0ac09d4 100644
--- a/app/views/profiles/passwords/edit.html.haml
+++ b/app/views/profiles/passwords/edit.html.haml
@@ -3,30 +3,30 @@
Change your password or recover your current one.
%hr
.update-password
- = form_for @user, url: profile_password_path, method: :put do |f|
+ = form_for @user, url: profile_password_path, method: :put, html: { class: 'form-horizontal' } do |f|
%div
%p.slead
You must provide current password in order to change it.
%br
After a successful password update you will be redirected to login page where you should login with your new password
-if @user.errors.any?
- .alert.alert-error
+ .alert.alert-danger
%ul
- @user.errors.full_messages.each do |msg|
%li= msg
- .control-group
- = f.label :current_password
- .controls
- = f.password_field :current_password, required: true
+ .form-group
+ = f.label :current_password, class: 'control-label'
+ .col-sm-10
+ = f.password_field :current_password, required: true, class: 'form-control'
%div
= link_to "Forgot your password?", reset_profile_password_path, method: :put
- .control-group
- = f.label :password, 'New password'
- .controls= f.password_field :password, required: true
- .control-group
- = f.label :password_confirmation
- .controls
- = f.password_field :password_confirmation, required: true
+ .form-group
+ = f.label :password, 'New password', class: 'control-label'
+ .col-sm-10= f.password_field :password, required: true, class: 'form-control'
+ .form-group
+ = f.label :password_confirmation, class: 'control-label'
+ .col-sm-10
+ = f.password_field :password_confirmation, required: true, class: 'form-control'
.form-actions
= f.submit 'Save password', class: "btn btn-save"
diff --git a/app/views/profiles/passwords/new.html.haml b/app/views/profiles/passwords/new.html.haml
index a4e7dadd16a..f333879cf6c 100644
--- a/app/views/profiles/passwords/new.html.haml
+++ b/app/views/profiles/passwords/new.html.haml
@@ -5,18 +5,18 @@
%br
After successful password update you will be redirected to login screen
-if @user.errors.any?
- .alert.alert-error
+ .alert.alert-danger
%ul
- @user.errors.full_messages.each do |msg|
%li= msg
- .control-group
+ .form-group
= f.label :password
- .controls= f.password_field :password, required: true
- .control-group
+ .col-sm-10= f.password_field :password, required: true
+ .form-group
= f.label :password_confirmation
- .controls
+ .col-sm-10
= f.password_field :password_confirmation, required: true
- .control-group
- .controls
+ .form-group
+ .col-sm-10
= f.submit 'Set new password', class: "btn btn-create"
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index d382d2d70c4..6ae1032aebf 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -8,62 +8,76 @@
-= form_for @user, url: profile_path, method: :put, html: { multipart: true, class: "edit_user form-horizontal" } do |f|
+= form_for @user, url: profile_path, method: :put, html: { multipart: true, class: "edit_user form-horizontal" }, authenticity_token: true do |f|
-if @user.errors.any?
- %div.alert.alert-error
+ %div.alert.alert-danger
%ul
- @user.errors.full_messages.each do |msg|
%li= msg
.row
- .span7
- .control-group
+ .col-md-7
+ .form-group
= f.label :name, class: "control-label"
- .controls
- = f.text_field :name, class: "input-xlarge", required: true
+ .col-sm-10
+ = f.text_field :name, class: "form-control", required: true
%span.help-block Enter your name, so people you know can recognize you.
- .control-group
+
+ .form-group
= f.label :email, class: "control-label"
- .controls
- = f.text_field :email, class: "input-xlarge", required: true
- - if @user.unconfirmed_email.present?
- %span.help-block
- We sent confirmation email to
- %strong #{@user.unconfirmed_email}
+ .col-sm-10
+ - if @user.ldap_user?
+ = f.text_field :email, class: "form-control", required: true, readonly: true
+ %span.help-block.light
+ Email is read-only for LDAP user
- else
- %span.help-block We also use email for avatar detection if no avatar is uploaded.
- .control-group
+ = f.text_field :email, class: "form-control", required: true
+ - if @user.unconfirmed_email.present?
+ %span.help-block
+ We sent confirmation email to
+ %strong #{@user.unconfirmed_email}
+ - else
+ %span.help-block We also use email for avatar detection if no avatar is uploaded.
+ .form-group
= f.label :skype, class: "control-label"
- .controls= f.text_field :skype, class: "input-xlarge"
- .control-group
+ .col-sm-10= f.text_field :skype, class: "form-control"
+ .form-group
= f.label :linkedin, class: "control-label"
- .controls= f.text_field :linkedin, class: "input-xlarge"
- .control-group
+ .col-sm-10= f.text_field :linkedin, class: "form-control"
+ .form-group
= f.label :twitter, class: "control-label"
- .controls= f.text_field :twitter, class: "input-xlarge"
- .control-group
+ .col-sm-10= f.text_field :twitter, class: "form-control"
+ .form-group
= f.label :bio, class: "control-label"
- .controls
- = f.text_area :bio, rows: 6, class: "input-xlarge", maxlength: 250
+ .col-sm-10
+ = f.text_area :bio, rows: 6, class: "form-control", maxlength: 250
%span.help-block Tell us about yourself in fewer than 250 characters.
- .span5.pull-right
+ .col-md-5
.light-well
= image_tag avatar_icon(@user.email, 160), alt: '', class: 'avatar s160'
.clearfix
.profile-avatar-form-option
%p.light
- You can upload an avatar here
- %br
- or change it at #{link_to "gravatar.com", "http://gravatar.com"}
+ - if @user.avatar?
+ You can change your avatar here
+ %br
+ or remove the current avatar to revert to #{link_to "gravatar.com", "http://gravatar.com"}
+ - else
+ You can upload an avatar here
+ %br
+ or change it at #{link_to "gravatar.com", "http://gravatar.com"}
%hr
%a.choose-btn.btn.btn-small.js-choose-user-avatar-button
%i.icon-paper-clip
%span Choose File ...
&nbsp;
%span.file_name.js-avatar-filename File name...
- = f.file_field :avatar, class: "js-user-avatar-input hide"
- %span.help-block The maximum file size allowed is 100KB.
+ = f.file_field :avatar, class: "js-user-avatar-input hidden"
+ .light The maximum file size allowed is 100KB.
+ - if @user.avatar?
+ %hr
+ = link_to 'Remove avatar', profile_avatar_path, data: { confirm: "Avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-small remove-avatar"
.form-actions
= f.submit 'Save changes', class: "btn btn-save"
diff --git a/app/views/projects/_dropdown.html.haml b/app/views/projects/_dropdown.html.haml
index e646d04282e..e283bd2bf1d 100644
--- a/app/views/projects/_dropdown.html.haml
+++ b/app/views/projects/_dropdown.html.haml
@@ -6,15 +6,19 @@
- if @project.issues_enabled && can?(current_user, :write_issue, @project)
%li
= link_to url_for_new_issue, title: "New Issue" do
- Issue
+ New issue
- if @project.merge_requests_enabled && can?(current_user, :write_merge_request, @project)
%li
= link_to new_project_merge_request_path(@project), title: "New Merge Request" do
- Merge Request
+ New merge request
- if @project.snippets_enabled && can?(current_user, :write_snippet, @project)
%li
= link_to new_project_snippet_path(@project), title: "New Snippet" do
- Snippet
+ New snippet
+ - if can?(current_user, :admin_team_member, @project)
+ %li
+ = link_to new_project_team_member_path(@project), title: "New project member" do
+ New project member
- if can? current_user, :push_code, @project
%li.divider
%li
@@ -26,9 +30,4 @@
%i.icon-tag
Git tag
- - if can?(current_user, :admin_team_member, @project)
- %li.divider
- %li
- = link_to new_project_team_member_path(@project), title: "New project member" do
- Project member
diff --git a/app/views/projects/_errors.html.haml b/app/views/projects/_errors.html.haml
index bb9759353a3..7c8bb33ed7e 100644
--- a/app/views/projects/_errors.html.haml
+++ b/app/views/projects/_errors.html.haml
@@ -1,4 +1,4 @@
- if @project.errors.any?
- .alert.alert-error
+ .alert.alert-danger
%button{ type: "button", class: "close", "data-dismiss" => "alert"} &times;
= @project.errors.full_messages.first
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
new file mode 100644
index 00000000000..6ac56a192ef
--- /dev/null
+++ b/app/views/projects/_home_panel.html.haml
@@ -0,0 +1,30 @@
+- empty_repo = @project.empty_repo?
+.project-home-panel{:class => ("empty-project" if empty_repo)}
+ .visibility-level-label.has_tooltip{'data-title' => "#{visibility_level_label(@project.visibility_level)} project" }
+ = visibility_level_icon(@project.visibility_level)
+ .row
+ .col-sm-6
+ %h4.project-home-title
+ = @project.name_with_namespace
+
+ .col-sm-6
+ - unless empty_repo
+ .project-home-dropdown
+ = render "dropdown"
+ .form-horizontal
+ = render "shared/clone_panel"
+
+ .project-home-extra.clearfix.row
+ .project-home-desc.col-md-8
+ - if @project.description.present?
+ = @project.description
+ - if can?(current_user, :admin_project, @project)
+ &ndash;
+ %strong= link_to 'Edit', edit_project_path
+
+ - unless empty_repo
+ .project-home-links
+ = link_to pluralize(@repository.round_commit_count, 'commit'), project_commits_path(@project, @ref || @repository.root_ref)
+ = link_to pluralize(@repository.branch_names.count, 'branch'), project_branches_path(@project)
+ = link_to pluralize(@repository.tag_names.count, 'tag'), project_tags_path(@project)
+ %span.light.prepend-left-20= repository_size
diff --git a/app/views/projects/_visibility_level.html.haml b/app/views/projects/_visibility_level.html.haml
new file mode 100644
index 00000000000..b708fb771c5
--- /dev/null
+++ b/app/views/projects/_visibility_level.html.haml
@@ -0,0 +1,27 @@
+.form-group.project-visibility-level-holder
+ = f.label :visibility_level, class: 'control-label' do
+ Visibility Level
+ = link_to "(?)", help_public_access_path
+ .col-sm-10
+ - if can_change_visibility_level
+ - Gitlab::VisibilityLevel.values.each do |level|
+ .append-bottom-10
+ - restricted = restricted_visibility_levels.include?(level)
+ = f.radio_button :visibility_level, level, checked: (visibility_level == level), disabled: restricted
+ %span.descr{:class => ("restricted" if restricted)}
+ = label :project_visibility_level, level do
+ = visibility_level_icon(level)
+ %strong
+ = visibility_level_label(level)
+ .light.prepend-left-20= visibility_level_description(level)
+ - unless restricted_visibility_levels.empty?
+ .col-sm-10
+ %span.info
+ Some visibility level settings have been restricted by the administrator.
+ - else
+ .col-sm-10
+ %span.info
+ = visibility_level_icon(visibility_level)
+ %strong
+ = visibility_level_label(visibility_level)
+ .light= visibility_level_description(visibility_level)
diff --git a/app/views/projects/blob/_actions.html.haml b/app/views/projects/blob/_actions.html.haml
index 5641c528a4f..2f82bfe52f3 100644
--- a/app/views/projects/blob/_actions.html.haml
+++ b/app/views/projects/blob/_actions.html.haml
@@ -1,7 +1,10 @@
.btn-group.tree-btn-group
-# only show edit link for text files
- if @blob.text?
- = link_to "edit", project_edit_tree_path(@project, @id), class: "btn btn-small", disabled: !allowed_tree_edit?
+ - if allowed_tree_edit?
+ = link_to "edit", project_edit_tree_path(@project, @id), class: "btn btn-small"
+ - else
+ %span.btn.btn-small.disabled edit
= link_to "raw", project_raw_path(@project, @id), class: "btn btn-small", target: "_blank"
-# only show normal/blame view links for text files
- if @blob.text?
@@ -10,3 +13,7 @@
- else
= link_to "blame", project_blame_path(@project, @id), class: "btn btn-small" unless @blob.empty?
= link_to "history", project_commits_path(@project, @id), class: "btn btn-small"
+
+ - if allowed_tree_edit?
+ = link_to '#modal-remove-blob', class: "remove-blob btn btn-small btn-remove", "data-toggle" => "modal" do
+ remove
diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml
index 6524aaeef4c..32ea967105a 100644
--- a/app/views/projects/blob/_blob.html.haml
+++ b/app/views/projects/blob/_blob.html.haml
@@ -4,7 +4,6 @@
= link_to project_tree_path(@project, @ref) do
= @project.path
- tree_breadcrumbs(@tree, 6) do |title, path|
- \/
%li
- if path
- if path.end_with?(@path)
diff --git a/app/views/projects/blob/_remove.html.haml b/app/views/projects/blob/_remove.html.haml
new file mode 100644
index 00000000000..6384703671a
--- /dev/null
+++ b/app/views/projects/blob/_remove.html.haml
@@ -0,0 +1,22 @@
+#modal-remove-blob.modal.hide
+ .modal-dialog
+ .modal-content
+ .modal-header
+ %a.close{href: "#", "data-dismiss" => "modal"} ×
+ %h3.page-title Remove #{@blob.name}
+ %p.light
+ From branch
+ %strong= @ref
+
+ .modal-body
+ = form_tag project_blob_path(@project, @id), method: :delete, class: 'form-horizontal' do
+ .form-group.commit_message-group
+ = label_tag 'commit_message', class: "control-label" do
+ Commit message
+ .col-sm-10
+ = text_area_tag 'commit_message', params[:commit_message], placeholder: "Removed this file because...", required: true, rows: 3, class: 'form-control'
+ .form-group
+ .col-sm-2
+ .col-sm-10
+ = submit_tag 'Remove file', class: 'btn btn-remove'
+ = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml
index d96595bc7f0..56220e520f3 100644
--- a/app/views/projects/blob/show.html.haml
+++ b/app/views/projects/blob/show.html.haml
@@ -2,3 +2,6 @@
= render 'shared/ref_switcher', destination: 'blob', path: @path
%div#tree-holder.tree-holder
= render 'blob', blob: @blob
+
+- if allowed_tree_edit?
+ = render 'projects/blob/remove'
diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml
index 4372647a41c..2bd39985107 100644
--- a/app/views/projects/branches/_branch.html.haml
+++ b/app/views/projects/branches/_branch.html.haml
@@ -18,7 +18,7 @@
Compare
- if can?(current_user, :admin_project, @project) && branch.name != @repository.root_ref
- = link_to project_branch_path(@project, branch.name), class: 'btn grouped btn-small remove-row', method: :delete, confirm: 'Removed branch cannot be restored. Are you sure?', remote: true do
+ = link_to project_branch_path(@project, branch.name), class: 'btn grouped btn-small remove-row', method: :delete, data: { confirm: 'Removed branch cannot be restored. Are you sure?'}, remote: true do
%i.icon-trash
%p
@@ -27,6 +27,4 @@
= image_tag avatar_icon(commit.author_email), class: "avatar s16", alt: ''
%span.light
= gfm escape_once(truncate(commit.title, length: 40))
- %span
- = time_ago_in_words(commit.committed_date)
- ago
+ #{time_ago_with_tooltip(commit.committed_date)}
diff --git a/app/views/projects/branches/_filter.html.haml b/app/views/projects/branches/_filter.html.haml
index 7ea11a74a2b..7e1478292e0 100644
--- a/app/views/projects/branches/_filter.html.haml
+++ b/app/views/projects/branches/_filter.html.haml
@@ -1,12 +1,22 @@
%ul.nav.nav-pills.nav-stacked
= nav_link(path: 'branches#recent') do
- = link_to 'Recent', recent_project_branches_path(@project)
+ = link_to recent_project_branches_path(@project) do
+ Recent
+ .pull-right
+ = @repository.recent_branches.count
+
= nav_link(path: 'protected_branches#index') do
= link_to project_protected_branches_path(@project) do
Protected
%i.icon-lock
+ .pull-right
+ = @project.protected_branches.count
+
= nav_link(path: 'branches#index') do
- = link_to 'All branches', project_branches_path(@project)
+ = link_to project_branches_path(@project) do
+ All branches
+ .pull-right
+ = @repository.branch_names.count
%hr
diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml
index 45b9c6c8521..690df98a2ab 100644
--- a/app/views/projects/branches/index.html.haml
+++ b/app/views/projects/branches/index.html.haml
@@ -1,10 +1,10 @@
= render "projects/commits/head"
.row
- .span3
+ .col-md-3
= render "filter"
- .span9
+ .col-md-9
- unless @branches.empty?
%ul.bordered-list.top-list
- @branches.each do |branch|
= render "projects/branches/branch", branch: branch
- = paginate @branches, theme: 'gitlab' \ No newline at end of file
+ = paginate @branches, theme: 'gitlab'
diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml
index 37612d3fd7b..d173065afd2 100644
--- a/app/views/projects/branches/new.html.haml
+++ b/app/views/projects/branches/new.html.haml
@@ -1,15 +1,15 @@
%h3.page-title
%i.icon-code-fork
New branch
-= form_tag project_branches_path, method: :post do
- .control-group
+= form_tag project_branches_path, method: :post, class: "form-horizontal" do
+ .form-group
= label_tag :branch_name, 'Name for new branch', class: 'control-label'
- .controls
- = text_field_tag :branch_name, nil, placeholder: 'feature/dashboard', required: true, tabindex: 1
- .control-group
+ .col-sm-10
+ = text_field_tag :branch_name, nil, placeholder: 'feature/dashboard', required: true, tabindex: 1, class: 'form-control'
+ .form-group
= label_tag :ref, 'Create from', class: 'control-label'
- .controls
- = text_field_tag :ref, nil, placeholder: 'master', required: true, tabindex: 2
+ .col-sm-10
+ = text_field_tag :ref, nil, placeholder: 'master', required: true, tabindex: 2, class: 'form-control'
.light branch name or commit SHA
.form-actions
= submit_tag 'Create branch', class: 'btn btn-create', tabindex: 3
diff --git a/app/views/projects/branches/recent.html.haml b/app/views/projects/branches/recent.html.haml
index 25f416c78f2..37d7919121e 100644
--- a/app/views/projects/branches/recent.html.haml
+++ b/app/views/projects/branches/recent.html.haml
@@ -1,8 +1,8 @@
= render "projects/commits/head"
.row
- .span3
+ .col-md-3
= render "filter"
- .span9
+ .col-md-9
%ul.bordered-list.top-list
- @branches.each do |branch|
- = render "projects/branches/branch", branch: branch \ No newline at end of file
+ = render "projects/branches/branch", branch: branch
diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml
index 111446db586..3d666807cf9 100644
--- a/app/views/projects/commit/_commit_box.html.haml
+++ b/app/views/projects/commit/_commit_box.html.haml
@@ -23,16 +23,14 @@
%span.light Authored by
%strong
= commit_author_link(@commit, avatar: true, size: 24)
- %time{title: @commit.authored_date.stamp("Aug 21, 2011 9:23pm")}
- #{time_ago_in_words(@commit.authored_date)} ago
+ #{time_ago_with_tooltip(@commit.authored_date)}
- if @commit.different_committer?
.commit-info-row
%span.light Committed by
%strong
= commit_committer_link(@commit, avatar: true, size: 24)
- %time{title: @commit.committed_date.stamp("Aug 21, 2011 9:23pm")}
- #{time_ago_in_words(@commit.committed_date)} ago
+ #{time_ago_with_tooltip(@commit.committed_date)}
.commit-info-row
%span.cgray= pluralize(@commit.parents.count, "parent")
diff --git a/app/views/projects/commit/huge_commit.html.haml b/app/views/projects/commit/huge_commit.html.haml
index 210d8d9e207..398ce771426 100644
--- a/app/views/projects/commit/huge_commit.html.haml
+++ b/app/views/projects/commit/huge_commit.html.haml
@@ -1,3 +1,3 @@
= render "projects/commit/commit_box"
-.alert.alert-error
+.alert.alert-danger
%h4 Commit diffs are too big to be displayed
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index d70fd96accd..b6527558275 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -7,13 +7,11 @@
.notes_count
- notes = project.notes.for_commit_id(commit.id)
- if notes.any?
- %span.badge.badge-info
+ %span.label.label-gray
%i.icon-comment
= notes.count
.commit-row-info
= commit_author_link(commit, avatar: true, size: 16)
- %time.committed_ago{ datetime: commit.committed_date, title: commit.committed_date.stamp("Aug 21, 2011 9:23pm") }
- = time_ago_in_words(commit.committed_date)
- ago
- &nbsp;
+ .committed_ago
+ #{time_ago_with_tooltip(commit.committed_date)} &nbsp;
diff --git a/app/views/projects/commits/_commits.html.haml b/app/views/projects/commits/_commits.html.haml
index b6404778073..e3411b62eb6 100644
--- a/app/views/projects/commits/_commits.html.haml
+++ b/app/views/projects/commits/_commits.html.haml
@@ -1,11 +1,11 @@
- @commits.group_by { |c| c.committed_date.to_date }.sort.reverse.each do |day, commits|
.row.commits-row
- .span2
+ .col-md-2
%h4
%i.icon-calendar
%span= day.stamp("28 Aug, 2010")
%p= pluralize(commits.count, 'commit')
- .span10
+ .col-md-10
%ul.well-list
= render commits, project: @project
%hr.lists-separator
diff --git a/app/views/projects/commits/_diffs.html.haml b/app/views/projects/commits/_diffs.html.haml
index 2e61b9ece19..a41a89bb972 100644
--- a/app/views/projects/commits/_diffs.html.haml
+++ b/app/views/projects/commits/_diffs.html.haml
@@ -1,6 +1,6 @@
- @suppress_diff ||= @suppress_diff || @force_suppress_diff
- if @suppress_diff
- .alert.alert-block
+ .alert.alert-warning
%p
%strong Warning! This is a large diff.
%p
@@ -30,6 +30,10 @@
%strong.cgreen #{@commit.stats.additions} additions
and
%strong.cred #{@commit.stats.deletions} deletions
+ - if params[:view] == 'parallel'
+ = link_to "Inline Diff", url_for(view: 'inline'), {id: "commit-diff-viewtype", class: 'btn btn-tiny pull-right'}
+ - else
+ = link_to "Side-by-side Diff", url_for(view: 'parallel'), {id: "commit-diff-viewtype", class: 'btn btn-tiny pull-right'}
.file-stats
= render "projects/commits/diff_head", diffs: diffs
@@ -46,7 +50,7 @@
%span= diff.old_path
- if @commit.parent_ids.present?
- = link_to project_blob_path(project, tree_join(@commit.parent_id, diff.new_path)), {:class => 'btn btn-tiny pull-right view-file'} do
+ = link_to project_blob_path(project, tree_join(@commit.parent_id, diff.new_path)), { class: 'btn btn-small view-file' } do
View file @
%span.commit-short-id= @commit.short_id(6)
- else
@@ -54,7 +58,7 @@
- if diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode
%span.file-mode= "#{diff.a_mode} → #{diff.b_mode}"
- = link_to project_blob_path(project, tree_join(@commit.id, diff.new_path)), {:class => 'btn btn-tiny pull-right view-file'} do
+ = link_to project_blob_path(project, tree_join(@commit.id, diff.new_path)), { class: 'btn btn-small view-file' } do
View file @
%span.commit-short-id= @commit.short_id(6)
@@ -62,7 +66,10 @@
-# Skipp all non non-supported blobs
- next unless file.respond_to?('text?')
- if file.text?
- = render "projects/commits/text_file", diff: diff, index: i
+ - if params[:view] == 'parallel'
+ = render "projects/commits/parallel_view", diff: diff, project: project, file: file, index: i
+ - else
+ = render "projects/commits/text_file", diff: diff, index: i
- elsif file.image?
- old_file = project.repository.blob_at(@commit.parent_id, diff.old_path) if @commit.parent_id
= render "projects/commits/image", diff: diff, old_file: old_file, file: file, index: i
diff --git a/app/views/projects/commits/_head.html.haml b/app/views/projects/commits/_head.html.haml
index c2da9f273b3..b9ab27d212c 100644
--- a/app/views/projects/commits/_head.html.haml
+++ b/app/views/projects/commits/_head.html.haml
@@ -1,4 +1,4 @@
-%ul.nav.nav-tabs
+%ul.nav.nav-tabs.append-bottom-15
%li= render partial: 'shared/ref_switcher', locals: {destination: 'commits'}
= nav_link(controller: [:commit, :commits]) do
@@ -22,6 +22,6 @@
- if current_user && current_controller?(:commits) && current_user.private_token
- %li.pull-right
+ %li.pull-right.hidden-sm
= link_to project_commits_path(@project, @ref, {format: :atom, private_token: current_user.private_token}), title: "Feed" do
%i.icon-rss
diff --git a/app/views/projects/commits/_image.html.haml b/app/views/projects/commits/_image.html.haml
index 73f87289d3d..9a8b7c857e5 100644
--- a/app/views/projects/commits/_image.html.haml
+++ b/app/views/projects/commits/_image.html.haml
@@ -49,7 +49,7 @@
%img{src: "data:#{old_file.mime_type};base64,#{Base64.encode64(old_file.data)}"}
.frame.added
%img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
- .controls
+ .col-sm-10
.transparent
.drag-track
.dragger{:style => "left: 0px;"}
@@ -60,4 +60,4 @@
%ul.view-modes-menu
%li.two-up{data: {mode: 'two-up'}} 2-up
%li.swipe{data: {mode: 'swipe'}} Swipe
- %li.onion-skin{data: {mode: 'onion-skin'}} Onion skin \ No newline at end of file
+ %li.onion-skin{data: {mode: 'onion-skin'}} Onion skin
diff --git a/app/views/projects/commits/_inline_commit.html.haml b/app/views/projects/commits/_inline_commit.html.haml
index 5be8460e061..f5863463fee 100644
--- a/app/views/projects/commits/_inline_commit.html.haml
+++ b/app/views/projects/commits/_inline_commit.html.haml
@@ -3,7 +3,4 @@
= link_to commit.short_id(8), project_commit_path(project, commit), class: "commit_short_id"
&nbsp;
= link_to_gfm truncate(commit.title, length: 40), project_commit_path(project, commit.id), class: "commit-row-message"
- %time.committed_ago{ datetime: commit.committed_date, title: commit.committed_date.stamp("Aug 21, 2011 9:23pm") }
- = time_ago_in_words(commit.committed_date)
- ago
- &nbsp;
+ #{time_ago_with_tooltip(commit.committed_date)} &nbsp;
diff --git a/app/views/projects/commits/_parallel_view.html.haml b/app/views/projects/commits/_parallel_view.html.haml
new file mode 100644
index 00000000000..3234e9da0ac
--- /dev/null
+++ b/app/views/projects/commits/_parallel_view.html.haml
@@ -0,0 +1,75 @@
+/ Side-by-side diff view
+- old_file = get_old_file(project, @commit, diff)
+- deleted_lines = {}
+- added_lines = {}
+- each_diff_line(diff, index) do |line, type, line_code, line_new, line_old, raw_line|
+ - if type == "old"
+ - deleted_lines[line_old] = { line_code: line_code, type: type, line: line }
+ - elsif type == "new"
+ - added_lines[line_new] = { line_code: line_code, type: type, line: line }
+
+- max_length = old_file.sloc + added_lines.length if old_file
+- max_length ||= file.sloc
+- offset1 = 0
+- offset2 = 0
+
+%div.text-file-parallel
+ %table{ style: "table-layout: fixed;" }
+ - max_length.times do |line_index|
+ - line_index1 = line_index - offset1
+ - line_index2 = line_index - offset2
+ - deleted_line = deleted_lines[line_index1 + 1]
+ - added_line = added_lines[line_index2 + 1]
+ - old_line = old_file.lines[line_index1] if old_file
+ - new_line = file.lines[line_index2]
+
+ - if deleted_line && added_line
+ - elsif deleted_line
+ - new_line = nil
+ - offset2 += 1
+ - elsif added_line
+ - old_line = nil
+ - offset1 += 1
+
+ %tr.line_holder.parallel
+ - if line_index == 0 && diff.new_file
+ %td.line_content.parallel= "File was created"
+ %td.old_line= ""
+ - elsif deleted_line
+ %td.line_content{class: "parallel noteable_line old #{deleted_line[:line_code]}", "line_code" => deleted_line[:line_code] }= old_line
+ %td.old_line.old
+ = line_index1 + 1
+ - if @comments_allowed
+ =# render "projects/notes/diff_note_link", line_code: deleted_line[:line_code]
+ - elsif old_line
+ %td.line_content.parallel= old_line
+ %td.old_line= line_index1 + 1
+ - else
+ %td.line_content.parallel= ""
+ %td.old_line= ""
+
+ %td.diff_line= ""
+
+ - if diff.deleted_file && line_index == 0
+ %td.new_line= ""
+ %td.line_content.parallel= "File was deleted"
+ - elsif added_line
+ %td.new_line.new
+ = line_index2 + 1
+ - if @comments_allowed
+ =# render "projects/notes/diff_note_link", line_code: added_line[:line_code]
+ %td.line_content{class: "parallel noteable_line new #{added_line[:line_code]}", "line_code" => added_line[:line_code] }= new_line
+ - elsif new_line
+ %td.new_line= line_index2 + 1
+ %td.line_content.parallel= new_line
+ - else
+ %td.new_line= ""
+ %td.line_content.parallel= ""
+
+ - if @reply_allowed
+ - comments1 = []
+ - comments2 = []
+ - comments1 = @line_notes.select { |n| n.line_code == deleted_line[:line_code] }.sort_by(&:created_at) if deleted_line
+ - comments2 = @line_notes.select { |n| n.line_code == added_line[:line_code] }.sort_by(&:created_at) if added_line
+ - unless comments1.empty? && comments2.empty?
+ = render "projects/notes/diff_notes_with_reply_parallel", notes1: comments1, notes2: comments2, line1: deleted_line, line2: added_line \ No newline at end of file
diff --git a/app/views/projects/commits/_text_file.html.haml b/app/views/projects/commits/_text_file.html.haml
index c724213878a..c827d96d855 100644
--- a/app/views/projects/commits/_text_file.html.haml
+++ b/app/views/projects/commits/_text_file.html.haml
@@ -21,3 +21,4 @@
- comments = @line_notes.select { |n| n.line_code == line_code }.sort_by(&:created_at)
- unless comments.empty?
= render "projects/notes/diff_notes_with_reply", notes: comments, line: line
+
diff --git a/app/views/projects/commits/show.atom.builder b/app/views/projects/commits/show.atom.builder
index 27c8fa6da72..32c82edb248 100644
--- a/app/views/projects/commits/show.atom.builder
+++ b/app/views/projects/commits/show.atom.builder
@@ -3,7 +3,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
xml.title "Recent commits to #{@project.name}:#{@ref}"
xml.link :href => project_commits_url(@project, @ref, format: :atom), :rel => "self", :type => "application/atom+xml"
xml.link :href => project_commits_url(@project, @ref), :rel => "alternate", :type => "text/html"
- xml.id project_commits_url(@project)
+ xml.id project_commits_url(@project, @ref)
xml.updated @commits.first.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ") if @commits.any?
@commits.each do |commit|
diff --git a/app/views/projects/commits/show.js.haml b/app/views/projects/commits/show.js.haml
deleted file mode 100644
index 045c9dd83d7..00000000000
--- a/app/views/projects/commits/show.js.haml
+++ /dev/null
@@ -1,3 +0,0 @@
-:plain
- CommitsList.append(#{@commits.count}, "#{escape_javascript(render('projects/commits/commits'))}");
-
diff --git a/app/views/projects/compare/_form.html.haml b/app/views/projects/compare/_form.html.haml
index ca1ea4073e6..0762655fb93 100644
--- a/app/views/projects/compare/_form.html.haml
+++ b/app/views/projects/compare/_form.html.haml
@@ -1,21 +1,21 @@
-= form_tag project_compare_index_path(@project), method: :post do
- .clearfix
- .pull-left
- - if params[:to] && params[:from]
- = link_to 'switch', {from: params[:to], to: params[:from]}, {class: 'commits-compare-switch has_tooltip', title: 'Switch base of comparison'}
- .input-prepend
- %span.add-on.input-xpadding from
- = text_field_tag :from, params[:from], class: "span3 input-xpadding"
- = "..."
- .input-prepend
- %span.add-on.input-xpadding to
- = text_field_tag :to, params[:to], class: "span3 input-xpadding"
- .pull-left
- &nbsp;
- = submit_tag "Compare", class: "btn btn-create commits-compare-btn"
- - if compare_to_mr_button?
- = link_to compare_mr_path, class: 'prepend-left-10' do
- %strong Make a merge request
+= form_tag project_compare_index_path(@project), method: :post, class: 'form-inline' do
+ .clearfix.append-bottom-20
+ - if params[:to] && params[:from]
+ = link_to 'switch', {from: params[:to], to: params[:from]}, {class: 'commits-compare-switch has_tooltip', title: 'Switch base of comparison'}
+ .form-group
+ .input-group.inline-input-group
+ %span.input-group-addon from
+ = text_field_tag :from, params[:from], class: "form-control"
+ = "..."
+ .form-group
+ .input-group.inline-input-group
+ %span.input-group-addon to
+ = text_field_tag :to, params[:to], class: "form-control"
+ &nbsp;
+ = submit_tag "Compare", class: "btn btn-create commits-compare-btn"
+ - if compare_to_mr_button?
+ = link_to compare_mr_path, class: 'prepend-left-10' do
+ %strong Make a merge request
:javascript
diff --git a/app/views/projects/compare/show.html.haml b/app/views/projects/compare/show.html.haml
index 4be62d36917..e9456c24960 100644
--- a/app/views/projects/compare/show.html.haml
+++ b/app/views/projects/compare/show.html.haml
@@ -6,7 +6,7 @@
= render "form"
- if @commits.size > 100
- .alert.alert-block
+ .alert.alert-warning
%p
%strong Warning! This comparison includes more than 100 commits.
%p To preserve performance the line diff is not shown.
diff --git a/app/views/projects/deploy_keys/_deploy_key.html.haml b/app/views/projects/deploy_keys/_deploy_key.html.haml
index 45f80ecd556..2b4f36fb4b8 100644
--- a/app/views/projects/deploy_keys/_deploy_key.html.haml
+++ b/app/views/projects/deploy_keys/_deploy_key.html.haml
@@ -10,7 +10,7 @@
%i.icon-off
Disable
- else
- = link_to 'Remove', project_deploy_key_path(@project, deploy_key), confirm: 'You are going to remove deploy key. Are you sure?', method: :delete, class: "btn btn-remove delete-key btn-small pull-right"
+ = link_to 'Remove', project_deploy_key_path(@project, deploy_key), data: { confirm: 'You are going to remove deploy key. Are you sure?'}, method: :delete, class: "btn btn-remove delete-key btn-small pull-right"
= link_to project_deploy_key_path(deploy_key.projects.include?(@project) ? @project : deploy_key.projects.first, deploy_key) do
@@ -19,7 +19,6 @@
%p.light.prepend-top-10
- deploy_key.projects.map(&:name_with_namespace).each do |project_name|
- %span.label= project_name
+ %span.label.label-gray.deploy-project-label= project_name
%small.pull-right
- Created #{time_ago_in_words(deploy_key.created_at)} ago
-
+ Created #{time_ago_with_tooltip(deploy_key.created_at)}
diff --git a/app/views/projects/deploy_keys/_form.html.haml b/app/views/projects/deploy_keys/_form.html.haml
index d49b2204537..ebb92b36b47 100644
--- a/app/views/projects/deploy_keys/_form.html.haml
+++ b/app/views/projects/deploy_keys/_form.html.haml
@@ -1,21 +1,21 @@
%div
- = form_for [@project, @key], url: project_deploy_keys_path do |f|
+ = form_for [@project, @key], url: project_deploy_keys_path, html: { class: 'deploy-key-form form-horizontal' } do |f|
-if @key.errors.any?
- .alert.alert-error
+ .alert.alert-danger
%ul
- @key.errors.full_messages.each do |msg|
%li= msg
- .control-group
- = f.label :title
- .controls= f.text_field :title, class: 'input-xlarge'
- .control-group
- = f.label :key
- .controls
+ .form-group
+ = f.label :title, class: "control-label"
+ .col-sm-10= f.text_field :title, class: 'form-control'
+ .form-group
+ = f.label :key, class: "control-label"
+ .col-sm-10
%p.light
Paste a machine public key here. Read more about how to generate it
= link_to "here", help_ssh_path
- = f.text_area :key, class: "input-xxlarge thin_area"
+ = f.text_area :key, class: "form-control thin_area", rows: 5
.form-actions
= f.submit 'Create', class: "btn-create btn"
diff --git a/app/views/projects/deploy_keys/index.html.haml b/app/views/projects/deploy_keys/index.html.haml
index 53d6e36c62c..fe847630e06 100644
--- a/app/views/projects/deploy_keys/index.html.haml
+++ b/app/views/projects/deploy_keys/index.html.haml
@@ -12,7 +12,7 @@
%hr.clearfix
.row
- .span5.enabled-keys
+ .col-md-5.enabled-keys
%h5
%strong.cgreen Enabled deploy keys
for this project
@@ -21,7 +21,7 @@
- if @enabled_keys.blank?
.light-well
%p.nothing_here_message Create a #{link_to 'new deploy key', new_project_deploy_key_path(@project)} or add an existing one
- .span5.available-keys
+ .col-md-5.available-keys
%h5
%strong Deploy keys
from projects available to you
diff --git a/app/views/projects/deploy_keys/show.html.haml b/app/views/projects/deploy_keys/show.html.haml
index 49566f83d20..67615e1781f 100644
--- a/app/views/projects/deploy_keys/show.html.haml
+++ b/app/views/projects/deploy_keys/show.html.haml
@@ -10,4 +10,4 @@
%hr
%pre= @key.key
.pull-right
- = link_to 'Remove', project_deploy_key_path(@project, @key), confirm: 'Are you sure?', method: :delete, class: "btn-remove btn delete-key"
+ = link_to 'Remove', project_deploy_key_path(@project, @key), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn-remove btn delete-key"
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index d13ae937be5..a0c95e0d82d 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -7,93 +7,78 @@
%p.light Some settings, such as "Transfer Project", are hidden inside the danger area below
%hr
.form-holder
- = form_for(@project, remote: true) do |f|
+ = form_for @project, remote: true, html: { class: "edit_project form-horizontal" } do |f|
%fieldset
- .control-group.project_name_holder
- = f.label :name do
+ .form-group.project_name_holder
+ = f.label :name, class: 'control-label' do
Project name
- .controls
- = f.text_field :name, placeholder: "Example Project", class: "span5"
+ .col-sm-10
+ = f.text_field :name, placeholder: "Example Project", class: "form-control"
- .control-group
- = f.label :description do
+ .form-group
+ = f.label :description, class: 'control-label' do
Project description
%span.light (optional)
- .controls
- = f.text_area :description, placeholder: "Awesome project", class: "span5", rows: 3, maxlength: 250
+ .col-sm-10
+ = f.text_area :description, placeholder: "Awesome project", class: "form-control", rows: 3, maxlength: 250
- if @project.repository.exists? && @project.repository.branch_names.any?
- .control-group
- = f.label :default_branch, "Default Branch"
- .controls= f.select(:default_branch, @repository.branch_names, {}, {class: 'chosen'})
-
-
- - if can?(current_user, :change_public_mode, @project)
- %fieldset.public-mode
- %legend
- Public mode:
- .control-group
- = f.label :public, class: 'control-label' do
- %span Public access
- .controls
- = f.check_box :public
- %span.descr
- If checked, this project can be cloned
- %em without any
- authentication.
- It will also be listed on the #{link_to "public access directory", public_root_path}.
- %em Any
- user will have #{link_to "Guest", help_permissions_path} permissions on the repository.
+ .form-group
+ = f.label :default_branch, "Default Branch", class: 'control-label'
+ .col-sm-10= f.select(:default_branch, @repository.branch_names, {}, {class: 'chosen select-wide'})
+
+
+ = render "visibility_level", f: f, visibility_level: @project.visibility_level, can_change_visibility_level: can?(current_user, :change_visibility_level, @project)
%fieldset.features
%legend
Labels:
- .control-group
+ .form-group
= f.label :label_list, "Labels", class: 'control-label'
- .controls
- = f.text_field :label_list, maxlength: 2000, class: "span5"
+ .col-sm-10
+ = f.text_field :label_list, maxlength: 2000, class: "form-control"
%p.hint Separate labels with commas.
%fieldset.features
%legend
Features:
- .control-group
+ .form-group
= f.label :issues_enabled, "Issues", class: 'control-label'
- .controls
+ .col-sm-10
= f.check_box :issues_enabled
%span.descr Lightweight issue tracking system for this project
- if Project.issues_tracker.values.count > 1
- .control-group
+ .form-group
= f.label :issues_tracker, "Issues tracker", class: 'control-label'
- .controls= f.select(:issues_tracker, project_issues_trackers, {}, { disabled: !@project.issues_enabled })
+ .col-sm-10= f.select(:issues_tracker, project_issues_trackers(@project.issues_tracker), {}, { disabled: !@project.issues_enabled })
- .control-group
+ .form-group
= f.label :issues_tracker_id, "Project name or id in issues tracker", class: 'control-label'
- .controls= f.text_field :issues_tracker_id, disabled: !@project.can_have_issues_tracker_id?
+ .col-sm-10= f.text_field :issues_tracker_id, disabled: !@project.can_have_issues_tracker_id?
- .control-group
+ .form-group
= f.label :merge_requests_enabled, "Merge Requests", class: 'control-label'
- .controls
+ .col-sm-10
= f.check_box :merge_requests_enabled
%span.descr Submit changes to be merged upstream.
- .control-group
+ .form-group
= f.label :wiki_enabled, "Wiki", class: 'control-label'
- .controls
+ .col-sm-10
= f.check_box :wiki_enabled
%span.descr Pages for project documentation
- .control-group
+ .form-group
= f.label :wall_enabled, "Wall", class: 'control-label'
- .controls
+ .col-sm-10
= f.check_box :wall_enabled
%span.descr Simple chat system for broadcasting inside project
- .control-group
+ .form-group
= f.label :snippets_enabled, "Snippets", class: 'control-label'
- .controls
+ .col-sm-10
= f.check_box :snippets_enabled
%span.descr Share code pastes with others out of git repository
@@ -113,18 +98,49 @@
%i.icon-chevron-down
.js-toggle-visibility-container.hide
+ - if can? current_user, :archive_project, @project
+ .ui-box.ui-box-danger
+ .title
+ - if @project.archived?
+ Unarchive project
+ - else
+ Archive project
+ .body
+ - if @project.archived?
+ %p
+ Unarchiving the project will mark its repository as active.
+ %br
+ The project can be committed to.
+ %br
+ %strong Once active this project shows up in the search and on the dashboard.
+ = link_to 'Unarchive', unarchive_project_path(@project),
+ data: { confirm: "Are you sure that you want to unarchive this project?\nWhen this project is unarchived it is active and can be comitted to again." },
+ method: :post, class: "btn btn-remove"
+ - else
+ %p
+ Archiving the project will mark its repository as read-only.
+ %br
+ It is hidden from the dashboard and doesn't show up in searches.
+ %br
+ %strong Archived projects cannot be committed to!
+ = link_to 'Archive', archive_project_path(@project),
+ data: { confirm: "Are you sure that you want to archive this project?\nAn archived project cannot be committed to." },
+ method: :post, class: "btn btn-remove"
+ - else
+ %p.nothing_here_message Only the project owner can archive a project
+
- if can?(current_user, :change_namespace, @project)
.ui-box.ui-box-danger
.title Transfer project
.errors-holder
.form-holder
- = form_for(@project, url: transfer_project_path(@project), remote: true, html: { class: 'transfer-project' }) do |f|
- .control-group
- = f.label :namespace_id do
+ = form_for(@project, url: transfer_project_path(@project), method: :put, remote: true, html: { class: 'transfer-project form-horizontal' }) do |f|
+ .form-group
+ = f.label :namespace_id, class: 'control-label' do
%span Namespace
- .controls
- .control-group
- = f.select :namespace_id, namespaces_options(@project.namespace_id), {prompt: 'Choose a project namespace'}, {class: 'chosen'}
+ .col-sm-10
+ .form-group
+ = f.select :namespace_id, namespaces_options(@project.namespace_id), { prompt: 'Choose a project namespace' }, { class: 'chosen' }
%ul
%li Be careful. Changing the project's namespace can have unintended side effects.
%li You can only transfer the project to namespaces you manage.
@@ -138,13 +154,15 @@
.title Rename repository
.errors-holder
.form-holder
- = form_for(@project) do |f|
- .control-group
- = f.label :path do
+ = form_for(@project, html: { class: 'form-horizontal' }) do |f|
+ .form-group
+ = f.label :path, class: 'control-label' do
%span Path
- .controls
- .control-group
- = f.text_field :path
+ .col-sm-9
+ .form-group
+ .input-group
+ = f.text_field :path, class: 'form-control'
+ %span.input-group-addon .git
%ul
%li Be careful. Renaming a project's repository can have unintended side effects.
%li You will need to update your local repositories to point to the new location.
@@ -154,13 +172,13 @@
- if can?(current_user, :remove_project, @project)
.ui-box.ui-box-danger
.title Remove project
- .ui-box-body
+ .body
%p
Removing the project will delete its repository and all related resources including issues, merge requests etc.
%br
%strong Removed projects cannot be restored!
- = link_to 'Remove project', @project, confirm: remove_project_message(@project), method: :delete, class: "btn btn-remove"
+ = link_to 'Remove project', @project, data: { confirm: remove_project_message(@project) }, method: :delete, class: "btn btn-remove"
- else
%p.nothing_here_message Only project owner can remove a project
diff --git a/app/views/projects/edit_tree/show.html.haml b/app/views/projects/edit_tree/show.html.haml
index b1fb4f8637d..10dae3c8ff0 100644
--- a/app/views/projects/edit_tree/show.html.haml
+++ b/app/views/projects/edit_tree/show.html.haml
@@ -11,15 +11,15 @@
%strong= @ref
%span.options
.btn-group.tree-btn-group
- = link_to "Cancel", project_blob_path(@project, @id), class: "btn btn-tiny btn-cancel", confirm: leave_edit_message
+ = link_to "Cancel", project_blob_path(@project, @id), class: "btn btn-tiny btn-cancel", data: { confirm: leave_edit_message }
.file-content.code
%pre#editor= @blob.data
- .control-group.commit_message-group
+ .form-group.commit_message-group
= label_tag 'commit_message', class: "control-label" do
Commit message
- .controls
- = text_area_tag 'commit_message', '', placeholder: "Update #{@blob.name}", required: true, rows: 3
+ .col-sm-10
+ = text_area_tag 'commit_message', '', placeholder: "Update #{@blob.name}", required: true, rows: 3, class: 'form-control'
.form-actions
= hidden_field_tag 'last_commit', @last_commit
= hidden_field_tag 'content', '', id: "file-content"
@@ -28,7 +28,7 @@
.message
to branch
%strong= @ref
- = link_to "Cancel", project_blob_path(@project, @id), class: "btn btn-cancel", confirm: leave_edit_message
+ = link_to "Cancel", project_blob_path(@project, @id), class: "btn btn-cancel", data: { confirm: leave_edit_message}
:javascript
ace.config.set("modePath", gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}/ace-src-noconflict")
diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml
index 04fc0c31733..15cf150460f 100644
--- a/app/views/projects/empty.html.haml
+++ b/app/views/projects/empty.html.haml
@@ -1,7 +1,4 @@
-%h3.page-title
- = @project.name_with_namespace
- .form-horizontal.pull-right
- = render "shared/clone_panel"
+= render "home_panel"
- if @project.import? && !@project.imported
.save-project-loader
@@ -32,7 +29,7 @@
touch README
git add README
git commit -m 'first commit'
- %span.clone= "git remote add origin #{@project.url_to_repo}"
+ %span.clone= "git remote add origin #{default_url_to_repo}"
:preserve
git push -u origin master
@@ -41,10 +38,10 @@
%pre.dark
:preserve
cd existing_git_repo
- %span.clone= "git remote add origin #{@project.url_to_repo}"
+ %span.clone= "git remote add origin #{default_url_to_repo}"
:preserve
git push -u origin master
- if can? current_user, :remove_project, @project
.prepend-top-20
- = link_to 'Remove project', @project, confirm: remove_project_message(@project), method: :delete, class: "btn btn-remove pull-right"
+ = link_to 'Remove project', @project, data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right"
diff --git a/app/views/projects/fork.html.haml b/app/views/projects/fork.html.haml
index a1c109e5d62..bdd75de447a 100644
--- a/app/views/projects/fork.html.haml
+++ b/app/views/projects/fork.html.haml
@@ -1,11 +1,11 @@
-.alert.alert-error.alert-block
+.alert.alert-danger.alert-block
%h4
%i.icon-code-fork
Fork Error!
%p
- You are trying to fork
+ You tried to fork
= link_to_project @project
- but it fails due to next reason:
+ but it failed for the following reason:
- if @forked_project && @forked_project.errors.any?
diff --git a/app/views/projects/graphs/show.js.haml b/app/views/projects/graphs/show.js.haml
index 43e776e5330..dcf6cacdceb 100644
--- a/app/views/projects/graphs/show.js.haml
+++ b/app/views/projects/graphs/show.js.haml
@@ -16,4 +16,4 @@
})
- else
:plain
- $('.stat-graph').replaceWith('<div class="alert alert-error">Failed to load graph</div>')
+ $('.stat-graph').replaceWith('<div class="alert alert-danger">Failed to load graph</div>')
diff --git a/app/views/projects/hooks/_data_ex.html.erb b/app/views/projects/hooks/_data_ex.html.erb
deleted file mode 100644
index 5092aaf6750..00000000000
--- a/app/views/projects/hooks/_data_ex.html.erb
+++ /dev/null
@@ -1,44 +0,0 @@
-<% data_ex_str = <<eos
-{
- "before": "95790bf891e76fee5e1747ab589903a6a1f80f22",
- "after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
- "ref": "refs/heads/master",
- "user_id": 4,
- "user_name": "John Smith",
- "project_id": 15,
- "repository": {
- "name": "Diaspora",
- "url": "git@localhost:diaspora.git",
- "description": "",
- "homepage": "http://localhost/diaspora",
- },
- "commits": [
- {
- "id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
- "message": "Update Catalan translation to e38cb41.",
- "timestamp": "2011-12-12T14:27:31+02:00",
- "url": "http://localhost/diaspora/commits/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
- "author": {
- "name": "Jordi Mallach",
- "email": "jordi@softcatala.org",
- }
- },
- // ...
- {
- "id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
- "message": "fixed readme",
- "timestamp": "2012-01-03T23:36:29+02:00",
- "url": "http://localhost/diaspora/commits/da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
- "author": {
- "name": "GitLab dev user",
- "email": "gitlabdev@dv6700.(none)",
- },
- },
- ],
- "total_commits_count": 4,
-};
-eos
-%>
-<div class="<%= user_color_scheme_class%>">
- <%= raw Pygments::Lexer[:js].highlight(data_ex_str) %>
-</div>
diff --git a/app/views/projects/hooks/index.html.haml b/app/views/projects/hooks/index.html.haml
index f748eb29294..dac6a698bdc 100644
--- a/app/views/projects/hooks/index.html.haml
+++ b/app/views/projects/hooks/index.html.haml
@@ -1,35 +1,61 @@
%h3.page-title
- Post-receive hooks
+ Web hooks
%p.light
- #{link_to "Post-receive hooks ", help_web_hooks_path, class: "vlink"} can be
- used for binding events when someone pushes to the repository.
+ #{link_to "Web hooks ", help_web_hooks_path, class: "vlink"} can be
+ used for binding events when something happends to the the project.
%hr.clearfix
-= form_for [@project, @hook], as: :hook, url: project_hooks_path(@project), html: { class: 'form-inline' } do |f|
+= form_for [@project, @hook], as: :hook, url: project_hooks_path(@project), html: { class: 'form-horizontal' } do |f|
-if @hook.errors.any?
- .alert.alert-error
+ .alert.alert-danger
- @hook.errors.full_messages.each do |msg|
%p= msg
- .control-group
- = f.label :url, "URL:"
- .controls
- = f.text_field :url, class: "text_field input-xxlarge input-xpadding", placeholder: 'http://example.com/trigger-ci.json'
- &nbsp;
- = f.submit "Add Web Hook", class: "btn btn-create"
-%hr
+ .form-group
+ = f.label :url, "URL", class: 'control-label'
+ .col-sm-10
+ = f.text_field :url, class: "form-control", placeholder: 'http://example.com/trigger-ci.json'
+ .form-group
+ = f.label :url, "Trigger", class: 'control-label'
+ .col-sm-10
+ %div
+ = f.check_box :push_events, class: 'pull-left'
+ .prepend-left-20
+ = f.label :push_events, class: 'list-label' do
+ %strong Push events
+ %p.light
+ This url will be triggered in case of push to repository
+ %div
+ = f.check_box :issues_events, class: 'pull-left'
+ .prepend-left-20
+ = f.label :issues_events, class: 'list-label' do
+ %strong Issues events
+ %p.light
+ This url will be triggered for created issues
+ %div
+ = f.check_box :merge_requests_events, class: 'pull-left'
+ .prepend-left-20
+ = f.label :merge_requests_events, class: 'list-label' do
+ %strong Merge Request events
+ %p.light
+ This url will be triggered for created merge requests
+ .form-actions
+ = f.submit "Add Web Hook", class: "btn btn-create"
-if @hooks.any?
.ui-box
.title
- Hooks (#{@hooks.count})
+ Web hooks (#{@hooks.count})
%ul.well-list
- @hooks.each do |hook|
%li
- %span.badge.badge-info POST
- &rarr;
- %span.monospace= hook.url
.pull-right
= link_to 'Test Hook', test_project_hook_path(@project, hook), class: "btn btn-small grouped"
- = link_to 'Remove', project_hook_path(@project, hook), confirm: 'Are you sure?', method: :delete, class: "btn btn-remove btn-small grouped"
+ = link_to 'Remove', project_hook_path(@project, hook), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove btn-small grouped"
+ .clearfix
+ %span.monospace= hook.url
+ %p
+ - %w(push_events issues_events merge_requests_events).each do |trigger|
+ - if hook.send(trigger)
+ %span.label.label-gray= trigger.titleize
diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml
index 05cfb2788be..0935d386129 100644
--- a/app/views/projects/issues/_form.html.haml
+++ b/app/views/projects/issues/_form.html.haml
@@ -1,50 +1,47 @@
%div.issue-form-holder
%h3.page-title= @issue.new_record? ? "New Issue" : "Edit Issue ##{@issue.iid}"
- = form_for [@project, @issue] do |f|
+ %hr
+ = form_for [@project, @issue], html: { class: 'form-horizontal issue-form' } do |f|
-if @issue.errors.any?
- .alert.alert-error
+ .alert.alert-danger
- @issue.errors.full_messages.each do |msg|
%span= msg
%br
- .ui-box.ui-box-show
- .ui-box-head
- .control-group
- = f.label :title do
- %strong= "Subject *"
- .controls
- = f.text_field :title, maxlength: 255, class: "input-xxlarge js-gfm-input", autofocus: true, required: true
- .ui-box-body
- .control-group
- .issue_assignee.pull-left
- = f.label :assignee_id do
- %i.icon-user
- Assign to
- .controls
- .pull-left
- = f.select(:assignee_id, @project.team.members.sort_by(&:name).map {|p| [ p.name, p.id ] }, { include_blank: "Select a user" }, {class: 'chosen'})
- .pull-right
- &nbsp;
- = link_to 'Assign to me', '#', class: 'btn btn-small assign-to-me-link'
- .issue_milestone.pull-left
- = f.label :milestone_id do
- %i.icon-time
- Milestone
- .controls= f.select(:milestone_id, @project.milestones.active.all.collect {|p| [ p.title, p.id ] }, { include_blank: "Select milestone" }, {class: 'chosen'})
+ .form-group
+ = f.label :title, class: 'control-label' do
+ %strong= "Subject *"
+ .col-sm-10
+ = f.text_field :title, maxlength: 255, class: "form-control js-gfm-input", autofocus: true, required: true
+ .form-group
+ = f.label :description, "Details", class: 'control-label'
+ .col-sm-10
+ = f.text_area :description, class: "form-control js-gfm-input", rows: 14
+ %p.hint Issues are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
+ %hr
+ .form-group
+ .issue-assignee
+ = f.label :assignee_id, class: 'control-label' do
+ %i.icon-user
+ Assign to
+ .col-sm-10
+ = f.select(:assignee_id, assignee_options(@issue), { include_blank: "Select a user" }, {class: 'chosen'})
+ &nbsp;
+ = link_to 'Assign to me', '#', class: 'btn btn-small assign-to-me-link'
+ .form-group
+ .issue-milestone
+ = f.label :milestone_id, class: 'control-label' do
+ %i.icon-time
+ Milestone
+ .col-sm-10= f.select(:milestone_id, milestone_options(@issue), { include_blank: "Select milestone" }, {class: 'chosen'})
- .ui-box-bottom
- .control-group
- = f.label :label_list do
- %i.icon-tag
- Labels
- .controls
- = f.text_field :label_list, maxlength: 2000, class: "input-xxlarge"
- %p.hint Separate labels with commas.
+ .form-group
+ = f.label :label_list, class: 'control-label' do
+ %i.icon-tag
+ Labels
+ .col-sm-10
+ = f.text_field :label_list, maxlength: 2000, class: "form-control"
+ %p.hint Separate labels with commas.
- .control-group
- = f.label :description, "Details"
- .controls
- = f.text_area :description, class: "input-xxlarge js-gfm-input", rows: 14
- %p.hint Issues are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
.form-actions
diff --git a/app/views/projects/issues/_head.html.haml b/app/views/projects/issues/_head.html.haml
index 438cc02b477..36549aae7d3 100644
--- a/app/views/projects/issues/_head.html.haml
+++ b/app/views/projects/issues/_head.html.haml
@@ -1,11 +1,26 @@
-%ul.nav.nav-tabs
+%ul.nav.nav-tabs.append-bottom-15
= nav_link(controller: :issues) do
- = link_to 'Browse Issues', project_issues_path(@project), class: "tab"
+ = link_to project_issues_path(@project), class: "tab" do
+ Browse Issues
+ - if current_controller?(:issues)
+ %span.badge.issue_counter #{@issues.total_count}
= nav_link(controller: :milestones) do
= link_to 'Milestones', project_milestones_path(@project), class: "tab"
= nav_link(controller: :labels) do
= link_to 'Labels', project_labels_path(@project), class: "tab"
- - if current_user
+
+ - if current_controller?(:issues)
+ - if current_user
+ %li
+ = link_to project_issues_path(@project, :atom, { private_token: current_user.private_token }) do
+ %i.icon-rss
+
%li.pull-right
- = link_to project_issues_path(@project, :atom, { private_token: current_user.private_token }) do
- %i.icon-rss
+ .pull-right
+ = form_tag project_issues_path(@project), method: :get, id: "issue_search_form", class: 'inline issue-search-form' do
+ .append-right-10.hidden-xs.hidden-sm
+ = search_field_tag :issue_search, nil, { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input' }
+ - if can? current_user, :write_issue, @project
+ = link_to new_project_issue_path(@project, issue: { assignee_id: params[:assignee_id], milestone_id: params[:milestone_id]}), class: "btn btn-new", title: "New Issue", id: "new_issue_link" do
+ %i.icon-plus
+ New Issue
diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml
index effad718c80..8b9433f04a5 100644
--- a/app/views/projects/issues/_issue.html.haml
+++ b/app/views/projects/issues/_issue.html.haml
@@ -26,7 +26,7 @@
%i.icon-time
= issue.milestone.title
.pull-right
- %small updated #{time_ago_in_words(issue.updated_at)} ago
+ %small updated #{time_ago_with_tooltip(issue.updated_at, 'bottom', 'issue_update_ago')}
.issue-labels
- issue.labels.each do |label|
diff --git a/app/views/projects/issues/_issue_context.html.haml b/app/views/projects/issues/_issue_context.html.haml
new file mode 100644
index 00000000000..d31b20b8815
--- /dev/null
+++ b/app/views/projects/issues/_issue_context.html.haml
@@ -0,0 +1,25 @@
+= form_for [@project, @issue], remote: true, html: {class: 'edit-issue inline-update'} do |f|
+ Created by #{link_to_member(@project, issue.author)}&nbsp;
+ - if issue.assignee
+ \ and currently assigned to
+
+ - if can?(current_user, :modify_issue, @issue)
+ = link_to profile_path(issue.assignee) do
+ = image_tag(avatar_icon(issue.assignee.email), class: 'avatar avatar-inline s16 assignee') if issue.assignee
+ = f.select(:assignee_id, assignee_options(@issue), { include_blank: "Assign to user (none):" }, {class: 'chosen'})
+ - elsif issue.assignee
+ = link_to_member(@project, @issue.assignee)
+
+
+ .pull-right.hidden-sm
+ - if issue.milestone
+ - milestone = issue.milestone
+ %cite.cgray Attached to milestone
+
+ - if can?(current_user, :modify_issue, @issue)
+ = f.select(:milestone_id, milestone_options(@issue), { include_blank: "Select milestone (none):" }, {class: 'chosen chosen-compact'})
+
+ = hidden_field_tag :issue_context
+ = f.submit class: 'btn'
+ - elsif issue.milestone
+ = link_to issue.milestone.title, project_milestone_path
diff --git a/app/views/projects/issues/_issues.html.haml b/app/views/projects/issues/_issues.html.haml
index 427d6533134..e2ce26feac3 100644
--- a/app/views/projects/issues/_issues.html.haml
+++ b/app/views/projects/issues/_issues.html.haml
@@ -6,8 +6,8 @@
= form_tag bulk_update_project_issues_path(@project), method: :post do
%span Update selected issues with &nbsp;
= select_tag('update[status]', options_for_select(['open', 'closed']), prompt: "Status")
- = select_tag('update[assignee_id]', options_from_collection_for_select(@project.team.members, "id", "name", params[:assignee_id]), prompt: "Assignee")
- = select_tag('update[milestone_id]', options_from_collection_for_select(project_active_milestones, "id", "title", params[:milestone_id]), prompt: "Milestone")
+ = select_tag('update[assignee_id]', bulk_update_assignee_options, prompt: "Assignee")
+ = select_tag('update[milestone_id]', bulk_update_milestone_options, prompt: "Milestone")
= hidden_field_tag 'update[issues_ids]', []
= hidden_field_tag :status, params[:status]
= button_tag "Save", class: "btn update_selected_issues btn-small btn-save"
@@ -78,6 +78,29 @@
%strong= milestone.title
%small.light= milestone.expires_at
+ .dropdown.inline.prepend-left-10
+ %a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"}
+ %span.light sort:
+ - if @sort.present?
+ = @sort
+ - else
+ Newest
+ %b.caret
+ %ul.dropdown-menu
+ %li
+ = link_to project_filter_path(sort: 'newest') do
+ Newest
+ = link_to project_filter_path(sort: 'oldest') do
+ Oldest
+ = link_to project_filter_path(sort: 'recently_updated') do
+ Recently updated
+ = link_to project_filter_path(sort: 'last_updated') do
+ Last updated
+ = link_to project_filter_path(sort: 'milestone_due_soon') do
+ Milestone due soon
+ = link_to project_filter_path(sort: 'milestone_due_later') do
+ Milestone due later
+
%ul.well-list.issues-list
= render @issues
@@ -90,4 +113,4 @@
%span.issue_counter #{@issues.total_count}
issues for this filter
- = paginate @issues, remote: true, theme: "gitlab"
+ = paginate @issues, theme: "gitlab"
diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml
index 81594b4972e..71a89af61a2 100644
--- a/app/views/projects/issues/index.html.haml
+++ b/app/views/projects/issues/index.html.haml
@@ -1,23 +1,6 @@
= render "head"
-.issues_content
- %h3.page-title
- Issues
- %span (<span class=issue_counter>#{@issues.total_count}</span>)
- .pull-right
- .span6
- - if can? current_user, :write_issue, @project
- = link_to new_project_issue_path(@project, issue: { assignee_id: params[:assignee_id], milestone_id: params[:milestone_id]}), class: "btn btn-new pull-right", title: "New Issue", id: "new_issue_link" do
- %i.icon-plus
- New Issue
- = form_tag project_issues_path(@project), method: :get, remote: true, id: "issue_search_form", class: 'pull-right' do
- = hidden_field_tag :status, params[:status], id: 'search_status'
- = hidden_field_tag :assignee_id, params[:assignee_id], id: 'search_assignee_id'
- = hidden_field_tag :milestone_id, params[:milestone_id], id: 'search_milestone_id'
- = hidden_field_tag :label_name, params[:label_name], id: 'search_label_name'
- = search_field_tag :issue_search, nil, { placeholder: 'Filter by title or description', class: 'input-xpadding issue_search input-xlarge append-right-10 search-text-input' }
-
.row
- .span3
+ .col-md-3
= render 'shared/project_filter', project_entities_path: project_issues_path(@project)
- .span9.issues-holder
+ .col-md-9.issues-holder
= render "issues"
diff --git a/app/views/projects/issues/index.js.haml b/app/views/projects/issues/index.js.haml
deleted file mode 100644
index 1be6a64f535..00000000000
--- a/app/views/projects/issues/index.js.haml
+++ /dev/null
@@ -1,4 +0,0 @@
-:plain
- $('.issues-holder').html("#{escape_javascript(render('issues'))}");
- History.replaceState({path: "#{request.url}"}, document.title, "#{request.url}");
- Issues.reload();
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index d08a8af2bb9..82f6b1193c7 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -2,8 +2,12 @@
Issue ##{@issue.iid}
%small
- created at
- = @issue.created_at.stamp("Aug 21, 2011")
+ created #{time_ago_with_tooltip(@issue.created_at)}
+
+ - if @issue.closed?
+ %span.state-label.state-label-red Closed
+ - else
+ %span.state-label.state-label-green Open
%span.pull-right
- if can?(current_user, :write_issue, @project)
@@ -20,41 +24,29 @@
%i.icon-edit
Edit
-.pull-right
- .span3#votes= render 'votes/votes_block', votable: @issue
+.votes-holder
+ #votes= render 'votes/votes_block', votable: @issue
.back-link
= link_to project_issues_path(@project) do
&larr; To issues list
+ %span.milestone-nav-link
+ - if @issue.milestone
+ |
+ = link_to project_milestone_path(@project, @issue.milestone) do
+ %span.light Milestone
+ = @issue.milestone.title
+.issue-box
+ %h4.title
+ = gfm escape_once(@issue.title)
-.ui-box.ui-box-show
- .ui-box-head
- %h4.box-title
- - if @issue.closed?
- .state-label.state-label-red Closed
- = gfm escape_once(@issue.title)
-
- .ui-box-body
+ .context
%cite.cgray
- Created by #{link_to_member(@project, @issue.author)}
- - if @issue.assignee
- \ and currently assigned to #{link_to_member(@project, @issue.assignee)}
-
- - if @issue.milestone
- - milestone = @issue.milestone
- %cite.cgray and attached to milestone
- %strong= link_to_gfm truncate(milestone.title, length: 20), project_milestone_path(milestone.project, milestone)
-
- .pull-right
- - @issue.labels.each do |label|
- %span{class: "label #{label_css_class(label.name)}"}
- %i.icon-tag
- = label.name
- &nbsp;
+ = render partial: 'issue_context', locals: { issue: @issue }
- if @issue.description.present?
- .ui-box-bottom
+ .description
.wiki
= preserve do
= markdown @issue.description
@@ -71,4 +63,11 @@
- @issue.participants.each do |participant|
= link_to_member(@project, participant, name: false, size: 24)
+ .issue-show-labels.pull-right
+ - @issue.labels.each do |label|
+ %span{class: "label #{label_css_class(label.name)}"}
+ %i.icon-tag
+ = label.name
+ &nbsp;
+
.voting_notes#notes= render "projects/notes/notes_with_form"
diff --git a/app/views/projects/issues/update.js.haml b/app/views/projects/issues/update.js.haml
index 7f66022a2de..2be1d0f0db0 100644
--- a/app/views/projects/issues/update.js.haml
+++ b/app/views/projects/issues/update.js.haml
@@ -2,3 +2,12 @@
- if @issue.valid?
:plain
$("##{dom_id(@issue)}").fadeOut();
+- elsif params[:issue_context]
+ $('.issue-box .context').html("#{escape_javascript(render partial: 'issue_context', locals: { issue: @issue })}");
+ $('.issue-box .context').effect('highlight');
+ $('.chosen').chosen();
+ $('.edit-issue.inline-update input[type="submit"]').hide();
+ - if @issue.milestone
+ $('.milestone-nav-link').replaceWith("#{escape_javascript(link_to "| #{@issue.milestone.title}", project_milestone_path(@issue.project, @issue.milestone), :class => 'milestone-nav-link')}")
+ - else
+ $('.milestone-nav-link').html('')
diff --git a/app/views/projects/merge_requests/_form.html.haml b/app/views/projects/merge_requests/_form.html.haml
index ce72756303e..87611c08db0 100644
--- a/app/views/projects/merge_requests/_form.html.haml
+++ b/app/views/projects/merge_requests/_form.html.haml
@@ -1,57 +1,61 @@
-= form_for [@project, @merge_request], html: { class: "#{controller.action_name}-merge-request form-horizontal" } do |f|
+= form_for [@project, @merge_request], html: { class: "merge-request-form form-horizontal" } do |f|
-if @merge_request.errors.any?
- .alert.alert-error
+ .alert.alert-danger
%ul
- @merge_request.errors.full_messages.each do |msg|
%li= msg
.merge-request-branches
.row
- .span5
+ .col-md-5
.clearfix
.pull-left
- = f.select(:source_project_id,[[@merge_request.source_project.path_with_namespace,@merge_request.source_project.id]] , {}, {class: 'source_project chosen span3'})
+ = f.select(:source_project_id, [[@merge_request.source_project_path,@merge_request.source_project.id]] , {}, { class: 'source_project chosen span3', disabled: @merge_request.persisted? })
.pull-left
&nbsp;
- %i.icon-code-fork
= f.select(:source_branch, @merge_request.source_project.repository.branch_names, { include_blank: "Select branch" }, {class: 'source_branch chosen span2'})
.mr_source_commit.prepend-top-10
- .span2
- %h2.merge-request-angle.light
+ .col-md-2
+ .merge-request-angle
%i.icon-long-arrow-right
- .span5
+ .col-md-5
.clearfix
.pull-left
- projects = @project.forked_from_project.nil? ? [@project] : [ @project,@project.forked_from_project]
- = f.select(:target_project_id, projects.map { |proj| [proj.path_with_namespace,proj.id] }, {include_blank: "Select Target Project" }, {class: 'target_project chosen span3'})
+ = f.select(:target_project_id, options_from_collection_for_select(projects, 'id', 'path_with_namespace'), {}, { class: 'target_project chosen span3', disabled: @merge_request.persisted? })
.pull-left
&nbsp;
- %i.icon-code-fork
= f.select(:target_branch, @target_branches, { include_blank: "Select branch" }, {class: 'target_branch chosen span2'})
.mr_target_commit.prepend-top-10
%hr
.merge-request-form-info
- .control-group
- = f.label :title do
+ .form-group
+ = f.label :title, class: 'control-label' do
%strong= "Title *"
- .controls= f.text_field :title, class: "input-xxlarge pad js-gfm-input", maxlength: 255, rows: 5, required: true
- .control-group
- .left
- = f.label :assignee_id do
+ .col-sm-10= f.text_field :title, class: "form-control pad js-gfm-input", maxlength: 255, rows: 5, required: true
+ .form-group
+ = f.label :description, "Description", class: 'control-label'
+ .col-sm-10
+ = f.text_area :description, class: "form-control js-gfm-input", rows: 14
+ %p.hint Description is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
+
+ %hr
+ .form-group
+ .merge-request-assignee
+ = f.label :assignee_id, class: 'control-label' do
%i.icon-user
Assign to
- .controls= f.select(:assignee_id, @project.team.members.sort_by(&:name).map {|p| [ p.name, p.id ] }, { include_blank: "Select user" }, {class: 'chosen span3'})
- .left
- = f.label :milestone_id do
+ .col-sm-10
+ = f.select(:assignee_id, assignee_options(@merge_request), { include_blank: "Select a user" }, {class: 'chosen'})
+ &nbsp;
+ = link_to 'Assign to me', '#', class: 'btn btn-small assign-to-me-link'
+ .form-group
+ .merge-request-milestone
+ = f.label :milestone_id, class: 'control-label' do
%i.icon-time
Milestone
- .controls= f.select(:milestone_id, @project.milestones.active.all.map {|p| [ p.title, p.id ] }, { include_blank: "Select milestone" }, {class: 'chosen'})
- .control-group
- = f.label :description, "Description"
- .controls
- = f.text_area :description, class: "input-xxlarge js-gfm-input", rows: 14
- %p.hint Description is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
+ .col-sm-10= f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'chosen'})
.form-actions
@@ -85,3 +89,7 @@
target_branch.on("change", function() {
$.get("#{branch_to_project_merge_requests_path(@source_project)}", {target_project_id: target_project.val(),ref: $(this).val() });
});
+ $('.assign-to-me-link').on('click', function(e){
+ $('#merge_request_assignee_id').val("#{current_user.id}").trigger("chosen:updated");
+ e.preventDefault();
+ });
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index 933d78bcbfb..ff763bca307 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -10,7 +10,7 @@
%span.pull-right
- if merge_request.for_fork?
%span.light
- = "#{merge_request.source_project.path_with_namespace}"
+ = "#{merge_request.source_project_path}"
= "#{merge_request.source_branch}"
%i.icon-angle-right.light
= "#{merge_request.target_branch}"
@@ -34,4 +34,4 @@
.pull-right
- %small updated #{time_ago_in_words(merge_request.updated_at)} ago
+ %small updated #{time_ago_with_tooltip(merge_request.updated_at, 'bottom', 'merge_request_updated_ago')}
diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml
index 47c14abdedd..42641765c5c 100644
--- a/app/views/projects/merge_requests/_show.html.haml
+++ b/app/views/projects/merge_requests/_show.html.haml
@@ -2,13 +2,17 @@
= render "projects/merge_requests/show/mr_title"
= render "projects/merge_requests/show/how_to_merge"
= render "projects/merge_requests/show/mr_box"
- = render "projects/merge_requests/show/mr_accept"
+ - if @merge_request.opened?
+ - if @merge_request.source_branch_exists? && @merge_request.target_branch_exists?
+ = render "projects/merge_requests/show/mr_accept"
+ - else
+ = render "projects/merge_requests/show/no_accept"
- if @merge_request.source_project.gitlab_ci?
= render "projects/merge_requests/show/mr_ci"
= render "projects/merge_requests/show/commits"
- if @commits.present?
- %ul.nav.nav-tabs
+ %ul.nav.nav-tabs.append-bottom-10
%li.notes-tab{data: {action: 'notes'}}
= link_to project_merge_request_path(@project, @merge_request) do
%i.icon-comment
diff --git a/app/views/projects/merge_requests/commits.js.haml b/app/views/projects/merge_requests/commits.js.haml
deleted file mode 100644
index 923b1ea032f..00000000000
--- a/app/views/projects/merge_requests/commits.js.haml
+++ /dev/null
@@ -1,4 +0,0 @@
-:plain
- merge_request.$(".commits").html("#{escape_javascript(render(partial: "commits"))}");
-
-
diff --git a/app/views/projects/merge_requests/diffs.js.haml b/app/views/projects/merge_requests/diffs.js.haml
deleted file mode 100644
index 2964f0ec462..00000000000
--- a/app/views/projects/merge_requests/diffs.js.haml
+++ /dev/null
@@ -1,2 +0,0 @@
-:plain
- merge_request.$(".diffs").html("#{escape_javascript(render(partial: "projects/merge_requests/show/diffs"))}");
diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml
index e256ee2153c..d6d0cde85be 100644
--- a/app/views/projects/merge_requests/index.html.haml
+++ b/app/views/projects/merge_requests/index.html.haml
@@ -8,9 +8,9 @@
.row
- .span3
+ .col-md-3
= render 'shared/project_filter', project_entities_path: project_merge_requests_path(@project)
- .span9
+ .col-md-9
.ui-box
.title
.mr-filters
diff --git a/app/views/projects/merge_requests/invalid.html.haml b/app/views/projects/merge_requests/invalid.html.haml
index c962811a3e4..b9c466657de 100644
--- a/app/views/projects/merge_requests/invalid.html.haml
+++ b/app/views/projects/merge_requests/invalid.html.haml
@@ -2,16 +2,22 @@
= render "projects/merge_requests/show/mr_title"
= render "projects/merge_requests/show/mr_box"
- .alert.alert-error
- %h5
- %i.icon-exclamation-sign
- We cannot find
- %span.label-branch= @merge_request.source_branch
- or
- %span.label-branch= @merge_request.target_branch
- branches in the repository.
- %p
- Maybe it was removed or never pushed.
+ .alert.alert-danger
%p
+ We cannot render this merge request properly because
+ - if @merge_request.for_fork? && !@merge_request.source_project
+ fork project was removed
+ - elsif !@merge_request.source_branch_exists?
+ %span.label.label-inverse= @merge_request.source_branch
+ does not exist in
+ %span.label.label-info= @merge_request.source_project_path
+ - elsif !@merge_request.target_branch_exists?
+ %span.label.label-inverse= @merge_request.target_branch
+ does not exist in
+ %span.label.label-info= @merge_request.target_project_path
+ - else
+ of internal error
+
+ %strong
Please close Merge Request or change branches with existing one
diff --git a/app/views/projects/merge_requests/show.js.haml b/app/views/projects/merge_requests/show.js.haml
deleted file mode 100644
index 2ce6eb63290..00000000000
--- a/app/views/projects/merge_requests/show.js.haml
+++ /dev/null
@@ -1,2 +0,0 @@
-:plain
- merge_request.$(".notes").html("#{escape_javascript(render "notes/notes_with_form")}");
diff --git a/app/views/projects/merge_requests/show/_how_to_merge.html.haml b/app/views/projects/merge_requests/show/_how_to_merge.html.haml
index 030ac285f2a..90483094b58 100644
--- a/app/views/projects/merge_requests/show/_how_to_merge.html.haml
+++ b/app/views/projects/merge_requests/show/_how_to_merge.html.haml
@@ -1,42 +1,44 @@
%div#modal_merge_info.modal.hide
- .modal-header
- %a.close{href: "#", "data-dismiss" => "modal"} ×
- %h3 How to merge
- .modal-body
- - if @merge_request.for_fork?
- - source_remote = @merge_request.source_project.namespace.nil? ? "source" :@merge_request.source_project.namespace.path
- - target_remote = @merge_request.target_project.namespace.nil? ? "target" :@merge_request.target_project.namespace.path
- %p
- %strong Step 1.
- Checkout target branch and get recent objects from GitLab
- Assuming remote for #{@merge_request.target_project.path_with_namespace} is called #{target_remote}
- remote for #{@merge_request.source_project.path_with_namespace} is called #{source_remote}
- %pre.dark
- :preserve
- git checkout #{target_remote} #{@merge_request.target_branch}
- git fetch #{source_remote}
- %p
- %strong Step 2.
- Merge source branch into target branch and push changes to GitLab
- %pre.dark
- :preserve
- git merge #{source_remote}/#{@merge_request.source_branch}
- git push #{target_remote} #{@merge_request.target_branch}
- - else
- %p
- %strong Step 1.
- Checkout target branch and get recent objects from GitLab
- %pre.dark
- :preserve
- git checkout #{@merge_request.target_branch}
- git fetch origin
- %p
- %strong Step 2.
- Merge source branch into target branch and push changes to GitLab
- %pre.dark
- :preserve
- git merge origin/#{@merge_request.source_branch}
- git push origin #{@merge_request.target_branch}
+ .modal-dialog
+ .modal-content
+ .modal-header
+ %a.close{href: "#", "data-dismiss" => "modal"} ×
+ %h3 How to merge
+ .modal-body
+ - if @merge_request.for_fork?
+ - source_remote = @merge_request.source_project.namespace.nil? ? "source" :@merge_request.source_project.namespace.path
+ - target_remote = @merge_request.target_project.namespace.nil? ? "target" :@merge_request.target_project.namespace.path
+ %p
+ %strong Step 1.
+ Checkout target branch and get recent objects from GitLab
+ Assuming remote for #{@merge_request.target_project.path_with_namespace} is called #{target_remote}
+ remote for #{@merge_request.source_project_path} is called #{source_remote}
+ %pre.dark
+ :preserve
+ git checkout #{target_remote} #{@merge_request.target_branch}
+ git fetch #{source_remote}
+ %p
+ %strong Step 2.
+ Merge source branch into target branch and push changes to GitLab
+ %pre.dark
+ :preserve
+ git merge #{source_remote}/#{@merge_request.source_branch}
+ git push #{target_remote} #{@merge_request.target_branch}
+ - else
+ %p
+ %strong Step 1.
+ Checkout target branch and get recent objects from GitLab
+ %pre.dark
+ :preserve
+ git checkout #{@merge_request.target_branch}
+ git fetch origin
+ %p
+ %strong Step 2.
+ Merge source branch into target branch and push changes to GitLab
+ %pre.dark
+ :preserve
+ git merge origin/#{@merge_request.source_branch}
+ git push origin #{@merge_request.target_branch}
:javascript
diff --git a/app/views/projects/merge_requests/show/_mr_accept.html.haml b/app/views/projects/merge_requests/show/_mr_accept.html.haml
index 299e1bc1e08..2d8c7449fd2 100644
--- a/app/views/projects/merge_requests/show/_mr_accept.html.haml
+++ b/app/views/projects/merge_requests/show/_mr_accept.html.haml
@@ -14,7 +14,7 @@
%strong
= link_to "click here", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal"
for instructions
- .accept_group
+ .accept-group
= f.submit "Accept Merge Request", class: "btn btn-create accept_merge_request"
- unless @merge_request.disallow_source_branch_removal?
.remove_branch_holder
@@ -25,7 +25,7 @@
.automerge_widget.no_satellite{style: "display:none"}
- .alert.alert-error
+ .alert.alert-danger
%span
%strong This repository does not have satellite. Ask an administrator to fix this issue
@@ -37,7 +37,7 @@
%strong This request can't be merged with GitLab. You should do it manually
.automerge_widget.unchecked
- .alert
+ .alert.alert-warning
%strong
%i.icon-refresh
Checking for ability to automatically merge…
diff --git a/app/views/projects/merge_requests/show/_mr_box.html.haml b/app/views/projects/merge_requests/show/_mr_box.html.haml
index e1d0c7c11ec..b4f648ab197 100644
--- a/app/views/projects/merge_requests/show/_mr_box.html.haml
+++ b/app/views/projects/merge_requests/show/_mr_box.html.haml
@@ -1,47 +1,39 @@
-.ui-box.ui-box-show
- .ui-box-head
- %h4.box-title
- = gfm escape_once(@merge_request.title)
- - if @merge_request.merged?
- .state-label.state-label-green
- %i.icon-ok
- Merged
- - elsif @merge_request.closed?
- .state-label.state-label-red
- Closed
+.issue-box
+ %h4.title
+ = gfm escape_once(@merge_request.title)
- .ui-box-body
- %div
- %cite.cgray
- Created on #{@merge_request.created_at.stamp("Aug 21, 2011")} by #{link_to_member(@project, @merge_request.author)}
- - if @merge_request.assignee
- \, currently assigned to #{link_to_member(@project, @merge_request.assignee)}
- - if @merge_request.milestone
+ .context
+ %cite.cgray
+ Created by #{link_to_member(@project, @merge_request.author)}.
+ - if @merge_request.assignee
+ Currently assigned to #{link_to_member(@project, @merge_request.assignee)}.
+ - if @merge_request.milestone
+ .pull-right
- milestone = @merge_request.milestone
- %cite.cgray and attached to milestone
+ %cite.cgray Attached to milestone
%strong= link_to_gfm truncate(milestone.title, length: 20), project_milestone_path(milestone.project, milestone)
- if @merge_request.description.present?
- .ui-box-bottom
+ .description
.wiki
= preserve do
= markdown @merge_request.description
- if @merge_request.closed?
- .ui-box-bottom.alert-error
+ .description.alert-danger
%span
%i.icon-remove
Closed by #{link_to_member(@project, @merge_request.closed_event.author)}
- %span #{time_ago_in_words(@merge_request.closed_event.created_at)} ago.
+ #{time_ago_with_tooltip(@merge_request.closed_event.created_at)}.
- if @merge_request.merged?
- .ui-box-bottom.alert-success
+ .description.alert-success
%span
%i.icon-ok
Merged by #{link_to_member(@project, @merge_request.merge_event.author)}
- #{time_ago_in_words(@merge_request.merge_event.created_at)} ago.
+ #{time_ago_with_tooltip(@merge_request.merge_event.created_at)}.
- if !@closes_issues.empty? && @merge_request.opened?
- .ui-box-bottom.alert-info
+ .description.alert-info
%span
%i.icon-ok
Accepting this merge request will close #{@closes_issues.size == 1 ? 'issue' : 'issues'}
diff --git a/app/views/projects/merge_requests/show/_mr_ci.html.haml b/app/views/projects/merge_requests/show/_mr_ci.html.haml
index 9b15c4526e9..2e923ec5f35 100644
--- a/app/views/projects/merge_requests/show/_mr_ci.html.haml
+++ b/app/views/projects/merge_requests/show/_mr_ci.html.haml
@@ -8,7 +8,7 @@
.ci_widget.ci-failed{style: "display:none"}
- .alert.alert-error
+ .alert.alert-danger
%i.icon-remove
%strong CI build failed
for #{@merge_request.last_commit_short_sha}.
@@ -16,20 +16,20 @@
- [:running, :pending].each do |status|
.ci_widget{class: "ci-#{status}", style: "display:none"}
- .alert
+ .alert.alert-warning
%i.icon-time
%strong CI build #{status}
for #{@merge_request.last_commit_short_sha}.
= link_to "Build page", ci_build_details_path(@merge_request)
.ci_widget
- .alert
+ .alert.alert-warning
%strong
%i.icon-refresh
Checking for CI status for #{@merge_request.last_commit_short_sha}
.ci_widget.ci-error{style: "display:none"}
- .alert.alert-error
+ .alert.alert-danger
%i.icon-remove
%strong Cannot connect to CI server. Please check your setting
diff --git a/app/views/projects/merge_requests/show/_mr_title.html.haml b/app/views/projects/merge_requests/show/_mr_title.html.haml
index 096a9167645..08a3fdf869a 100644
--- a/app/views/projects/merge_requests/show/_mr_title.html.haml
+++ b/app/views/projects/merge_requests/show/_mr_title.html.haml
@@ -1,16 +1,20 @@
%h3.page-title
- = "Merge Request ##{@merge_request.iid}:"
- &nbsp;
- -if @merge_request.for_fork?
- %span.label-branch
- %span.label-project= truncate(@merge_request.source_project.path_with_namespace, length: 25)
- #{@merge_request.source_branch}
- &rarr;
- %span.label-branch= @merge_request.target_branch
+ = "Merge Request ##{@merge_request.iid}"
+ %small
+ created #{time_ago_with_tooltip(@merge_request.created_at)}
+
+ - if @merge_request.merged?
+ %span.state-label.state-label-green
+ %i.icon-ok
+ Merged
+ - elsif @merge_request.closed?
+ %span.state-label.state-label-red
+ Closed
- else
- %span.label-branch= @merge_request.source_branch
- &rarr;
- %span.label-branch= @merge_request.target_branch
+ %span.state-label.state-label-green
+ Open
+
+
%span.pull-right
- if can?(current_user, :modify_merge_request, @merge_request)
@@ -24,15 +28,28 @@
%li= link_to "Email Patches", project_merge_request_path(@project, @merge_request, format: :patch)
%li= link_to "Plain Diff", project_merge_request_path(@project, @merge_request, format: :diff)
- = link_to 'Close', project_merge_request_path(@project, @merge_request, merge_request: {state_event: :close }), method: :put, class: "btn grouped btn-close", title: "Close merge request"
+ = link_to 'Close', project_merge_request_path(@project, @merge_request, merge_request: { state_event: :close }), method: :put, class: "btn grouped btn-close", title: "Close merge request"
= link_to edit_project_merge_request_path(@project, @merge_request), class: "btn grouped", id:"edit_merge_request" do
%i.icon-edit
Edit
-.pull-right
- .span3#votes= render 'votes/votes_block', votable: @merge_request
+.votes-holder
+ #votes= render 'votes/votes_block', votable: @merge_request
.back-link
= link_to project_merge_requests_path(@project) do
&larr; To merge requests
+
+ %span.prepend-left-20.monospace
+ -if @merge_request.for_fork?
+ %span
+ %strong
+ #{truncate(@merge_request.source_project_path, length: 25)}:
+ #{@merge_request.source_branch}
+ &rarr;
+ %span= @merge_request.target_branch
+ - else
+ %span= @merge_request.source_branch
+ &rarr;
+ %spanh= @merge_request.target_branch
diff --git a/app/views/projects/merge_requests/show/_no_accept.html.haml b/app/views/projects/merge_requests/show/_no_accept.html.haml
new file mode 100644
index 00000000000..203e2612de4
--- /dev/null
+++ b/app/views/projects/merge_requests/show/_no_accept.html.haml
@@ -0,0 +1,12 @@
+.alert.alert-danger
+ %p
+ This merge request can not be accepted because branch
+ - unless @merge_request.source_branch_exists?
+ %span.label.label-inverse= @merge_request.source_branch
+ does not exist in
+ %span.label.label-info= @merge_request.source_project_path
+ - else
+ %span.label.label-inverse= @merge_request.target_branch
+ does not exist in
+ %span.label.label-info= @merge_request.target_project_path
+ %strong Please close this merge request or change branches with existing one
diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml
index b2fd1a9284d..d770bb5b371 100644
--- a/app/views/projects/milestones/_form.html.haml
+++ b/app/views/projects/milestones/_form.html.haml
@@ -7,27 +7,27 @@
= form_for [@project, @milestone], html: {class: "new_milestone form-horizontal"} do |f|
-if @milestone.errors.any?
- .alert.alert-error
+ .alert.alert-danger
%ul
- @milestone.errors.full_messages.each do |msg|
%li= msg
.row
- .span6
- .control-group
+ .col-md-6
+ .form-group
= f.label :title, "Title", class: "control-label"
- .controls
- = f.text_field :title, maxlength: 255, class: "input-xlarge"
+ .col-sm-10
+ = f.text_field :title, maxlength: 255, class: "form-control"
%p.hint Required
- .control-group
+ .form-group
= f.label :description, "Description", class: "control-label"
- .controls
- = f.text_area :description, maxlength: 2000, class: "input-xlarge", rows: 10
+ .col-sm-10
+ = f.text_area :description, maxlength: 2000, class: "form-control", rows: 10
%p.hint Milestones are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
- .span6
- .control-group
+ .col-md-6
+ .form-group
= f.label :due_date, "Due Date", class: "control-label"
- .controls= f.hidden_field :due_date
- .controls
+ .col-sm-10= f.hidden_field :due_date
+ .col-sm-10
.datepicker
.form-actions
diff --git a/app/views/projects/milestones/_milestone.html.haml b/app/views/projects/milestones/_milestone.html.haml
index bc3368b765c..4dec3838d97 100644
--- a/app/views/projects/milestones/_milestone.html.haml
+++ b/app/views/projects/milestones/_milestone.html.haml
@@ -24,4 +24,4 @@
&nbsp;
%span.light #{milestone.percent_complete}% complete
.progress.progress-info
- .bar{style: "width: #{milestone.percent_complete}%;"}
+ .progress-bar{style: "width: #{milestone.percent_complete}%;"}
diff --git a/app/views/projects/milestones/index.html.haml b/app/views/projects/milestones/index.html.haml
index ddb46bcb5cb..cc71434af23 100644
--- a/app/views/projects/milestones/index.html.haml
+++ b/app/views/projects/milestones/index.html.haml
@@ -8,7 +8,7 @@
New Milestone
.row
- .span3
+ .col-md-3
%ul.nav.nav-pills.nav-stacked
%li{class: ("active" if (params[:f] == "active" || !params[:f]))}
= link_to project_milestones_path(@project, f: "active") do
@@ -19,7 +19,7 @@
%li{class: ("active" if params[:f] == "all")}
= link_to project_milestones_path(@project, f: "all") do
All
- .span9
+ .col-md-9
.ui-box
%ul.well-list
= render @milestones
diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml
index a5bd7a29019..df65298d4df 100644
--- a/app/views/projects/milestones/show.html.haml
+++ b/app/views/projects/milestones/show.html.haml
@@ -3,15 +3,21 @@
Milestone ##{@milestone.iid}
%small
= @milestone.expires_at
+ - if @milestone.closed?
+ %span.state-label.state-label-red Closed
+ - elsif @milestone.expired?
+ %span.state-label.state-label-red Expired
+ - else
+ %span.state-label.state-label-green Open
.pull-right
- if can?(current_user, :admin_milestone, @project)
= link_to edit_project_milestone_path(@project, @milestone), class: "btn grouped" do
%i.icon-edit
Edit
- if @milestone.active?
- = link_to 'Close Milestone', project_milestone_path(@project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-remove"
+ = link_to 'Close Milestone', project_milestone_path(@project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-remove grouped"
- else
- = link_to 'Reopen Milestone', project_milestone_path(@project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn"
+ = link_to 'Reopen Milestone', project_milestone_path(@project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn grouped"
- if @milestone.issues.any? && @milestone.can_be_closed?
.alert.alert-success
@@ -22,17 +28,11 @@
&larr; To milestones list
-.ui-box.ui-box-show
- .ui-box-head
- %h4.box-title
- - if @milestone.closed?
- .state-label.state-label-red Closed
- - elsif @milestone.expired?
- .state-label.state-label-red Expired
+.issue-box
+ %h4.title
+ = gfm escape_once(@milestone.title)
- = gfm escape_once(@milestone.title)
-
- .ui-box-body
+ .context
%p
Progress:
#{@milestone.closed_items_count} closed
@@ -40,11 +40,11 @@
#{@milestone.open_items_count} open
%span.pull-right= @milestone.expires_at
.progress.progress-info
- .bar{style: "width: #{@milestone.percent_complete}%;"}
+ .progress-bar{style: "width: #{@milestone.percent_complete}%;"}
- if @milestone.description.present?
- .ui-box-bottom
+ .description
= preserve do
= markdown @milestone.description
@@ -72,22 +72,22 @@
.tab-content
.tab-pane.active#tab-issues
.row
- .span4
+ .col-md-4
= render('issues', title: 'Unstarted Issues (open and unassigned)', issues: @issues.opened.unassigned)
- .span4
+ .col-md-4
= render('issues', title: 'Ongoing Issues (open and assigned)', issues: @issues.opened.assigned)
- .span4
+ .col-md-4
= render('issues', title: 'Completed Issues (closed)', issues: @issues.closed)
.tab-pane#tab-merge-requests
.row
- .span6
+ .col-md-6
.ui-box
.title Open
%ul.well-list
- @merge_requests.opened.each do |merge_request|
= render 'merge_request', merge_request: merge_request
- .span6
+ .col-md-6
.ui-box
.title Closed
%ul.well-list
diff --git a/app/views/projects/network/_head.html.haml b/app/views/projects/network/_head.html.haml
index 2790ed6f594..03684a57703 100644
--- a/app/views/projects/network/_head.html.haml
+++ b/app/views/projects/network/_head.html.haml
@@ -1,23 +1,15 @@
-.clearfix
- .pull-left
+.row.append-bottom-20
+ .col-md-2.append-bottom-10
= render partial: 'shared/ref_switcher', locals: {destination: 'graph'}
- .pull-left
- = form_tag project_network_path(@project, @id), method: :get do |f|
- .control-group
- = label_tag :filter_ref, "Begin with the selected commit", class: 'control-label light'
- .controls
- = check_box_tag :filter_ref, 1, @options[:filter_ref]
- - @options.each do |key, value|
- = hidden_field_tag(key, value, id: nil) unless key == "filter_ref"
-
- .search.pull-right
- = form_tag project_network_path(@project, @id), method: :get do |f|
- .control-group
- = label_tag :search , "Looking for commit:", class: 'control-label light'
- .controls
- = text_field_tag :extended_sha1, @options[:extended_sha1], placeholder: "Input an extended SHA1 syntax", class: "search-input input-xlarge"
- = button_tag type: 'submit', class: 'btn vtop' do
- %i.icon-search
- - @options.each do |key, value|
- = hidden_field_tag(key, value, id: nil) unless key == "extended_sha1"
+ .col-md-10
+ = form_tag project_network_path(@project, @id), method: :get, class: 'form-inline network-form' do |f|
+ = label_tag :search , "Looking for", class: 'light inline-label'
+ = text_field_tag :extended_sha1, @options[:extended_sha1], placeholder: "Input an extended SHA1 syntax", class: "search-input form-control input-mx-250"
+ = button_tag type: 'submit', class: 'btn btn-success' do
+ %i.icon-search
+ .inline.prepend-left-20
+ .checkbox.light
+ = label_tag :filter_ref do
+ = check_box_tag :filter_ref, 1, @options[:filter_ref]
+ %span Begin with the selected commit
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index 466a63dee83..4f81f050f60 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -3,63 +3,59 @@
= render 'projects/errors'
.project-edit-content
- = form_for @project, remote: true do |f|
- .control-group.project-name-holder
- = f.label :name do
+ = form_for @project, remote: true, html: { class: 'new_project form-horizontal' } do |f|
+ .form-group.project-name-holder
+ = f.label :name, class: 'control-label' do
%strong Project name
- .controls
- = f.text_field :name, placeholder: "Example Project", class: "input-xlarge", tabindex: 1, autofocus: true
- %span.help-inline
+ .col-sm-10
+ = f.text_field :name, placeholder: "Example Project", class: "form-control", tabindex: 1, autofocus: true
+ .help-inline
= link_to "#", class: 'js-toggle-visibility-link' do
%span Customize repository name?
- .control-group.js-toggle-visibility-container.hide
- = f.label :path do
+ .form-group.js-toggle-visibility-container.hide
+ = f.label :path, class: 'control-label' do
%span Repository name
- .controls
- .input-append
- = f.text_field :path
- %span.add-on .git
+ .col-sm-10
+ .input-group
+ = f.text_field :path, class: 'form-control'
+ %span.input-group-addon .git
- if current_user.can_select_namespace?
- .control-group
- = f.label :namespace_id do
+ .form-group
+ = f.label :namespace_id, class: 'control-label' do
%span Namespace
- .controls
+ .col-sm-10
= f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user), {}, {class: 'chosen', tabindex: 2}
- .control-group
- .controls
+ .form-group
+ .col-sm-2
+ .col-sm-10
= link_to "#", class: 'appear-link' do
%i.icon-upload-alt
%span Import existing repository?
- .control-group.appear-data.import-url-data
- = f.label :import_url do
+ .form-group.appear-data.import-url-data
+ = f.label :import_url, class: 'control-label' do
%span Import existing repo
- .controls
- = f.text_field :import_url, class: 'input-xlarge', placeholder: 'https://github.com/randx/six.git'
+ .col-sm-10
+ = f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git'
.light
URL must be cloneable
- .control-group
- = f.label :description do
+ .form-group
+ = f.label :description, class: 'control-label' do
Description
%span.light (optional)
- .controls
- = f.text_area :description, placeholder: "Awesome project", class: "input-xlarge", rows: 3, maxlength: 250, tabindex: 3
- .control-group.project-public-holder
- = f.label :public do
- %span Public project
- .controls
- = f.check_box :public, { checked: gitlab_config.default_projects_features.public }, true, false
- %span.help-inline Make project visible to everyone
+ .col-sm-10
+ = f.text_area :description, placeholder: "Awesome project", class: "form-control", rows: 3, maxlength: 250, tabindex: 3
+ = render "visibility_level", f: f, visibility_level: gitlab_config.default_projects_features.visibility_level, can_change_visibility_level: true
.form-actions
= f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4
- if current_user.can_create_group?
.pull-right
- .controls.light
+ .light
Need a group for several dependent projects?
= link_to new_group_path, class: "btn btn-tiny" do
Create a group
diff --git a/app/views/projects/new_tree/show.html.haml b/app/views/projects/new_tree/show.html.haml
new file mode 100644
index 00000000000..b206bc000a6
--- /dev/null
+++ b/app/views/projects/new_tree/show.html.haml
@@ -0,0 +1,47 @@
+%h3.page-title New file
+%hr
+.file-editor
+ = form_tag(project_new_tree_path(@project, @id), method: :put, class: "form-horizontal") do
+ .form-group.commit_message-group
+ = label_tag 'file_name', class: "control-label" do
+ File name
+ .col-sm-10
+ %span.monospace= @path[-1] == "/" ? @path : @path + "/"
+ &nbsp;
+ = text_field_tag 'file_name', params[:file_name], placeholder: "sample.rb", required: true, class: 'form-control'
+ %span
+ &nbsp;
+ on
+ %span.label-branch= @ref
+
+ .form-group.commit_message-group
+ = label_tag 'commit_message', class: "control-label" do
+ Commit message
+ .col-sm-10
+ = text_area_tag 'commit_message', params[:commit_message], placeholder: "Added new file", required: true, rows: 3, class: 'form-control'
+
+ .file-holder
+ .file-title
+ %i.icon-file
+ .file-content.code
+ %pre#editor= params[:content]
+
+ .form-actions
+ = hidden_field_tag 'content', '', id: "file-content"
+ .commit-button-annotation
+ = button_tag "Commit changes", class: 'btn commit-btn js-commit-button btn-create'
+ .message
+ to branch
+ %strong= @ref
+ = link_to "Cancel", project_tree_path(@project, @id), class: "btn btn-cancel", data: { confirm: leave_edit_message}
+
+:javascript
+ ace.config.set("modePath", gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}/ace-src-noconflict")
+ var editor = ace.edit("editor");
+
+ disableButtonIfEmptyField("#commit_message", ".js-commit-button");
+
+ $(".js-commit-button").click(function(){
+ $("#file-content").val(editor.getValue());
+ $(".file-editor form").submit();
+ });
diff --git a/app/views/projects/notes/_diff_notes_with_reply_parallel.html.haml b/app/views/projects/notes/_diff_notes_with_reply_parallel.html.haml
new file mode 100644
index 00000000000..936dbb354cd
--- /dev/null
+++ b/app/views/projects/notes/_diff_notes_with_reply_parallel.html.haml
@@ -0,0 +1,34 @@
+- note1 = notes1.first # example note
+- note2 = notes2.first # example note
+%tr.notes_holder
+ -# Check if line want not changed since comment was left
+ /- if !defined?(line1) || line1 == note1.diff_line
+ - if note1
+ %td.notes_content
+ %ul.notes{ rel: note1.discussion_id }
+ = render notes1
+ = render "projects/notes/discussion_reply_button", note: note1
+ %td.notes_line2
+ %span.btn.disabled.parallel-comment
+ %i.icon-comment
+ = notes1.count
+ - else
+ %td= ""
+ %td= ""
+
+ %td= ""
+
+ -# Check if line want not changed since comment was left
+ /- if !defined?(line2) || line2 == note2.diff_line
+ - if note2
+ %td.notes_line
+ %span.btn.disabled.parallel-comment
+ %i.icon-comment
+ = notes2.count
+ %td.notes_content
+ %ul.notes{ rel: note2.discussion_id }
+ = render notes2
+ = render "projects/notes/discussion_reply_button", note: note2
+ - else
+ %td= ""
+ %td= ""
diff --git a/app/views/projects/notes/_discussion.html.haml b/app/views/projects/notes/_discussion.html.haml
index e0aad0cc956..ee65ae1e2f5 100644
--- a/app/views/projects/notes/_discussion.html.haml
+++ b/app/views/projects/notes/_discussion.html.haml
@@ -32,8 +32,7 @@
last updated by
= link_to_member(@project, last_note.author, avatar: false)
%span.discussion-last-update
- = time_ago_in_words(last_note.updated_at)
- ago
+ #{time_ago_with_tooltip(last_note.updated_at, 'bottom', 'discussion_updated_ago')}
.discussion-body
- if note.for_diff_line?
- if note.active?
diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml
index 7add2921830..bcaedc8bd7c 100644
--- a/app/views/projects/notes/_form.html.haml
+++ b/app/views/projects/notes/_form.html.haml
@@ -1,5 +1,4 @@
-= form_for [@project, @note], remote: true, html: { multipart: true, id: nil, class: "new_note js-new-note-form common-note-form" } do |f|
-
+= form_for [@project, @note], remote: true, html: { :'data-type' => 'json', multipart: true, id: nil, class: "new_note js-new-note-form common-note-form" }, authenticity_token: true do |f|
= note_target_fields
= f.hidden_field :commit_id
= f.hidden_field :line_code
@@ -7,10 +6,12 @@
= f.hidden_field :noteable_type
.note_text_and_preview.js-toggler-container
- %a.js-note-preview-button.js-toggler-target.turn-off{ href: "javascript:;", title: "Preview", data: {url: preview_project_notes_path(@project)} }
+ %a.btn.js-note-preview-button.js-toggler-target.turn-off{ href: "javascript:;", data: {url: preview_project_notes_path(@project)} }
%i.icon-eye-open
- %a.js-note-edit-button.js-toggler-target.turn-off{ href: "javascript:;", title: "Edit" }
+ Preview
+ %a.btn.btn-primary.js-note-edit-button.js-toggler-target.turn-off{ href: "javascript:;" }
%i.icon-edit
+ Write
= f.text_area :note, size: 255, class: 'note_text js-note-text js-gfm-input turn-on'
.note_preview.js-note-preview.turn-off
@@ -27,11 +28,11 @@
%a.btn.grouped.js-close-discussion-note-form Cancel
.note-form-option
- %a.choose-btn.btn.btn-small.js-choose-note-attachment-button
+ %a.choose-btn.btn.js-choose-note-attachment-button
%i.icon-paper-clip
%span Choose File ...
&nbsp;
%span.file_name.js-attachment-filename File name...
- = f.file_field :attachment, class: "js-note-attachment-input hide"
+ = f.file_field :attachment, class: "js-note-attachment-input hidden"
.clearfix
diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml
index e56e9153c2f..fd2a3f43674 100644
--- a/app/views/projects/notes/_note.html.haml
+++ b/app/views/projects/notes/_note.html.haml
@@ -10,7 +10,7 @@
%i.icon-edit
Edit
&nbsp;
- = link_to project_note_path(@project, note), title: "Remove comment", method: :delete, confirm: 'Are you sure you want to remove this comment?', remote: true, class: "danger js-note-delete" do
+ = link_to project_note_path(@project, note), title: "Remove comment", method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: "danger js-note-delete" do
%i.icon-trash.cred
Remove
= image_tag avatar_icon(note.author_email), class: "avatar s32"
@@ -34,10 +34,10 @@
= markdown(note.note)
.note-edit-form
- = form_for note, url: project_note_path(@project, note), method: :put, remote: true do |f|
+ = form_for note, url: project_note_path(@project, note), method: :put, remote: true, authenticity_token: true do |f|
= f.text_area :note, class: 'note_text js-note-text js-gfm-input turn-on'
- .form-actions
+ .form-actions.clearfix
= f.submit 'Save changes', class: "btn btn-primary btn-save"
.note-form-option
@@ -46,7 +46,7 @@
%span Choose File ...
&nbsp;
%span.file_name.js-attachment-filename File name...
- = f.file_field :attachment, class: "js-note-attachment-input hide"
+ = f.file_field :attachment, class: "js-note-attachment-input hidden"
= link_to 'Cancel', "#", class: "btn btn-cancel note-edit-cancel"
@@ -61,6 +61,6 @@
%i.icon-paper-clip
= note.attachment_identifier
= link_to delete_attachment_project_note_path(@project, note),
- title: "Delete this attachment", method: :delete, remote: true, confirm: 'Are you sure you want to remove the attachment?', class: "danger js-note-attachment-delete" do
+ title: "Delete this attachment", method: :delete, remote: true, data: { confirm: 'Are you sure you want to remove the attachment?' }, class: "danger js-note-attachment-delete" do
%i.icon-trash.cred
.clear
diff --git a/app/views/projects/notes/_notes.html.haml b/app/views/projects/notes/_notes.html.haml
index ac8901fe704..ca60dd239b2 100644
--- a/app/views/projects/notes/_notes.html.haml
+++ b/app/views/projects/notes/_notes.html.haml
@@ -4,7 +4,7 @@
- if note_for_main_target?(note)
= render discussion_notes
- else
- = render 'discussion', discussion_notes: discussion_notes
+ = render 'projects/notes/discussion', discussion_notes: discussion_notes
- else
- @notes.each do |note|
- next unless note.author
diff --git a/app/views/projects/notes/_notes_with_form.html.haml b/app/views/projects/notes/_notes_with_form.html.haml
index ac28c7432ef..3bd592e3982 100644
--- a/app/views/projects/notes/_notes_with_form.html.haml
+++ b/app/views/projects/notes/_notes_with_form.html.haml
@@ -1,4 +1,5 @@
-%ul#notes-list.notes
+%ul#notes-list.notes.main-notes-list
+ = render "projects/notes/notes"
.js-notes-busy
.js-main-target-form
@@ -6,4 +7,4 @@
= render "projects/notes/form"
:javascript
- NoteList.init("#{@target_id}", "#{@target_type}", "#{project_notes_path(@project)}");
+ new Notes("#{project_notes_path(target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json})
diff --git a/app/views/projects/notes/create.js.haml b/app/views/projects/notes/create.js.haml
deleted file mode 100644
index c113b3482ec..00000000000
--- a/app/views/projects/notes/create.js.haml
+++ /dev/null
@@ -1,18 +0,0 @@
-- if @note.valid?
- var noteHtml = "#{escape_javascript(render @note)}";
-
- - if note_for_main_target?(@note)
- NoteList.appendNewNote(#{@note.id}, noteHtml);
- - else
- :plain
- var firstDiscussionNoteHtml = "#{escape_javascript(render "projects/notes/diff_notes_with_reply", notes: [@note])}";
- NoteList.appendNewDiscussionNote("#{@note.discussion_id}",
- firstDiscussionNoteHtml,
- noteHtml);
-
-- else
- var errorsHtml = "#{escape_javascript(render 'projects/notes/form_errors', note: @note)}";
- - if note_for_main_target?(@note)
- NoteList.errorsOnForm(errorsHtml);
- - else
- NoteList.errorsOnForm(errorsHtml, "#{@note.discussion_id}");
diff --git a/app/views/projects/notes/index.js.haml b/app/views/projects/notes/index.js.haml
deleted file mode 100644
index 6c4ed203497..00000000000
--- a/app/views/projects/notes/index.js.haml
+++ /dev/null
@@ -1,4 +0,0 @@
-- unless @notes.blank?
- var notesHtml = "#{escape_javascript(render 'projects/notes/notes')}";
- - new_note_ids = @notes.map(&:id)
- NoteList.setContent(#{new_note_ids}, notesHtml);
diff --git a/app/views/projects/protected_branches/index.html.haml b/app/views/projects/protected_branches/index.html.haml
index 8930ec4b30a..3533d4d23bc 100644
--- a/app/views/projects/protected_branches/index.html.haml
+++ b/app/views/projects/protected_branches/index.html.haml
@@ -1,8 +1,8 @@
= render "projects/commits/head"
.row
- .span3
+ .col-md-3
= render "projects/branches/filter"
- .span9
+ .col-md-9
.alert.alert-info
%p Protected branches designed to prevent push for all except #{link_to "masters", help_permissions_path, class: "vlink"}.
%p This ability allows:
@@ -14,14 +14,14 @@
- if can? current_user, :admin_project, @project
= form_for [@project, @protected_branch] do |f|
-if @protected_branch.errors.any?
- .alert.alert-error
+ .alert.alert-danger
%ul
- @protected_branch.errors.full_messages.each do |msg|
%li= msg
.entry.clearfix
= f.label :name, "Branch"
- .span3
+ .col-md-3
= f.select(:name, @project.open_branches.map { |br| [br.name, br.name] } , {include_blank: "Select branch"}, {class: "chosen span3"})
&nbsp;
= f.submit 'Protect', class: "btn-create btn"
@@ -39,15 +39,13 @@
%i.icon-lock
.pull-right
- if can? current_user, :admin_project, @project
- = link_to 'Unprotect', [@project, branch], confirm: 'Branch will be writable for developers. Are you sure?', method: :delete, class: "btn btn-remove btn-small"
+ = link_to 'Unprotect', [@project, branch], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: "btn btn-remove btn-small"
- if commit = branch.commit
= link_to project_commit_path(@project, commit.id), class: 'commit_short_id' do
= commit.short_id
%span.light
= gfm escape_once(truncate(commit.title, length: 40))
- %span
- = time_ago_in_words(commit.committed_date)
- ago
+ #{time_ago_with_tooltip(commit.committed_date)}
- else
(branch was removed from repository)
diff --git a/app/views/projects/refs/logs_tree.js.haml b/app/views/projects/refs/logs_tree.js.haml
index 213c54f5f75..e7343e0997f 100644
--- a/app/views/projects/refs/logs_tree.js.haml
+++ b/app/views/projects/refs/logs_tree.js.haml
@@ -5,5 +5,5 @@
:plain
var row = $("table.table_#{@hex_path} tr.file_#{hexdigest(file_name)}");
- row.find("td.tree_time_ago").html('#{escape_javascript time_ago_in_words(commit.committed_date)} ago');
+ row.find("td.tree_time_ago").html('#{escape_javascript time_ago_with_tooltip(commit.committed_date)}');
row.find("td.tree_commit").html('#{escape_javascript render("projects/tree/tree_commit_column", commit: commit)}');
diff --git a/app/views/projects/repositories/_feed.html.haml b/app/views/projects/repositories/_feed.html.haml
index 6e537d2959b..c77ffff43fe 100644
--- a/app/views/projects/repositories/_feed.html.haml
+++ b/app/views/projects/repositories/_feed.html.haml
@@ -15,5 +15,4 @@
= gfm escape_once(truncate(commit.title, length: 40))
%td
%span.pull-right.cgray
- = time_ago_in_words(commit.committed_date)
- ago
+ = time_ago_with_tooltip(commit.committed_date)
diff --git a/app/views/projects/repositories/stats.html.haml b/app/views/projects/repositories/stats.html.haml
index 679b4211cde..fd353978572 100644
--- a/app/views/projects/repositories/stats.html.haml
+++ b/app/views/projects/repositories/stats.html.haml
@@ -1,6 +1,6 @@
= render "projects/commits/head"
.row
- .span6
+ .col-md-6
%div#activity-chart.chart
%hr
%p
@@ -14,7 +14,7 @@
%span= @stats.authors_count
- .span6
+ .col-md-6
%h4 Top 50 Committers:
%ol.styled
- @stats.authors[0...50].each do |author|
diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml
index a099193cb6e..a3b5296ed1e 100644
--- a/app/views/projects/services/_form.html.haml
+++ b/app/views/projects/services/_form.html.haml
@@ -1,11 +1,6 @@
%h3.page-title
- - if @service.activated?
- %span.cgreen
- %i.icon-circle
- - else
- %span.cgray
- %i.icon-circle-blank
= @service.title
+ = boolean_to_icon @service.activated?
%p= @service.description
@@ -15,17 +10,17 @@
%hr
-= form_for(@service, as: :service, url: project_service_path(@project, @service.to_param), method: :put) do |f|
+= form_for(@service, as: :service, url: project_service_path(@project, @service.to_param), method: :put, html: { class: 'form-horizontal' }) do |f|
- if @service.errors.any?
- .alert.alert-error
+ .alert.alert-danger
%ul
- @service.errors.full_messages.each do |msg|
%li= msg
- .control-group
+ .form-group
= f.label :active, "Active", class: "control-label"
- .controls
+ .col-sm-10
= f.check_box :active
- @service.fields.each do |field|
@@ -33,11 +28,13 @@
- type = field[:type]
- placeholder = field[:placeholder]
- .control-group
+ .form-group
= f.label name, class: "control-label"
- .controls
+ .col-sm-10
- if type == 'text'
- = f.text_field name, class: "input-xlarge", placeholder: placeholder
+ = f.text_field name, class: "form-control", placeholder: placeholder
+ - elsif type == 'textarea'
+ = f.text_area name, rows: 5, class: "form-control", placeholder: placeholder
- elsif type == 'checkbox'
= f.check_box name
diff --git a/app/views/projects/services/index.html.haml b/app/views/projects/services/index.html.haml
index 82b85a18acd..7271dd830ca 100644
--- a/app/views/projects/services/index.html.haml
+++ b/app/views/projects/services/index.html.haml
@@ -1,17 +1,13 @@
-%h3.page-title Services
-%p.light Services allow you to integrate GitLab with other applications
+%h3.page-title Project services
+%p.light Project services allow you to integrate GitLab with other applications
%hr
%ul.bordered-list
- - @services.each do |service|
+ - @services.sort_by(&:title).each do |service|
%li
%h4
- - if service.activated?
- %span.cgreen
- %i.icon-circle
- - else
- %span.cgray
- %i.icon-circle-blank
= link_to edit_project_service_path(@project, service.to_param) do
= service.title
+ .pull-right
+ = boolean_to_icon service.activated?
%p= service.description
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index cad072a83db..dd3574c4553 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -1,52 +1,33 @@
-.project-home-panel
- .row
- .span4
- %h4.project-home-title
- = @project.name_with_namespace
- - if @project.public
- %span.public-label Public
- - else
- %span.public-label Private
-
- .span8
- .project-home-dropdown
- = render "dropdown"
- .form-horizontal
- = render "shared/clone_panel"
-
- .project-home-extra.clearfix
- .project-home-desc
- - if @project.description.present?
- = @project.description
- - if can?(current_user, :admin_project, @project)
- &ndash;
- %strong= link_to 'Edit', edit_project_path
-
- .project-home-links
- = link_to pluralize(@repository.round_commit_count, 'commit'), project_commits_path(@project, @ref || @repository.root_ref)
- = link_to pluralize(@repository.branch_names.count, 'branch'), project_branches_path(@project)
- = link_to pluralize(@repository.tag_names.count, 'tag'), project_tags_path(@project)
- %span.light.prepend-left-20= repository_size
+= render "home_panel"
.row
- .span9
+ .col-md-9.hidden-sm
= render "events/event_last_push", event: @last_push
= render 'shared/event_filter'
.content_list
.loading.hide
- .span3
+ .col-md-3.project-side
.clearfix
+ - if @project.archived?
+ .alert
+ %h5
+ %i.icon-warning-sign
+ Archived project!
+ %p Repository is read-only
+
+
- if @project.forked_from_project
.alert.alert-success
- %i.icon-code-fork
+ %i.icon-code-fork.project-fork-icon
Forked from:
+ %br
= link_to @project.forked_from_project.name_with_namespace, project_path(@project.forked_from_project)
- unless @project.empty_repo?
- if current_user && can?(current_user, :fork_project, @project) && @project.namespace != current_user.namespace
- if current_user.already_forked?(@project)
= link_to project_path(current_user.fork_of(@project)), class: 'btn btn-block' do
- %i.icon-ok
- Already forked
+ %i.icon-compass
+ Go to fork
- else
= link_to fork_project_path(@project), title: "Fork", class: "btn btn-block", method: "POST" do
%i.icon-code-fork
@@ -56,8 +37,15 @@
= link_to archive_project_repository_path(@project), class: "btn btn-block" do
%i.icon-download-alt
%span Download
- %br
- .light-well
+ = link_to project_compare_index_path(@project, from: @repository.root_ref, to: @ref || @repository.root_ref), class: 'btn btn-block' do
+ Compare code
+
+ - if @repository.readme
+ - readme = @repository.readme
+ = link_to project_blob_path(@project, tree_join(@repository.root_ref, readme.name)), class: 'btn btn-block' do
+ = readme.name
+
+ .prepend-top-10
%p
%span.light Created on
#{@project.created_at.stamp('Aug 22, 2013')}
diff --git a/app/views/projects/show.js.haml b/app/views/projects/show.js.haml
deleted file mode 100644
index 511f278929e..00000000000
--- a/app/views/projects/show.js.haml
+++ /dev/null
@@ -1,2 +0,0 @@
-:plain
- Pager.append(#{@events.count}, "#{escape_javascript(render(@events))}");
diff --git a/app/views/projects/snippets/_form.html.haml b/app/views/projects/snippets/_form.html.haml
index d414ee2d1ec..68ba227045c 100644
--- a/app/views/projects/snippets/_form.html.haml
+++ b/app/views/projects/snippets/_form.html.haml
@@ -4,21 +4,21 @@
.snippet-form-holder
= form_for [@project, @snippet], as: :project_snippet, url: url do |f|
-if @snippet.errors.any?
- .alert.alert-error
+ .alert.alert-danger
%ul
- @snippet.errors.full_messages.each do |msg|
%li= msg
- .control-group
+ .form-group
= f.label :title
- .controls= f.text_field :title, placeholder: "Example Snippet", class: 'input-xlarge', required: true
- .control-group
+ .col-sm-10= f.text_field :title, placeholder: "Example Snippet", class: 'form-control', required: true
+ .form-group
= f.label "Lifetime"
- .controls= f.select :expires_at, lifetime_select_options, {}, {class: 'chosen span2'}
- .control-group
+ .col-sm-10= f.select :expires_at, lifetime_select_options, {}, {class: 'chosen span2'}
+ .form-group
.file-editor
= f.label :file_name, "File"
- .controls
+ .col-sm-10
.file-holder.snippet
.file-title
= f.text_field :file_name, placeholder: "example.rb", class: 'snippet-file-name', required: true
@@ -34,7 +34,7 @@
= link_to "Cancel", project_snippets_path(@project), class: "btn btn-cancel"
- unless @snippet.new_record?
- = link_to 'Remove snippet', project_snippet_path(@project, @snippet), confirm: 'Are you sure?', method: :delete, class: "btn pull-right btn-remove delete-snippet prepend-left-10", id: "destroy_snippet_#{@snippet.id}"
+ = link_to 'Remove snippet', project_snippet_path(@project, @snippet), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn pull-right btn-remove delete-snippet prepend-left-10", id: "destroy_snippet_#{@snippet.id}"
:javascript
var editor = ace.edit("editor");
diff --git a/app/views/projects/snippets/_snippet.html.haml b/app/views/projects/snippets/_snippet.html.haml
index 6185e35cf6f..ddd9645ee51 100644
--- a/app/views/projects/snippets/_snippet.html.haml
+++ b/app/views/projects/snippets/_snippet.html.haml
@@ -18,4 +18,5 @@
by
= image_tag avatar_icon(snippet.author_email), class: "avatar avatar-inline s16"
= snippet.author_name
- %span.light #{time_ago_in_words(snippet.created_at)} ago
+ %span.light
+ #{time_ago_with_tooltip(snippet.created_at)}
diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml
index 5361517b2fc..945853c726b 100644
--- a/app/views/projects/tags/index.html.haml
+++ b/app/views/projects/tags/index.html.haml
@@ -24,8 +24,7 @@
.pull-right
%small.cdark
%i.icon-calendar
- = time_ago_in_words(commit.committed_date)
- ago
+ #{time_ago_with_tooltip(commit.committed_date)}
%p.prepend-left-20
= link_to commit.short_id(8), project_commit_path(@project, commit), class: "monospace"
&ndash;
@@ -37,7 +36,7 @@
%i.icon-download-alt
Download
- if can?(current_user, :admin_project, @project)
- = link_to project_tag_path(@project, tag.name), class: 'btn btn-small remove-row', method: :delete, confirm: 'Removed tag cannot be restored. Are you sure?', remote: true do
+ = link_to project_tag_path(@project, tag.name), class: 'btn btn-small remove-row', method: :delete, data: { confirm: 'Removed tag cannot be restored. Are you sure?'}, remote: true do
%i.icon-trash
= paginate @tags, theme: 'gitlab'
diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml
index 67030785e06..a9fd97f8915 100644
--- a/app/views/projects/tags/new.html.haml
+++ b/app/views/projects/tags/new.html.haml
@@ -1,15 +1,15 @@
%h3.page-title
%i.icon-code-fork
New tag
-= form_tag project_tags_path, method: :post do
- .control-group
+= form_tag project_tags_path, method: :post, class: "form-horizontal" do
+ .form-group
= label_tag :tag_name, 'Name for new tag', class: 'control-label'
- .controls
- = text_field_tag :tag_name, nil, placeholder: 'v3.0.1', required: true, tabindex: 1
- .control-group
+ .col-sm-10
+ = text_field_tag :tag_name, nil, placeholder: 'v3.0.1', required: true, tabindex: 1, class: 'form-control'
+ .form-group
= label_tag :ref, 'Create from', class: 'control-label'
- .controls
- = text_field_tag :ref, nil, placeholder: 'master', required: true, tabindex: 2
+ .col-sm-10
+ = text_field_tag :ref, nil, placeholder: 'master', required: true, tabindex: 2, class: 'form-control'
.light Branch name or commit SHA
.form-actions
= submit_tag 'Create tag', class: 'btn btn-create', tabindex: 3
diff --git a/app/views/projects/team_members/_form.html.haml b/app/views/projects/team_members/_form.html.haml
index 5214a54e909..01218090ff0 100644
--- a/app/views/projects/team_members/_form.html.haml
+++ b/app/views/projects/team_members/_form.html.haml
@@ -1,23 +1,23 @@
%h3.page-title
= "New project member(s)"
-= form_for @user_project_relation, as: :team_member, url: project_team_members_path(@project) do |f|
+= form_for @user_project_relation, as: :team_member, url: project_team_members_path(@project), html: { class: "form-horizontal users-project-form" } do |f|
-if @user_project_relation.errors.any?
- .alert.alert-error
+ .alert.alert-danger
%ul
- @user_project_relation.errors.full_messages.each do |msg|
%li= msg
%p 1. Choose people you want in the project
- .control-group
- = f.label :user_ids, "People"
- .controls
+ .form-group
+ = f.label :user_ids, "People", class: 'control-label'
+ .col-sm-10
= users_select_tag(:user_ids, multiple: true)
%p 2. Set access level for them
- .control-group
- = f.label :project_access, "Project Access"
- .controls= select_tag :project_access, options_for_select(Gitlab::Access.options, @user_project_relation.project_access), class: "project-access-select chosen"
+ .form-group
+ = f.label :project_access, "Project Access", class: 'control-label'
+ .col-sm-10= select_tag :project_access, options_for_select(Gitlab::Access.options, @user_project_relation.project_access), class: "project-access-select chosen"
.form-actions
= f.submit 'Add users', class: "btn btn-create"
diff --git a/app/views/projects/team_members/_team_member.html.haml b/app/views/projects/team_members/_team_member.html.haml
index 5c93e6e3eae..d93bb44ab96 100644
--- a/app/views/projects/team_members/_team_member.html.haml
+++ b/app/views/projects/team_members/_team_member.html.haml
@@ -7,7 +7,7 @@
= form_for(member, as: :team_member, url: project_team_member_path(@project, member.user)) do |f|
= f.select :project_access, options_for_select(UsersProject.access_roles, member.project_access), {}, class: "medium project-access-select span2 trigger-submit"
&nbsp;
- = link_to project_team_member_path(@project, user), confirm: remove_from_project_team_message(@project, user), method: :delete, class: "btn-tiny btn btn-remove", title: 'Remove user from team' do
+ = link_to project_team_member_path(@project, user), data: { confirm: remove_from_project_team_message(@project, user)}, method: :delete, class: "btn-tiny btn btn-remove", title: 'Remove user from team' do
%i.icon-minus.icon-white
= image_tag avatar_icon(user.email, 32), class: "avatar s32"
%p
diff --git a/app/views/projects/team_members/import.html.haml b/app/views/projects/team_members/import.html.haml
index 1d98b986210..ca7b32d987f 100644
--- a/app/views/projects/team_members/import.html.haml
+++ b/app/views/projects/team_members/import.html.haml
@@ -6,7 +6,7 @@
= form_tag apply_import_project_team_members_path(@project), method: 'post' do
.padded
= label_tag :source_project_id, "Project"
- .controls= select_tag(:source_project_id, options_from_collection_for_select(current_user.authorized_projects, :id, :name_with_namespace), prompt: "Select project", class: "chosen xxlarge", required: true)
+ .col-sm-10= select_tag(:source_project_id, options_from_collection_for_select(current_user.authorized_projects, :id, :name_with_namespace), prompt: "Select project", class: "chosen lg", required: true)
.form-actions
= submit_tag 'Import project members', class: "btn btn-create"
diff --git a/app/views/projects/tree/_readme.html.haml b/app/views/projects/tree/_readme.html.haml
index 98bacb49562..1fccfbc1645 100644
--- a/app/views/projects/tree/_readme.html.haml
+++ b/app/views/projects/tree/_readme.html.haml
@@ -1,8 +1,8 @@
-.file-holder#README
- .file-title
+.readme-holder#README
+ %h4
%i.icon-file
- = readme.name
- .file-content.wiki
+ = readme.name
+ .wiki
- if gitlab_markdown?(readme.name)
= preserve do
= markdown(readme.data)
diff --git a/app/views/projects/tree/_tree.html.haml b/app/views/projects/tree/_tree.html.haml
index eadfd33bd3c..4e80872df48 100644
--- a/app/views/projects/tree/_tree.html.haml
+++ b/app/views/projects/tree/_tree.html.haml
@@ -4,12 +4,16 @@
= link_to project_tree_path(@project, @ref) do
= @project.path
- tree_breadcrumbs(tree, 6) do |title, path|
- \/
%li
- if path
= link_to truncate(title, length: 40), project_tree_path(@project, path)
- else
= link_to title, '#'
+ - if @repository.branch_names.include?(@ref)
+ %li
+ = link_to project_new_tree_path(@project, @id), title: 'New file', id: 'new-file-link' do
+ %small
+ %i.icon-plus.light
%div#tree-content-holder.tree-content-holder
%table#tree-slider{class: "table_#{@hex_path} tree-table" }
@@ -17,15 +21,16 @@
%tr
%th Name
%th Last Update
- %th
+ %th.hidden-sm
Last Commit
- &nbsp;
- %i.icon-angle-right
- &nbsp;
- %small.light
- = link_to @commit.short_id, project_commit_path(@project, @commit)
- &ndash;
- = truncate(@commit.title, length: 50)
+ %span.last-commit
+ &nbsp;
+ %i.icon-angle-right
+ &nbsp;
+ %small.light
+ = link_to @commit.short_id, project_commit_path(@project, @commit)
+ &ndash;
+ = truncate(@commit.title, length: 50)
%th= link_to "history", project_commits_path(@project, @id), class: "pull-right"
- if @path.present?
diff --git a/app/views/projects/walls/show.html.haml b/app/views/projects/walls/show.html.haml
index 88aecee0815..c6afec443f4 100644
--- a/app/views/projects/walls/show.html.haml
+++ b/app/views/projects/walls/show.html.haml
@@ -3,7 +3,7 @@
- if can? current_user, :write_note, @project
.note-form-holder
- = form_for [@project, @note], remote: true, html: { multipart: true, id: nil, class: "new_note wall-note-form" } do |f|
+ = form_for [@project, @note], remote: true, html: { multipart: true, id: nil, class: "new_note wall-note-form" }, authenticity_token: true do |f|
= note_target_fields
.note_text_and_preview
= f.text_area :note, size: 255, class: 'note_text js-note-text js-gfm-input turn-on'
@@ -17,7 +17,7 @@
%span Choose File ...
&nbsp;
%span.file_name.js-attachment-filename File name...
- = f.file_field :attachment, class: "js-note-attachment-input hide"
+ = f.file_field :attachment, class: "js-note-attachment-input hidden"
.hint.pull-right CTRL + Enter to send message
.clearfix
diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml
index 16061c9dcbb..06d8660630d 100644
--- a/app/views/projects/wikis/_form.html.haml
+++ b/app/views/projects/wikis/_form.html.haml
@@ -1,4 +1,4 @@
-= form_for [@project, @wiki] do |f|
+= form_for [@project, @wiki], method: @wiki.persisted? ? :put : :post, html: { class: 'form-horizontal' } do |f|
-if @wiki.errors.any?
#error_explanation
%h2= "#{pluralize(@wiki.errors.count, "error")} prohibited this wiki from being saved:"
@@ -6,30 +6,30 @@
- @wiki.errors.full_messages.each do |msg|
%li= msg
- .ui-box.ui-box-show
- .ui-box-head
- %h3.page-title
- .edit-wiki-header
- = @wiki.title.titleize
- = f.hidden_field :title, value: @wiki.title
- = f.select :format, options_for_select(GollumWiki::MARKUPS, {selected: @wiki.format}), {}, class: "pull-right input-medium"
- = f.label :format, class: "pull-right", style: "padding-right: 20px;"
- .ui-box-body
- .controls
- %span.cgray
- Wiki content is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
- To link to a (new) page you can just type
- %code [Link Title](page-slug)
- \.
+ = f.hidden_field :title, value: @wiki.title
+ .form-group
+ = f.label :format, class: 'control-label'
+ .col-sm-10
+ = f.select :format, options_for_select(GollumWiki::MARKUPS, {selected: @wiki.format}), {}, class: "form-control"
+
+ .row
+ .col-sm-2
+ .col-sm-10
+ %p.cgray
+ Wiki content is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
+ To link to a (new) page you can just type
+ %code [Link Title](page-slug)
+ \.
+
+ .form-group
+ = f.label :content, class: 'control-label'
+ .col-sm-10
+ = f.text_area :content, class: 'form-control js-gfm-input', rows: 18
+
+ .form-group
+ = f.label :commit_message, class: 'control-label'
+ .col-sm-10= f.text_field :message, class: 'form-control', rows: 18
- .ui-box-bottom
- .control-group
- = f.label :content
- .controls= f.text_area :content, class: 'span8 js-gfm-input'
- .ui-box-bottom
- .control-group
- = f.label :commit_message
- .controls= f.text_field :message, class: 'span8'
.form-actions
- if @wiki && @wiki.persisted?
= f.submit 'Save changes', class: "btn-save btn"
diff --git a/app/views/projects/wikis/_nav.html.haml b/app/views/projects/wikis/_nav.html.haml
index 0a7e51e974c..5e5aa5170d6 100644
--- a/app/views/projects/wikis/_nav.html.haml
+++ b/app/views/projects/wikis/_nav.html.haml
@@ -1,4 +1,4 @@
-%ul.nav.nav-tabs
+%ul.nav.nav-tabs.append-bottom-20
= nav_link(html_options: {class: params[:id] == 'home' ? 'active' : '' }) do
= link_to 'Home', project_wiki_path(@project, :home)
diff --git a/app/views/projects/wikis/_new.html.haml b/app/views/projects/wikis/_new.html.haml
index f64772b2001..8cb7fa8aa0b 100644
--- a/app/views/projects/wikis/_new.html.haml
+++ b/app/views/projects/wikis/_new.html.haml
@@ -1,12 +1,14 @@
%div#modal-new-wiki.modal.hide
- .modal-header
- %a.close{href: "#", "data-dismiss" => "modal"} ×
- %h3.page-title New Wiki Page
- .modal-body
- = label_tag :new_wiki_path do
- %span Page slug
- = text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'input-xlarge', required: true, :'data-wikis-path' => project_wikis_path(@project)
- %p.hint
- Please don't use spaces and slashes
- .modal-footer
- = link_to 'Build', '#', class: 'build-new-wiki btn btn-create'
+ .modal-dialog
+ .modal-content
+ .modal-header
+ %a.close{href: "#", "data-dismiss" => "modal"} ×
+ %h3.page-title New Wiki Page
+ .modal-body
+ = label_tag :new_wiki_path do
+ %span Page slug
+ = text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'form-control', required: true, :'data-wikis-path' => project_wikis_path(@project)
+ %p.hint
+ Please don't use spaces and slashes
+ .modal-footer
+ = link_to 'Build', '#', class: 'build-new-wiki btn btn-create'
diff --git a/app/views/projects/wikis/edit.html.haml b/app/views/projects/wikis/edit.html.haml
index 66be82777c9..47b236083b3 100644
--- a/app/views/projects/wikis/edit.html.haml
+++ b/app/views/projects/wikis/edit.html.haml
@@ -1,10 +1,13 @@
= render 'nav'
-%h3.page-title
- Editing page
+.pull-right
= render 'main_links'
+%h3.page-title
+ Editing -
+ %span.light #{@wiki.title.titleize}
+%hr
= render 'form'
.pull-right
- if @wiki.persisted? && can?(current_user, :admin_wiki, @project)
- = link_to project_wiki_path(@project, @wiki), confirm: "Are you sure you want to delete this page?", method: :delete, class: "btn btn-small btn-remove" do
+ = link_to project_wiki_path(@project, @wiki), data: { confirm: "Are you sure you want to delete this page?"}, method: :delete, class: "btn btn-small btn-remove" do
Delete this page
diff --git a/app/views/projects/wikis/git_access.html.haml b/app/views/projects/wikis/git_access.html.haml
index 692eda4e535..55fb6d5bf7b 100644
--- a/app/views/projects/wikis/git_access.html.haml
+++ b/app/views/projects/wikis/git_access.html.haml
@@ -7,7 +7,7 @@
.git-clone-holder
%button{class: "btn active", :"data-clone" => @gollum_wiki.ssh_url_to_repo} SSH
%button{class: "btn", :"data-clone" => @gollum_wiki.http_url_to_repo}= gitlab_config.protocol.upcase
- = text_field_tag :project_clone, @gollum_wiki.url_to_repo, class: "one_click_select input-xxlarge", readonly: true
+ = text_field_tag :project_clone, @gollum_wiki.url_to_repo, class: "one_click_select form-control", readonly: true
.git-empty
%fieldset
diff --git a/app/views/projects/wikis/history.html.haml b/app/views/projects/wikis/history.html.haml
index 344a5e88e6e..55efb624e23 100644
--- a/app/views/projects/wikis/history.html.haml
+++ b/app/views/projects/wikis/history.html.haml
@@ -3,7 +3,7 @@
%span.light History for
= link_to @wiki.title.titleize, project_wiki_path(@project, @wiki)
-%table
+%table.table
%thead
%tr
%th Page version
@@ -23,8 +23,7 @@
%td
= commit.title
%td
- = time_ago_in_words(version.date)
- ago
+ #{time_ago_with_tooltip(version.date)}
%td
%strong
= @wiki.page.wiki.page(@wiki.page.name, commit.id).try(:format)
diff --git a/app/views/projects/wikis/pages.html.haml b/app/views/projects/wikis/pages.html.haml
index 59d104a985b..7a890816568 100644
--- a/app/views/projects/wikis/pages.html.haml
+++ b/app/views/projects/wikis/pages.html.haml
@@ -8,4 +8,4 @@
= link_to wiki_page.title.titleize, project_wiki_path(@project, wiki_page)
%small (#{wiki_page.format})
.pull-right
- %small Last edited #{time_ago_in_words(wiki_page.commit.created_at)} ago
+ %small Last edited #{time_ago_with_tooltip(wiki_page.commit.created_at)}
diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml
index 63e7f12b7fe..f7d4fe42b57 100644
--- a/app/views/projects/wikis/show.html.haml
+++ b/app/views/projects/wikis/show.html.haml
@@ -7,9 +7,14 @@
This is an old version of this page.
You can view the #{link_to "most recent version", project_wiki_path(@project, @wiki)} or browse the #{link_to "history", history_project_wiki_path(@project, @wiki)}.
-.file-holder
- .file-content.wiki
+%hr
+
+.wiki-holder
+ .wiki
= preserve do
= render_wiki_content(@wiki)
-%p.time Last edited by #{commit_author_link(@wiki.commit, avatar: true, size: 16)} #{time_ago_in_words @wiki.commit.created_at} ago
+%hr
+
+.wiki-last-edit-by
+ Last edited by #{commit_author_link(@wiki.commit, avatar: true, size: 16)} #{time_ago_with_tooltip(@wiki.commit.created_at)}
diff --git a/app/views/public/projects/index.html.haml b/app/views/public/projects/index.html.haml
index 21aee644579..d511a3037aa 100644
--- a/app/views/public/projects/index.html.haml
+++ b/app/views/public/projects/index.html.haml
@@ -1,16 +1,38 @@
-.row
- .span6
- %h3.page-title
- Projects (#{@projects.total_count})
- .light
- You can browse public projects in read-only mode until signed in.
+%h3.page-title
+ Projects (#{@projects.total_count})
+.light
+ You can browse public projects in read-only mode until signed in.
+%hr
+.clearfix
+ .pull-left
+ = form_tag public_projects_path, method: :get, class: 'form-inline form-tiny' do |f|
+ .form-group
+ = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input", id: "projects_search"
+ .form-group
+ = submit_tag 'Search', class: "btn btn-primary wide"
+
+ .pull-right
+ .dropdown.inline
+ %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
+ %span.light sort:
+ - if @sort.present?
+ = @sort.humanize
+ - else
+ Name
+ %b.caret
+ %ul.dropdown-menu
+ %li
+ = link_to public_projects_path(sort: nil) do
+ Name
+ = link_to public_projects_path(sort: 'newest') do
+ Newest
+ = link_to public_projects_path(sort: 'oldest') do
+ Oldest
+ = link_to public_projects_path(sort: 'recently_updated') do
+ Recently updated
+ = link_to public_projects_path(sort: 'last_updated') do
+ Last updated
- .span6
- .pull-right
- = form_tag public_projects_path, method: :get, class: 'form-inline' do |f|
- .search-holder
- = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "span3 search-text-input", id: "projects_search"
- = submit_tag 'Search', class: "btn btn-primary wide"
%hr
.public-projects
%ul.bordered-list.top-list
@@ -19,6 +41,10 @@
%h4
= link_to project_path(project) do
= project.name_with_namespace
+ - if project.internal?
+ %small.access-icon
+ = internal_icon
+ Internal
.pull-right
%pre.public-clone git clone #{project.http_url_to_repo}
diff --git a/app/views/search/_global_results.html.haml b/app/views/search/_global_results.html.haml
new file mode 100644
index 00000000000..6e6feeb6c5a
--- /dev/null
+++ b/app/views/search/_global_results.html.haml
@@ -0,0 +1,5 @@
+.search_results
+ %ul.bordered-list
+ = render partial: "search/results/project", collection: @projects
+ = render partial: "search/results/merge_request", collection: @merge_requests
+ = render partial: "search/results/issue", collection: @issues
diff --git a/app/views/search/_project_results.html.haml b/app/views/search/_project_results.html.haml
new file mode 100644
index 00000000000..8d04dd27cca
--- /dev/null
+++ b/app/views/search/_project_results.html.haml
@@ -0,0 +1,17 @@
+%ul.nav.nav-pills
+ %li{class: ("active" if params[:search_code].present?)}
+ = link_to search_path(params.merge(search_code: true)) do
+ Repository Code
+ %li{class: ("active" if params[:search_code].blank?)}
+ = link_to search_path(params.merge(search_code: nil)) do
+ Everything else
+
+.search_results
+ - if params[:search_code].present?
+ .blob-results
+ = render partial: "search/results/blob", collection: @blobs
+ = paginate @blobs, theme: 'gitlab'
+ - else
+ %ul.bordered-list
+ = render partial: "search/results/merge_request", collection: @merge_requests
+ = render partial: "search/results/issue", collection: @issues
diff --git a/app/views/search/_result.html.haml b/app/views/search/_result.html.haml
deleted file mode 100644
index 5f7540d1b16..00000000000
--- a/app/views/search/_result.html.haml
+++ /dev/null
@@ -1,62 +0,0 @@
-%fieldset
- %legend
- Search results
- %span.cgray (#{@total_results})
-
-- if @project
- %ul.nav.nav-pills
- %li{class: ("active" if params[:search_code].present?)}
- = link_to search_path(params.merge(search_code: true)) do
- Repository Code
- %li{class: ("active" if params[:search_code].blank?)}
- = link_to search_path(params.merge(search_code: nil)) do
- Everything else
-
-.search_results
- %ul.bordered-list
- - @projects.each do |project|
- %li
- project:
- = link_to project do
- %strong.term= project.name_with_namespace
- - @merge_requests.each do |merge_request|
- %li
- merge request:
- = link_to [merge_request.target_project, merge_request] do
- %span ##{merge_request.iid}
- %strong.term
- = truncate merge_request.title, length: 50
- - if merge_request.for_fork?
- %span.light (#{merge_request.source_project.name_with_namespace}:#{merge_request.source_branch} &rarr; #{merge_request.target_project.name_with_namespace}:#{merge_request.target_branch})
- - else
- %span.light (#{merge_request.source_branch} &rarr; #{merge_request.target_branch})
- - if merge_request.closed?
- %span.label Closed
-
- - @issues.each do |issue|
- %li
- issue:
- = link_to [issue.project, issue] do
- %span ##{issue.iid}
- %strong.term
- = truncate issue.title, length: 50
- %span.light (#{issue.project.name_with_namespace})
- - if issue.closed?
- %span.label Closed
-
- - @wiki_pages.each do |wiki_page|
- %li
- wiki:
- = link_to project_wiki_path(wiki_page.project, wiki_page) do
- %strong.term
- = truncate wiki_page.title, length: 50
- %span.light (#{wiki_page.project.name_with_namespace})
-
- - @blobs.each do |blob|
- = render 'blob', blob: blob
-
- = paginate @blobs, theme: 'gitlab'
-
-:javascript
- $(".search_results .term").highlight("#{escape_javascript(params[:search])}");
-
diff --git a/app/views/search/_results.html.haml b/app/views/search/_results.html.haml
new file mode 100644
index 00000000000..75f134aae66
--- /dev/null
+++ b/app/views/search/_results.html.haml
@@ -0,0 +1,13 @@
+%fieldset
+ %legend
+ Search results
+ %span.cgray (#{@total_results})
+
+- if @project
+ = render "project_results"
+- else
+ = render "global_results"
+
+:javascript
+ $(".search_results .term").highlight("#{escape_javascript(params[:search])}");
+
diff --git a/app/views/search/_blob.html.haml b/app/views/search/results/_blob.html.haml
index 559fdd794fc..a2e4a00b718 100644
--- a/app/views/search/_blob.html.haml
+++ b/app/views/search/results/_blob.html.haml
@@ -1,4 +1,4 @@
-%li
+.blob-result
.file-holder
.file-title
= link_to project_blob_path(@project, tree_join(blob.ref, blob.filename), :anchor => "L" + blob.startline.to_s) do
diff --git a/app/views/search/results/_issue.html.haml b/app/views/search/results/_issue.html.haml
new file mode 100644
index 00000000000..7a24b76bced
--- /dev/null
+++ b/app/views/search/results/_issue.html.haml
@@ -0,0 +1,9 @@
+%li
+ issue:
+ = link_to [issue.project, issue] do
+ %span ##{issue.iid}
+ %strong.term
+ = truncate issue.title, length: 50
+ %span.light (#{issue.project.name_with_namespace})
+ - if issue.closed?
+ %span.label Closed
diff --git a/app/views/search/results/_merge_request.html.haml b/app/views/search/results/_merge_request.html.haml
new file mode 100644
index 00000000000..22d7587f6c1
--- /dev/null
+++ b/app/views/search/results/_merge_request.html.haml
@@ -0,0 +1,12 @@
+%li
+ merge request:
+ = link_to [merge_request.target_project, merge_request] do
+ %span ##{merge_request.iid}
+ %strong.term
+ = truncate merge_request.title, length: 50
+ - if merge_request.for_fork?
+ %span.light (#{merge_request.source_project.name_with_namespace}:#{merge_request.source_branch} &rarr; #{merge_request.target_project.name_with_namespace}:#{merge_request.target_branch})
+ - else
+ %span.light (#{merge_request.source_branch} &rarr; #{merge_request.target_branch})
+ - if merge_request.closed?
+ %span.label Closed
diff --git a/app/views/search/results/_project.html.haml b/app/views/search/results/_project.html.haml
new file mode 100644
index 00000000000..abc86c72bef
--- /dev/null
+++ b/app/views/search/results/_project.html.haml
@@ -0,0 +1,7 @@
+%li
+ project:
+ = link_to project do
+ %strong.term= project.name_with_namespace
+ - if project.description.present?
+ &ndash;
+ %span.light.term= project.description
diff --git a/app/views/search/show.html.haml b/app/views/search/show.html.haml
index f1f65981b37..3b6f10d4d9c 100644
--- a/app/views/search/show.html.haml
+++ b/app/views/search/show.html.haml
@@ -1,16 +1,20 @@
-= form_tag search_path, method: :get, class: 'form-inline' do |f|
- .search-holder
- = label_tag :search do
- %span Looking for
- .controls
- = search_field_tag :search, params[:search], placeholder: "issue 143", class: "input-xxlarge search-text-input", id: "dashboard_search"
+= form_tag search_path, method: :get, class: 'form-horizontal' do |f|
+ .search-holder.clearfix
+ .form-group
+ = label_tag :search, class: 'control-label' do
+ %span Looking for
+ .col-sm-6
+ = search_field_tag :search, params[:search], placeholder: "issue 143", class: "form-control search-text-input", id: "dashboard_search"
+ .col-sm-4
+ = submit_tag 'Search', class: "btn btn-create"
+ .form-group
+ .col-sm-2
+ .col-sm-10
+ = render 'filter', f: f
= hidden_field_tag :project_id, params[:project_id]
= hidden_field_tag :group_id, params[:group_id]
= hidden_field_tag :search_code, params[:search_code]
- = submit_tag 'Search', class: "btn btn-create"
- .prepend-top-10
- = render 'filter', f: f
.results.prepend-top-10
- if params[:search].present?
- = render 'search/result'
+ = render 'search/results'
diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml
index ac7b9ee7f2d..be71236c52c 100644
--- a/app/views/shared/_clone_panel.html.haml
+++ b/app/views/shared/_clone_panel.html.haml
@@ -1,4 +1,6 @@
.git-clone-holder
- %button{class: "btn active", :"data-clone" => @project.ssh_url_to_repo} SSH
- %button{class: "btn", :"data-clone" => @project.http_url_to_repo}= gitlab_config.protocol.upcase
- = text_field_tag :project_clone, @project.url_to_repo, class: "one_click_select span5", readonly: true
+ .protocol-btns
+ %button{class: "btn #{ 'active' if default_clone_protocol == 'ssh' }", :"data-clone" => @project.ssh_url_to_repo} SSH
+ %button{class: "btn #{ 'active' if default_clone_protocol == 'http' }", :"data-clone" => @project.http_url_to_repo}= gitlab_config.protocol.upcase
+ .protocol-clone
+ = text_field_tag :project_clone, default_url_to_repo, class: "one_click_select span4", readonly: true
diff --git a/app/views/shared/_filter.html.haml b/app/views/shared/_filter.html.haml
index 2f051cea48b..00496b067ac 100644
--- a/app/views/shared/_filter.html.haml
+++ b/app/views/shared/_filter.html.haml
@@ -1,29 +1,42 @@
-= form_tag filter_path(entity), method: 'get' do
- %fieldset
- %ul.nav.nav-pills.nav-stacked
- %li{class: ("active" if params[:status].blank?)}
- = link_to filter_path(entity, status: nil) do
- Open
- %li{class: ("active" if params[:status] == 'closed')}
- = link_to filter_path(entity, status: 'closed') do
- Closed
- %li{class: ("active" if params[:status] == 'all')}
- = link_to filter_path(entity, status: 'all') do
- All
+.side-filters.hidden-xs.hidden-sm
+ = form_tag filter_path(entity), method: 'get' do
+ %fieldset.scope-filter
+ %ul.nav.nav-pills.nav-stacked
+ %li{class: ("active" if params[:scope].blank?)}
+ = link_to filter_path(entity, scope: nil) do
+ Assigned to me
+ %li{class: ("active" if params[:scope] == 'authored')}
+ = link_to filter_path(entity, scope: 'authored') do
+ Created by me
+ %li{class: ("active" if params[:scope] == 'all')}
+ = link_to filter_path(entity, scope: 'all') do
+ All
- %fieldset
- %legend Projects
- %ul.nav.nav-pills.nav-pills-small.nav-stacked
- - @projects.each do |project|
- - unless entities_per_project(project, entity).zero?
- %li{class: ("active" if params[:project_id] == project.id.to_s)}
- = link_to filter_path(entity, project_id: project.id) do
- = project.name_with_namespace
- %small.pull-right= entities_per_project(project, entity)
+ %fieldset.status-filter
+ %ul.nav.nav-pills.nav-stacked
+ %li{class: ("active" if params[:status].blank?)}
+ = link_to filter_path(entity, status: nil) do
+ Open
+ %li{class: ("active" if params[:status] == 'closed')}
+ = link_to filter_path(entity, status: 'closed') do
+ Closed
+ %li{class: ("active" if params[:status] == 'all')}
+ = link_to filter_path(entity, status: 'all') do
+ All
- %fieldset
- - if params[:status].present? || params[:project_id].present?
- = link_to filter_path(entity, status: nil, project_id: nil), class: 'pull-right cgray' do
- %i.icon-remove
- %strong Clear filter
+ %fieldset
+ %legend Projects
+ %ul.nav.nav-pills.nav-pills-small.nav-stacked
+ - @projects.each do |project|
+ - unless entities_per_project(project, entity).zero?
+ %li{class: ("active" if params[:project_id] == project.id.to_s)}
+ = link_to filter_path(entity, project_id: project.id) do
+ = project.name_with_namespace
+ %small.pull-right= entities_per_project(project, entity)
+
+ %fieldset
+ - if params[:status].present? || params[:project_id].present?
+ = link_to filter_path(entity, status: nil, project_id: nil), class: 'pull-right cgray' do
+ %i.icon-remove
+ %strong Clear filter
diff --git a/app/views/shared/_issues.html.haml b/app/views/shared/_issues.html.haml
index 3b3888a50e9..199000656fe 100644
--- a/app/views/shared/_issues.html.haml
+++ b/app/views/shared/_issues.html.haml
@@ -1,6 +1,6 @@
- if @issues.any?
- @issues.group_by(&:project).each do |group|
- .ui-box.small-box
+ .ui-box.ui-box-small
- project = group[0]
.title
= link_to_project project
diff --git a/app/views/shared/_merge_requests.html.haml b/app/views/shared/_merge_requests.html.haml
index b7a7ca8fcc8..ddad28339c8 100644
--- a/app/views/shared/_merge_requests.html.haml
+++ b/app/views/shared/_merge_requests.html.haml
@@ -1,9 +1,10 @@
- if @merge_requests.any?
- @merge_requests.group_by(&:target_project).each do |group|
- .ui-box.small-box
+ .ui-box.ui-box-small
- project = group[0]
.title
= link_to_project project
+ = link_to 'show all', project_merge_requests_path(project), class: 'pull-right'
%ul.well-list.mr-list
- group[1].each do |merge_request|
= render 'projects/merge_requests/merge_request', merge_request: merge_request
diff --git a/app/views/shared/_no_ssh.html.haml b/app/views/shared/_no_ssh.html.haml
index 6d363807d62..077e6c6a808 100644
--- a/app/views/shared/_no_ssh.html.haml
+++ b/app/views/shared/_no_ssh.html.haml
@@ -1,3 +1,8 @@
-- if current_user.require_ssh_key? && alert.blank? && notice.blank?
- %p.error-message.centered
- You won't be able to pull or push project code via SSH until you #{link_to 'add an SSH key', new_profile_key_path} to your profile
+- if cookies[:hide_no_ssh_message].blank? && current_user.require_ssh_key? && !current_user.hide_no_ssh_key
+ .no-ssh-key-message
+ .container
+ You won't be able to pull or push project code via SSH until you #{link_to 'add an SSH key', new_profile_key_path} to your profile
+ %div.pull-right
+ = link_to "Don't show again", profile_path(user: {hide_no_ssh_key: true}), method: :put, class: 'hide-no-ssh-message', remote: true
+ |
+ = link_to 'Remind later', '#', class: 'hide-no-ssh-message'
diff --git a/app/views/shared/_project_filter.html.haml b/app/views/shared/_project_filter.html.haml
index f3d032ef986..0509cd41efc 100644
--- a/app/views/shared/_project_filter.html.haml
+++ b/app/views/shared/_project_filter.html.haml
@@ -1,32 +1,34 @@
-= form_tag project_entities_path, method: 'get' do
- %fieldset
+.side-filters.hidden-xs.hidden-sm
+ = form_tag project_entities_path, method: 'get' do
- if current_user
- %ul.nav.nav-pills.nav-stacked
- %li{class: ("active" if params[:scope].blank?)}
- = link_to project_filter_path(scope: nil) do
- Everyone's
- %li{class: ("active" if params[:scope] == 'assigned-to-me')}
- = link_to project_filter_path(scope: 'assigned-to-me') do
- Assigned to me
- %li{class: ("active" if params[:scope] == 'created-by-me')}
- = link_to project_filter_path(scope: 'created-by-me') do
- Created by me
+ %fieldset
+ %ul.nav.nav-pills.nav-stacked
+ %li{class: ("active" if params[:scope].blank?)}
+ = link_to project_filter_path(scope: nil) do
+ Everyone's
+ %li{class: ("active" if params[:scope] == 'assigned-to-me')}
+ = link_to project_filter_path(scope: 'assigned-to-me') do
+ Assigned to me
+ %li{class: ("active" if params[:scope] == 'created-by-me')}
+ = link_to project_filter_path(scope: 'created-by-me') do
+ Created by me
- %ul.nav.nav-pills.nav-stacked
- %li{class: ("active" if params[:state].blank?)}
- = link_to project_filter_path(state: nil) do
- Open
- %li{class: ("active" if params[:state] == 'closed')}
- = link_to project_filter_path(state: 'closed') do
- Closed
- %li{class: ("active" if params[:state] == 'all')}
- = link_to project_filter_path(state: 'all') do
- All
+ %fieldset
+ %ul.nav.nav-pills.nav-stacked
+ %li{class: ("active" if params[:state].blank?)}
+ = link_to project_filter_path(state: nil) do
+ Open
+ %li{class: ("active" if params[:state] == 'closed')}
+ = link_to project_filter_path(state: 'closed') do
+ Closed
+ %li{class: ("active" if params[:state] == 'all')}
+ = link_to project_filter_path(state: 'all') do
+ All
- %fieldset
- - if %w(state scope milestone_id assignee_id label_name).select { |k| params[k].present? }.any?
- = link_to project_entities_path, class: 'cgray pull-right' do
- %i.icon-remove
- %strong Clear filter
+ %fieldset
+ - if %w(state scope milestone_id assignee_id label_name).select { |k| params[k].present? }.any?
+ = link_to project_entities_path, class: 'cgray pull-right' do
+ %i.icon-remove
+ %strong Clear filter
diff --git a/app/views/shared/_ref_switcher.html.haml b/app/views/shared/_ref_switcher.html.haml
index dc8c656e12e..e02b615a4cd 100644
--- a/app/views/shared/_ref_switcher.html.haml
+++ b/app/views/shared/_ref_switcher.html.haml
@@ -1,5 +1,5 @@
= form_tag switch_project_refs_path(@project), method: :get, class: "project-refs-form" do
- = select_tag "ref", grouped_options_refs, class: "project-refs-select chosen"
+ = select_tag "ref", grouped_options_refs, class: "project-refs-select chosen chosen-sm"
= hidden_field_tag :destination, destination
- if defined?(path)
= hidden_field_tag :path, path
diff --git a/app/views/snippets/_blob.html.haml b/app/views/snippets/_blob.html.haml
index c2e0d97a117..e0152143a95 100644
--- a/app/views/snippets/_blob.html.haml
+++ b/app/views/snippets/_blob.html.haml
@@ -6,11 +6,20 @@
.btn-group.tree-btn-group.pull-right
- if @snippet.author == current_user
= link_to "Edit", edit_snippet_path(@snippet), class: "btn btn-tiny", title: 'Edit Snippet'
- = link_to "Delete", snippet_path(@snippet), method: :delete, confirm: "Are you sure?", class: "btn btn-tiny", title: 'Delete Snippet'
+ = link_to "Delete", snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-tiny", title: 'Delete Snippet'
= link_to "Raw", raw_snippet_path(@snippet), class: "btn btn-tiny", target: "_blank"
- .file-content.code
- - unless @snippet.content.empty?
- %div{class: user_color_scheme_class}
- = raw @snippet.colorize(formatter: :gitlab)
+ - unless @snippet.content.empty?
+ - if gitlab_markdown?(@snippet.file_name)
+ .file-content.wiki
+ = preserve do
+ = markdown(@snippet.data)
+ - elsif markup?(@snippet.file_name)
+ .file-content.wiki
+ = raw GitHub::Markup.render(@snippet.file_name, @snippet.data)
- else
+ .file-content.code
+ %div{class: user_color_scheme_class}
+ = raw @snippet.colorize(formatter: :gitlab)
+ - else
+ .file-content.code
%p.nothing_here_message Empty file
diff --git a/app/views/snippets/_form.html.haml b/app/views/snippets/_form.html.haml
index e77550e7be3..d466dc1af14 100644
--- a/app/views/snippets/_form.html.haml
+++ b/app/views/snippets/_form.html.haml
@@ -2,27 +2,38 @@
= @snippet.new_record? ? "New Snippet" : "Edit Snippet ##{@snippet.id}"
%hr
.snippet-form-holder
- = form_for @snippet, as: :personal_snippet, url: url do |f|
+ = form_for @snippet, as: :personal_snippet, url: url, html: { class: "form-horizontal snippet-form" } do |f|
-if @snippet.errors.any?
- .alert.alert-error
+ .alert.alert-danger
%ul
- @snippet.errors.full_messages.each do |msg|
%li= msg
- .control-group
- = f.label :title
- .controls= f.text_field :title, placeholder: "Example Snippet", class: 'input-xlarge', required: true
- .control-group
- = f.label "Private?"
- .controls
- = f.check_box :private, {class: ''}
- .control-group
+ .form-group
+ = f.label :title, class: 'control-label'
+ .col-sm-10= f.text_field :title, placeholder: "Example Snippet", class: 'form-control', required: true
+ .form-group
+ = f.label "Access", class: 'control-label'
+ .col-sm-10
+ = f.label :private_true, class: 'radio-label' do
+ = f.radio_button :private, true
+ %span
+ %strong Private
+ (only you can see this snippet)
+ %br
+ = f.label :private_false, class: 'radio-label' do
+ = f.radio_button :private, false
+ %span
+ %strong Public
+ (GitLab users can see this snippet)
+
+ .form-group
.file-editor
- = f.label :file_name, "File"
- .controls
+ = f.label :file_name, "File", class: 'control-label'
+ .col-sm-10
.file-holder.snippet
.file-title
- = f.text_field :file_name, placeholder: "example.rb", class: 'snippet-file-name', required: true
+ = f.text_field :file_name, placeholder: "example.rb", class: 'form-control snippet-file-name', required: true
.file-content.code
%pre#editor= @snippet.content
= f.hidden_field :content, class: 'snippet-file-content'
@@ -33,9 +44,10 @@
- else
= f.submit 'Save', class: "btn-save btn"
- = link_to "Cancel", snippets_path(@project), class: "btn btn-cancel"
- unless @snippet.new_record?
- .pull-right= link_to 'Destroy', snippet_path(@snippet), confirm: 'Removed snippet cannot be restored! Are you sure?', method: :delete, class: "btn pull-right danger delete-snippet", id: "destroy_snippet_#{@snippet.id}"
+ .pull-right.prepend-left-20
+ = link_to 'Remove', snippet_path(@snippet), data: { confirm: 'Removed snippet cannot be restored! Are you sure?'}, method: :delete, class: "btn btn-remove delete-snippet", id: "destroy_snippet_#{@snippet.id}"
+ = link_to "Cancel", snippets_path(@project), class: "btn btn-cancel"
:javascript
diff --git a/app/views/snippets/_snippet.html.haml b/app/views/snippets/_snippet.html.haml
index 8514bc3ddd0..e6f83167330 100644
--- a/app/views/snippets/_snippet.html.haml
+++ b/app/views/snippets/_snippet.html.haml
@@ -3,7 +3,7 @@
= link_to reliable_snippet_path(snippet) do
= truncate(snippet.title, length: 60)
- if snippet.private?
- %span.label.label-success
+ %span.label.label-gray
%i.icon-lock
private
%span.cgray.monospace.tiny.pull-right
@@ -20,4 +20,4 @@
= link_to user_snippets_path(snippet.author) do
= image_tag avatar_icon(snippet.author_email), class: "avatar avatar-inline s16", alt: ''
= snippet.author_name
- %span.light #{time_ago_in_words(snippet.created_at)} ago
+ %span.light #{time_ago_with_tooltip(snippet.created_at)}
diff --git a/app/views/snippets/current_user_index.html.haml b/app/views/snippets/current_user_index.html.haml
index 51030f965a1..bf712b2c7e7 100644
--- a/app/views/snippets/current_user_index.html.haml
+++ b/app/views/snippets/current_user_index.html.haml
@@ -11,7 +11,7 @@
%hr
.row
- .span3
+ .col-md-3
%ul.nav.nav-pills.nav-stacked
= nav_tab :scope, nil do
= link_to user_snippets_path(@user) do
@@ -29,6 +29,6 @@
%span.pull-right
= @user.snippets.public.count
- .span9.my-snippets
+ .col-md-9.my-snippets
= render 'snippets'
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 53a0a9232a4..566bbdb9311 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -1,5 +1,5 @@
.row
- .span8
+ .col-md-8
%h3.page-title
= image_tag avatar_icon(@user.email, 90), class: "avatar s90", alt: ''
= @user.name
@@ -16,6 +16,6 @@
%hr
%h4 User Activity:
= render @events
- .span4
+ .col-md-4
= render 'profile', user: @user
= render 'projects', user: @user
diff --git a/app/views/users_groups/_users_group.html.haml b/app/views/users_groups/_users_group.html.haml
index 5934ff72e8e..5f477f3c976 100644
--- a/app/views/users_groups/_users_group.html.haml
+++ b/app/views/users_groups/_users_group.html.haml
@@ -13,7 +13,7 @@
- if show_controls && can?(current_user, :manage_group, @group) && current_user != user
= link_to '#', class: "btn-tiny btn js-toggle-button", title: 'Edit access level' do
%i.icon-edit
- = link_to group_users_group_path(@group, member), confirm: remove_user_from_group_message(@group, user), method: :delete, remote: true, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do
+ = link_to group_users_group_path(@group, member), data: { confirm: remove_user_from_group_message(@group, user) }, method: :delete, remote: true, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do
%i.icon-minus.icon-white
.edit-member.hide.js-toggle-content
diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml
index bded53b2f21..788d9065a7b 100644
--- a/app/views/votes/_votes_block.html.haml
+++ b/app/views/votes/_votes_block.html.haml
@@ -1,6 +1,6 @@
.votes.votes-block
.progress
- .bar.bar-success{style: "width: #{votable.upvotes_in_percent}%;"}
- .bar.bar-danger{style: "width: #{votable.downvotes_in_percent}%;"}
+ .progress-bar.progress-bar-success{style: "width: #{votable.upvotes_in_percent}%;"}
+ .progress-bar.progress-bar-danger{style: "width: #{votable.downvotes_in_percent}%;"}
.upvotes= "#{votable.upvotes} up"
.downvotes= "#{votable.downvotes} down"
diff --git a/app/workers/emails_on_push_worker.rb b/app/workers/emails_on_push_worker.rb
new file mode 100644
index 00000000000..9982b362a10
--- /dev/null
+++ b/app/workers/emails_on_push_worker.rb
@@ -0,0 +1,25 @@
+class EmailsOnPushWorker
+ include Sidekiq::Worker
+
+ def perform(project_id, recipients, push_data)
+ project = Project.find(project_id)
+ before_sha = push_data["before"]
+ after_sha = push_data["after"]
+ branch = push_data["ref"]
+ author_id = push_data["user_id"]
+
+ if before_sha =~ /^000000/ || after_sha =~ /^000000/
+ # skip if new branch was pushed or branch was removed
+ return true
+ end
+
+ compare = Gitlab::Git::Compare.new(project.repository.raw_repository, before_sha, after_sha)
+
+ # Do not send emails if git compare failed
+ return false unless compare && compare.commits.present?
+
+ recipients.split(" ").each do |recipient|
+ Notify.delay.repository_push_email(project_id, recipient, author_id, branch, compare)
+ end
+ end
+end
diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb
index a3b4bd0c9b5..95b80bca7c0 100644
--- a/app/workers/repository_import_worker.rb
+++ b/app/workers/repository_import_worker.rb
@@ -14,7 +14,6 @@ class RepositoryImportWorker
project.imported = true
project.save
project.satellite.create unless project.satellite.exists?
- project.discover_default_branch
else
project.imported = false
end
diff --git a/config.ru b/config.ru
index dfd2d862237..c45449dd03f 100644
--- a/config.ru
+++ b/config.ru
@@ -1,5 +1,14 @@
# This file is used by Rack-based servers to start the application.
+unless defined?(PhusionPassenger)
+ require 'unicorn'
+ # Unicorn self-process killer
+ require 'unicorn/worker_killer'
+
+ # Max memory size (RSS) per worker
+ use Unicorn::WorkerKiller::Oom, (200 * (1 << 20)), (250 * (1 << 20))
+end
+
require ::File.expand_path('../config/environment', __FILE__)
map ENV['RAILS_RELATIVE_URL_ROOT'] || "/" do
diff --git a/config/application.rb b/config/application.rb
index d85bcab7885..1c91134f524 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -1,13 +1,9 @@
require File.expand_path('../boot', __FILE__)
require 'rails/all'
+require 'devise'
-if defined?(Bundler)
- # If you precompile assets before deploying to production, use this line
- # Bundler.require(*Rails.groups(assets: %w(development test)))
- # If you want your assets lazily compiled in production, use this line
- Bundler.require(:default, :assets, Rails.env)
-end
+Bundler.require(:default, Rails.env)
module Gitlab
class Application < Rails::Application
@@ -16,7 +12,7 @@ module Gitlab
# -- all .rb files in that directory are automatically loaded.
# Custom directories with classes and modules you want to be autoloadable.
- config.autoload_paths += %W(#{config.root}/lib #{config.root}/app/models/concerns)
+ config.autoload_paths += %W(#{config.root}/lib #{config.root}/app/models/concerns #{config.root}/app/models/project_services)
# Only load the plugins named here, in the order given (default is alphabetical).
# :all can be used as a placeholder for all plugins not explicitly named.
@@ -70,15 +66,24 @@ module Gitlab
config.assets.version = '1.0'
# Uncomment and customize the last line to run in a non-root path
- # WARNING: This feature is known to work, but unsupported
- # Note that three settings need to be changed for this to work.
+ # WARNING: We recommend creating a FQDN to host GitLab in a root path instead of this.
+ # Note that four settings need to be changed for this to work.
# 1) In your application.rb file: config.relative_url_root = "/gitlab"
# 2) In your gitlab.yml file: relative_url_root: /gitlab
# 3) In your unicorn.rb: ENV['RAILS_RELATIVE_URL_ROOT'] = "/gitlab"
+ # 4) In ../gitlab-shell/config.yml: gitlab_url: "http://127.0.0.1/gitlab"
+ # To update the path, run: sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
#
# config.relative_url_root = "/gitlab"
- # Uncomment to enable rack attack middleware
- # config.middleware.use Rack::Attack
+ config.middleware.use Rack::Attack
+
+ # Allow access to GitLab API from other domains
+ config.middleware.use Rack::Cors do
+ allow do
+ origins '*'
+ resource '/api/*', headers: :any, methods: [:get, :post, :options, :put, :delete]
+ end
+ end
end
end
diff --git a/config/database.yml.mysql b/config/database.yml.mysql
index e7a9227e41e..55ac088bc1d 100644
--- a/config/database.yml.mysql
+++ b/config/database.yml.mysql
@@ -7,7 +7,7 @@ production:
reconnect: false
database: gitlabhq_production
pool: 10
- username: gitlab
+ username: git
password: "secure password"
# host: localhost
# socket: /tmp/mysql.sock
diff --git a/config/environments/development.rb b/config/environments/development.rb
index 6cba17f6ea2..e4c7649fda0 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -6,9 +6,6 @@ Gitlab::Application.configure do
# since you don't have to restart the web server when you make code changes.
config.cache_classes = false
- # Log error messages when you accidentally call methods on nil.
- config.whiny_nils = true
-
# Show full error reports and disable caching
config.consider_all_requests_local = true
config.action_controller.perform_caching = false
@@ -25,10 +22,6 @@ Gitlab::Application.configure do
# Raise exception on mass assignment protection for Active Record models
config.active_record.mass_assignment_sanitizer = :strict
- # Log the query plan for queries taking more than this (works
- # with SQLite, MySQL, and PostgreSQL)
- config.active_record.auto_explain_threshold_in_seconds = 0.5
-
# Do not compress assets
config.assets.compress = false
@@ -39,4 +32,6 @@ Gitlab::Application.configure do
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
# Open sent mails in browser
config.action_mailer.delivery_method = :letter_opener
+
+ config.eager_load = false
end
diff --git a/config/environments/production.rb b/config/environments/production.rb
index e3476be8fba..9ac4622abc2 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -80,4 +80,9 @@ Gitlab::Application.configure do
# # }
config.action_mailer.perform_deliveries = true
config.action_mailer.raise_delivery_errors = true
+
+ config.eager_load = true
+ config.assets.js_compressor = :uglifier
+
+ config.allow_concurrency = false
end
diff --git a/config/environments/test.rb b/config/environments/test.rb
index b626986299b..3860dc5c74c 100644
--- a/config/environments/test.rb
+++ b/config/environments/test.rb
@@ -11,9 +11,6 @@ Gitlab::Application.configure do
config.serve_static_assets = true
config.static_cache_control = "public, max-age=3600"
- # Log error messages when you accidentally call methods on nil
- config.whiny_nils = true
-
# Show full error reports and disable caching
config.consider_all_requests_local = true
config.action_controller.perform_caching = false
@@ -34,4 +31,6 @@ Gitlab::Application.configure do
# Print deprecation notices to the stderr
config.active_support.deprecation = :stderr
+
+ config.eager_load = false
end
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 163af226aaa..2bc984c9294 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -20,11 +20,13 @@ production: &base
https: false
# Uncomment and customize the last line to run in a non-root path
- # WARNING: This feature is known to work, but unsupported
- # Note that three settings need to be changed for this to work.
+ # WARNING: We recommend creating a FQDN to host GitLab in a root path instead of this.
+ # Note that four settings need to be changed for this to work.
# 1) In your application.rb file: config.relative_url_root = "/gitlab"
# 2) In your gitlab.yml file: relative_url_root: /gitlab
# 3) In your unicorn.rb: ENV['RAILS_RELATIVE_URL_ROOT'] = "/gitlab"
+ # 4) In ../gitlab-shell/config.yml: gitlab_url: "http://127.0.0.1/gitlab"
+ # To update the path, run: sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
#
# relative_url_root: /gitlab
@@ -55,11 +57,15 @@ production: &base
# default: false - Account passwords are not sent via the email if signup is enabled.
# signup_enabled: true
+ # Restrict setting visibility levels for non-admin users.
+ # The default is to allow all levels.
+ #restricted_visibility_levels: [ "public" ]
+
## Automatic issue closing
# If a commit message matches this regular expression, all issues referenced from the matched text will be closed.
- # This happends when the commit is pushed or merged into the default branch of a project.
+ # This happens when the commit is pushed or merged into the default branch of a project.
# When not specified the default issue_closing_pattern as specified below will be used.
- # issue_closing_pattern: ([Cc]loses|[Ff]ixes) +#\d+
+ # issue_closing_pattern: ([Cc]lose[sd]|[Ff]ixe[sd]) +#\d+
## Default project features settings
default_projects_features:
@@ -68,7 +74,7 @@ production: &base
wiki: true
wall: false
snippets: false
- public: false
+ visibility_level: "private" # can be "private" | "internal" | "public"
## External issues trackers
issues_tracker:
@@ -110,6 +116,8 @@ production: &base
# ==========================
## LDAP settings
+ # You can inspect the first 100 LDAP users with login access by running:
+ # bundle exec rake gitlab:ldap:check[100] RAILS_ENV=production
ldap:
enabled: false
host: '_your_ldap_server'
@@ -136,7 +144,7 @@ production: &base
## Auth providers
# Uncomment the following lines and fill in the data of the auth provider you want to use
# If your favorite auth provider is not listed you can use others:
- # see https://github.com/gitlabhq/gitlabhq/wiki/Using-Custom-Omniauth-Providers
+ # see https://github.com/gitlabhq/gitlab-public-wiki/wiki/Working-custom-omniauth-provider-configurations
# The 'app_id' and 'app_secret' parameters are always passed as the first two
# arguments, followed by optional 'args' which can be either a hash or an array.
providers:
@@ -166,6 +174,8 @@ production: &base
## GitLab Shell settings
gitlab_shell:
+ path: /home/git/gitlab-shell/
+
# REPOS_PATH MUST NOT BE A SYMLINK!!!
repos_path: /home/git/repositories/
hooks_path: /home/git/gitlab-shell/hooks/
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 942b77ffd2e..ea391ca601c 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -30,6 +30,29 @@ class Settings < Settingslogic
gitlab.relative_url_root
].join('')
end
+
+ # check that values in `current` (string or integer) is a contant in `modul`.
+ def verify_constant_array(modul, current, default)
+ values = default || []
+ if !current.nil?
+ values = []
+ current.each do |constant|
+ values.push(verify_constant(modul, constant, nil))
+ end
+ values.delete_if { |value| value.nil? }
+ end
+ values
+ end
+
+ # check that `current` (string or integer) is a contant in `modul`.
+ def verify_constant(modul, current, default)
+ constant = modul.constants.find{ |name| modul.const_get(name) == current }
+ value = constant.nil? ? default : modul.const_get(constant)
+ if current.is_a? String
+ value = modul.const_get(current.upcase) rescue default
+ end
+ value
+ end
end
end
@@ -68,6 +91,7 @@ rescue ArgumentError # no user configured
'/home/' + Settings.gitlab['user']
end
Settings.gitlab['signup_enabled'] ||= false
+Settings.gitlab['restricted_visibility_levels'] = Settings.send(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], [])
Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil?
Settings.gitlab['issue_closing_pattern'] = '([Cc]loses|[Ff]ixes) #(\d+)' if Settings.gitlab['issue_closing_pattern'].nil?
Settings.gitlab['default_projects_features'] ||= {}
@@ -76,7 +100,7 @@ Settings.gitlab.default_projects_features['merge_requests'] = true if Settings.g
Settings.gitlab.default_projects_features['wiki'] = true if Settings.gitlab.default_projects_features['wiki'].nil?
Settings.gitlab.default_projects_features['wall'] = false if Settings.gitlab.default_projects_features['wall'].nil?
Settings.gitlab.default_projects_features['snippets'] = false if Settings.gitlab.default_projects_features['snippets'].nil?
-Settings.gitlab.default_projects_features['public'] = false if Settings.gitlab.default_projects_features['public'].nil?
+Settings.gitlab.default_projects_features['visibility_level'] = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE)
#
# Gravatar
@@ -90,6 +114,7 @@ Settings.gravatar['ssl_url'] ||= 'https://secure.gravatar.com/avatar/%{hash}?
# GitLab Shell
#
Settings['gitlab_shell'] ||= Settingslogic.new({})
+Settings.gitlab_shell['path'] ||= Settings.gitlab['user_home'] + '/gitlab-shell/'
Settings.gitlab_shell['hooks_path'] ||= Settings.gitlab['user_home'] + '/gitlab-shell/hooks/'
Settings.gitlab_shell['receive_pack'] = true if Settings.gitlab_shell['receive_pack'].nil?
Settings.gitlab_shell['upload_pack'] = true if Settings.gitlab_shell['upload_pack'].nil?
diff --git a/config/initializers/carrierwave.rb b/config/initializers/carrierwave.rb
index 45bc68f3220..6875fa74edd 100644
--- a/config/initializers/carrierwave.rb
+++ b/config/initializers/carrierwave.rb
@@ -15,5 +15,7 @@ if File.exists?(aws_file)
config.fog_directory = AWS_CONFIG['bucket'] # required
config.fog_public = false # optional, defaults to true
config.fog_attributes = {'Cache-Control'=>'max-age=315576000'} # optional, defaults to {}
+ config.fog_authenticated_url_expiration = 1 << 29 # optional time (in seconds) that authenticated urls will be valid.
+ # when fog_public is false and provider is AWS or Google, defaults to 600
end
end
diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb
index b7cb808d2e5..25390978cf6 100644
--- a/config/initializers/devise.rb
+++ b/config/initializers/devise.rb
@@ -6,6 +6,7 @@ Devise.setup do |config|
# note that it will be overwritten if you use your own mailer class with default "from" parameter.
config.mailer_sender = Gitlab.config.gitlab.email_from
+
# Configure the class responsible to send e-mails.
# config.mailer = "Devise::Mailer"
@@ -74,8 +75,8 @@ Devise.setup do |config|
# config.pepper = "2ef62d549c4ff98a5d3e0ba211e72cff592060247e3bbbb9f499af1222f876f53d39b39b823132affb32858168c79c1d7741d26499901b63c6030a42129924ef"
# ==> Configuration for :confirmable
- # The time you want to give your user to confirm his account. During this time
- # he will be able to access your application without confirming. Default is 0.days
+ # The time you want to give a user to confirm their account. During this time
+ # they will be able to access your application without confirming. Default is 0.days
# When confirm_within is zero, the user won't be able to sign in without confirming.
# You can use this to let your user access some features of your application
# without confirming the account, but blocking it after a certain period
@@ -101,7 +102,7 @@ Devise.setup do |config|
# ==> Configuration for :validatable
# Range for password length. Default is 6..128.
- config.password_length = 6..128
+ config.password_length = 8..128
# Email regex used to validate email formats. It simply asserts that
# an one (and only one) @ exists in the given string. This is mainly
diff --git a/config/initializers/gemoji.rb b/config/initializers/gemoji.rb
index 8c85aad5d3b..6cc33aced77 100644
--- a/config/initializers/gemoji.rb
+++ b/config/initializers/gemoji.rb
@@ -1,2 +1,3 @@
# Workaround for https://github.com/github/gemoji/pull/18
+require 'gemoji'
Gitlab::Application.config.assets.paths << Emoji.images_path
diff --git a/config/initializers/rack_attack.rb.example b/config/initializers/rack_attack.rb.example
index 76fa7ad282e..bc3234bf0b6 100644
--- a/config/initializers/rack_attack.rb.example
+++ b/config/initializers/rack_attack.rb.example
@@ -1,16 +1,18 @@
-# To enable rack-attack for your GitLab instance do the following:
-# 1. In config/application.rb find and uncomment the following line:
-# config.middleware.use Rack::Attack
-# 2. Rename this file to rack_attack.rb
-# 3. Review the paths_to_be_protected and add any other path you need protecting
-# 4. Restart GitLab instance
+# 1. Rename this file to rack_attack.rb
+# 2. Review the paths_to_be_protected and add any other path you need protecting
#
paths_to_be_protected = [
"#{Rails.application.config.relative_url_root}/users/password",
"#{Rails.application.config.relative_url_root}/users/sign_in",
- "#{Rails.application.config.relative_url_root}/users"
+ "#{Rails.application.config.relative_url_root}/api/#{API::API.version}/session.json",
+ "#{Rails.application.config.relative_url_root}/api/#{API::API.version}/session",
+ "#{Rails.application.config.relative_url_root}/users",
+ "#{Rails.application.config.relative_url_root}/users/confirmation"
]
-Rack::Attack.throttle('protected paths', limit: 6, period: 60.seconds) do |req|
- req.ip if paths_to_be_protected.include?(req.path) && req.post?
+
+unless Rails.env.test?
+ Rack::Attack.throttle('protected paths', limit: 10, period: 60.seconds) do |req|
+ req.ip if paths_to_be_protected.include?(req.path) && req.post?
+ end
end
diff --git a/config/initializers/secret_token.rb b/config/initializers/secret_token.rb
index 16d1d4a9fdd..98400290113 100644
--- a/config/initializers/secret_token.rb
+++ b/config/initializers/secret_token.rb
@@ -21,3 +21,4 @@ def find_secure_token
end
Gitlab::Application.config.secret_token = find_secure_token
+Gitlab::Application.config.secret_key_base = find_secure_token
diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb
index 501cad4a838..f80b67a554b 100644
--- a/config/initializers/session_store.rb
+++ b/config/initializers/session_store.rb
@@ -2,8 +2,9 @@
Gitlab::Application.config.session_store(
:redis_store, # Using the cookie_store would enable session replay attacks.
+ servers: Gitlab::Application.config.cache_store.last, # re-use the Redis config from the Rails cache store
key: '_gitlab_session',
- secure: Gitlab::Application.config.force_ssl,
+ secure: Gitlab.config.gitlab.https,
httponly: true,
path: (Rails.application.config.relative_url_root.nil?) ? '/' : Rails.application.config.relative_url_root
)
diff --git a/config/routes.rb b/config/routes.rb
index 78f75d11835..734421ede1d 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -22,7 +22,7 @@ Gitlab::Application.routes.draw do
project_root: Gitlab.config.gitlab_shell.repos_path,
upload_pack: Gitlab.config.gitlab_shell.upload_pack,
receive_pack: Gitlab.config.gitlab_shell.receive_pack
- }), at: '/', constraints: lambda { |request| /[-\/\w\.]+\.git\//.match(request.path_info) }
+ }), at: '/', constraints: lambda { |request| /[-\/\w\.]+\.git\//.match(request.path_info) }, via: [:get, :post]
#
# Help
@@ -86,9 +86,16 @@ Gitlab::Application.routes.draw do
get :test
end
+ resources :broadcast_messages, only: [:index, :create, :destroy]
resource :logs, only: [:show]
resource :background_jobs, controller: 'background_jobs', only: [:show]
- resources :projects, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ }, only: [:index, :show]
+
+ resources :projects, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ }, only: [:index, :show] do
+ member do
+ put :transfer
+ end
+ end
+
root to: "dashboard#index"
end
@@ -120,10 +127,11 @@ Gitlab::Application.routes.draw do
delete :leave
end
end
+ resource :avatar, only: [:destroy]
end
end
- match "/u/:username" => "users#show", as: :user, constraints: { username: /.*/ }
+ match "/u/:username" => "users#show", as: :user, constraints: { username: /.*/ }, via: :get
@@ -162,20 +170,24 @@ Gitlab::Application.routes.draw do
member do
put :transfer
post :fork
+ post :archive
+ post :unarchive
get :autocomplete_sources
end
scope module: :projects do
- resources :blob, only: [:show], constraints: {id: /.+/}
- resources :raw, only: [:show], constraints: {id: /.+/}
- resources :tree, only: [:show], constraints: {id: /.+/, format: /(html|js)/ }
- resources :edit_tree, only: [:show, :update], constraints: {id: /.+/}, path: 'edit'
- resources :commit, only: [:show], constraints: {id: /[[:alnum:]]{6,40}/}
- resources :commits, only: [:show], constraints: {id: /(?:[^.]|\.(?!atom$))+/, format: /atom/}
- resources :compare, only: [:index, :create]
- resources :blame, only: [:show], constraints: {id: /.+/}
+ resources :blob, only: [:show, :destroy], constraints: {id: /.+/}
+ resources :raw, only: [:show], constraints: {id: /.+/}
+ resources :tree, only: [:show], constraints: {id: /.+/, format: /(html|js)/ }
+ resources :edit_tree, only: [:show, :update], constraints: {id: /.+/}, path: 'edit'
+ resources :new_tree, only: [:show, :update], constraints: {id: /.+/}, path: 'new'
+ resources :commit, only: [:show], constraints: {id: /[[:alnum:]]{6,40}/}
+ resources :commits, only: [:show], constraints: {id: /(?:[^.]|\.(?!atom$))+/, format: /atom/}
+ resources :compare, only: [:index, :create]
+ resources :blame, only: [:show], constraints: {id: /.+/}
resources :network, only: [:show], constraints: {id: /(?:[^.]|\.(?!json$))+/, format: /json/}
- resources :graphs, only: [:show], constraints: {id: /(?:[^.]|\.(?!json$))+/, format: /json/}
+ resources :graphs, only: [:show], constraints: {id: /(?:[^.]|\.(?!json$))+/, format: /json/}
+
match "/compare/:from...:to" => "compare#show", as: "compare", via: [:get, :post], constraints: {from: /.+/, to: /.+/}
resources :snippets, constraints: {id: /\d+/} do
@@ -205,7 +217,7 @@ Gitlab::Application.routes.draw do
resource :repository, only: [:show] do
member do
get "stats"
- get "archive"
+ get "archive", constraints: { format: Gitlab::Regex.archive_formats_regex }
end
end
@@ -286,6 +298,7 @@ Gitlab::Application.routes.draw do
resources :team_members, except: [:index, :edit], constraints: { id: /[a-zA-Z.\/0-9_\-#%+]+/ } do
collection do
+ delete :leave
# Used for import team
# from another project
diff --git a/config/unicorn.rb.example b/config/unicorn.rb.example
index e4e13426831..ba5e5cdde0b 100644
--- a/config/unicorn.rb.example
+++ b/config/unicorn.rb.example
@@ -9,11 +9,13 @@
# documentation.
# Uncomment and customize the last line to run in a non-root path
-# WARNING: This feature is known to work, but unsupported
-# Note that three settings need to be changed for this to work.
+# WARNING: We recommend creating a FQDN to host GitLab in a root path instead of this.
+# Note that four settings need to be changed for this to work.
# 1) In your application.rb file: config.relative_url_root = "/gitlab"
# 2) In your gitlab.yml file: relative_url_root: /gitlab
# 3) In your unicorn.rb: ENV['RAILS_RELATIVE_URL_ROOT'] = "/gitlab"
+# 4) In ../gitlab-shell/config.yml: gitlab_url: "http://127.0.0.1/gitlab"
+# To update the path, run: sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
#
# ENV['RAILS_RELATIVE_URL_ROOT'] = "/gitlab"
diff --git a/db/fixtures/development/05_users.rb b/db/fixtures/development/05_users.rb
index cbb3e636acc..3440a645408 100644
--- a/db/fixtures/development/05_users.rb
+++ b/db/fixtures/development/05_users.rb
@@ -1,5 +1,5 @@
Gitlab::Seeder.quiet do
- (2..50).each do |i|
+ (2..10).each do |i|
begin
User.seed(:id, [{
id: i,
diff --git a/db/fixtures/development/09_issues.rb b/db/fixtures/development/09_issues.rb
index 85e309b6335..2b81d7a2597 100644
--- a/db/fixtures/development/09_issues.rb
+++ b/db/fixtures/development/09_issues.rb
@@ -6,7 +6,7 @@ Gitlab::Seeder.quiet do
project = Project.all.sample
# Random user
- user = project.users.sample
+ user = project.team.users.sample
next unless user
diff --git a/db/fixtures/development/10_merge_requests.rb b/db/fixtures/development/10_merge_requests.rb
index 75f6e56985d..2b1f4160211 100644
--- a/db/fixtures/development/10_merge_requests.rb
+++ b/db/fixtures/development/10_merge_requests.rb
@@ -6,7 +6,7 @@ Gitlab::Seeder.quiet do
project = Project.all.sample
# Random user
- user = project.users.sample
+ user = project.team.users.sample
next unless user
diff --git a/db/migrate/20131106151520_remove_default_branch.rb b/db/migrate/20131106151520_remove_default_branch.rb
new file mode 100644
index 00000000000..88a890eb3eb
--- /dev/null
+++ b/db/migrate/20131106151520_remove_default_branch.rb
@@ -0,0 +1,9 @@
+class RemoveDefaultBranch < ActiveRecord::Migration
+ def up
+ remove_column :projects, :default_branch
+ end
+
+ def down
+ add_column :projects, :default_branch, :string
+ end
+end
diff --git a/db/migrate/20131112114325_create_broadcast_messages.rb b/db/migrate/20131112114325_create_broadcast_messages.rb
new file mode 100644
index 00000000000..147178e9dcf
--- /dev/null
+++ b/db/migrate/20131112114325_create_broadcast_messages.rb
@@ -0,0 +1,12 @@
+class CreateBroadcastMessages < ActiveRecord::Migration
+ def change
+ create_table :broadcast_messages do |t|
+ t.text :message, null: false
+ t.datetime :starts_at
+ t.datetime :ends_at
+ t.integer :alert_type
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20131112220935_add_visibility_level_to_projects.rb b/db/migrate/20131112220935_add_visibility_level_to_projects.rb
new file mode 100644
index 00000000000..cf1e9f912a0
--- /dev/null
+++ b/db/migrate/20131112220935_add_visibility_level_to_projects.rb
@@ -0,0 +1,13 @@
+class AddVisibilityLevelToProjects < ActiveRecord::Migration
+ def self.up
+ add_column :projects, :visibility_level, :integer, :default => 0, :null => false
+ Project.where(public: true).update_all(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+ remove_column :projects, :public
+ end
+
+ def self.down
+ add_column :projects, :public, :boolean, :default => false, :null => false
+ Project.where(visibility_level: Gitlab::VisibilityLevel::PUBLIC).update_all(public: true)
+ remove_column :projects, :visibility_level
+ end
+end
diff --git a/db/migrate/20131129154016_add_archived_to_projects.rb b/db/migrate/20131129154016_add_archived_to_projects.rb
new file mode 100644
index 00000000000..917e690ba47
--- /dev/null
+++ b/db/migrate/20131129154016_add_archived_to_projects.rb
@@ -0,0 +1,5 @@
+class AddArchivedToProjects < ActiveRecord::Migration
+ def change
+ add_column :projects, :archived, :boolean, default: false, null: false
+ end
+end
diff --git a/db/migrate/20131130165425_add_color_and_font_to_broadcast_messages.rb b/db/migrate/20131130165425_add_color_and_font_to_broadcast_messages.rb
new file mode 100644
index 00000000000..473f355eceb
--- /dev/null
+++ b/db/migrate/20131130165425_add_color_and_font_to_broadcast_messages.rb
@@ -0,0 +1,6 @@
+class AddColorAndFontToBroadcastMessages < ActiveRecord::Migration
+ def change
+ add_column :broadcast_messages, :color, :string
+ add_column :broadcast_messages, :font, :string
+ end
+end
diff --git a/db/migrate/20131202192556_add_event_fields_for_web_hook.rb b/db/migrate/20131202192556_add_event_fields_for_web_hook.rb
new file mode 100644
index 00000000000..d29e996852e
--- /dev/null
+++ b/db/migrate/20131202192556_add_event_fields_for_web_hook.rb
@@ -0,0 +1,7 @@
+class AddEventFieldsForWebHook < ActiveRecord::Migration
+ def change
+ add_column :web_hooks, :push_events, :boolean, default: true, null: false
+ add_column :web_hooks, :issues_events, :boolean, default: false, null: false
+ add_column :web_hooks, :merge_requests_events, :boolean, default: false, null: false
+ end
+end
diff --git a/db/migrate/20131214224427_add_hide_no_ssh_key_to_users.rb b/db/migrate/20131214224427_add_hide_no_ssh_key_to_users.rb
new file mode 100644
index 00000000000..7cec79e7ee8
--- /dev/null
+++ b/db/migrate/20131214224427_add_hide_no_ssh_key_to_users.rb
@@ -0,0 +1,5 @@
+class AddHideNoSshKeyToUsers < ActiveRecord::Migration
+ def change
+ add_column :users, :hide_no_ssh_key, :boolean, :default => false
+ end
+end
diff --git a/db/migrate/20131217102743_add_recipients_to_service.rb b/db/migrate/20131217102743_add_recipients_to_service.rb
new file mode 100644
index 00000000000..9695c251352
--- /dev/null
+++ b/db/migrate/20131217102743_add_recipients_to_service.rb
@@ -0,0 +1,5 @@
+class AddRecipientsToService < ActiveRecord::Migration
+ def change
+ add_column :services, :recipients, :text
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index d6acb2f90e9..e02799e0dbc 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -9,55 +9,66 @@
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
# you'll amass, the slower it'll run and the greater likelihood for issues).
#
-# It's strongly recommended to check this file into your version control system.
-
-ActiveRecord::Schema.define(:version => 20131009115346) do
+# It's strongly recommended that you check this file into your version control system.
+
+ActiveRecord::Schema.define(version: 20131217102743) do
+
+ create_table "broadcast_messages", force: true do |t|
+ t.text "message", null: false
+ t.datetime "starts_at"
+ t.datetime "ends_at"
+ t.integer "alert_type"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.string "color"
+ t.string "font"
+ end
- create_table "deploy_keys_projects", :force => true do |t|
- t.integer "deploy_key_id", :null => false
- t.integer "project_id", :null => false
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
+ create_table "deploy_keys_projects", force: true do |t|
+ t.integer "deploy_key_id", null: false
+ t.integer "project_id", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
end
- add_index "deploy_keys_projects", ["project_id"], :name => "index_deploy_keys_projects_on_project_id"
+ add_index "deploy_keys_projects", ["project_id"], name: "index_deploy_keys_projects_on_project_id", using: :btree
- create_table "events", :force => true do |t|
+ create_table "events", force: true do |t|
t.string "target_type"
t.integer "target_id"
t.string "title"
t.text "data"
t.integer "project_id"
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
t.integer "action"
t.integer "author_id"
end
- add_index "events", ["action"], :name => "index_events_on_action"
- add_index "events", ["author_id"], :name => "index_events_on_author_id"
- add_index "events", ["created_at"], :name => "index_events_on_created_at"
- add_index "events", ["project_id"], :name => "index_events_on_project_id"
- add_index "events", ["target_id"], :name => "index_events_on_target_id"
- add_index "events", ["target_type"], :name => "index_events_on_target_type"
-
- create_table "forked_project_links", :force => true do |t|
- t.integer "forked_to_project_id", :null => false
- t.integer "forked_from_project_id", :null => false
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
+ add_index "events", ["action"], name: "index_events_on_action", using: :btree
+ add_index "events", ["author_id"], name: "index_events_on_author_id", using: :btree
+ add_index "events", ["created_at"], name: "index_events_on_created_at", using: :btree
+ add_index "events", ["project_id"], name: "index_events_on_project_id", using: :btree
+ add_index "events", ["target_id"], name: "index_events_on_target_id", using: :btree
+ add_index "events", ["target_type"], name: "index_events_on_target_type", using: :btree
+
+ create_table "forked_project_links", force: true do |t|
+ t.integer "forked_to_project_id", null: false
+ t.integer "forked_from_project_id", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
end
- add_index "forked_project_links", ["forked_to_project_id"], :name => "index_forked_project_links_on_forked_to_project_id", :unique => true
+ add_index "forked_project_links", ["forked_to_project_id"], name: "index_forked_project_links_on_forked_to_project_id", unique: true, using: :btree
- create_table "issues", :force => true do |t|
+ create_table "issues", force: true do |t|
t.string "title"
t.integer "assignee_id"
t.integer "author_id"
t.integer "project_id"
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
- t.integer "position", :default => 0
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.integer "position", default: 0
t.string "branch_name"
t.text "description"
t.integer "milestone_id"
@@ -65,174 +76,175 @@ ActiveRecord::Schema.define(:version => 20131009115346) do
t.integer "iid"
end
- add_index "issues", ["assignee_id"], :name => "index_issues_on_assignee_id"
- add_index "issues", ["author_id"], :name => "index_issues_on_author_id"
- add_index "issues", ["created_at"], :name => "index_issues_on_created_at"
- add_index "issues", ["milestone_id"], :name => "index_issues_on_milestone_id"
- add_index "issues", ["project_id"], :name => "index_issues_on_project_id"
- add_index "issues", ["title"], :name => "index_issues_on_title"
+ add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree
+ add_index "issues", ["author_id"], name: "index_issues_on_author_id", using: :btree
+ add_index "issues", ["created_at"], name: "index_issues_on_created_at", using: :btree
+ add_index "issues", ["milestone_id"], name: "index_issues_on_milestone_id", using: :btree
+ add_index "issues", ["project_id"], name: "index_issues_on_project_id", using: :btree
+ add_index "issues", ["title"], name: "index_issues_on_title", using: :btree
- create_table "keys", :force => true do |t|
+ create_table "keys", force: true do |t|
t.integer "user_id"
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
t.text "key"
t.string "title"
t.string "type"
t.string "fingerprint"
end
- add_index "keys", ["user_id"], :name => "index_keys_on_user_id"
+ add_index "keys", ["user_id"], name: "index_keys_on_user_id", using: :btree
- create_table "merge_requests", :force => true do |t|
- t.string "target_branch", :null => false
- t.string "source_branch", :null => false
- t.integer "source_project_id", :null => false
+ create_table "merge_requests", force: true do |t|
+ t.string "target_branch", null: false
+ t.string "source_branch", null: false
+ t.integer "source_project_id", null: false
t.integer "author_id"
t.integer "assignee_id"
t.string "title"
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
- t.text "st_commits", :limit => 2147483647
- t.text "st_diffs", :limit => 2147483647
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.text "st_commits", limit: 2147483647
+ t.text "st_diffs", limit: 2147483647
t.integer "milestone_id"
t.string "state"
t.string "merge_status"
- t.integer "target_project_id", :null => false
+ t.integer "target_project_id", null: false
t.integer "iid"
t.text "description"
end
- add_index "merge_requests", ["assignee_id"], :name => "index_merge_requests_on_assignee_id"
- add_index "merge_requests", ["author_id"], :name => "index_merge_requests_on_author_id"
- add_index "merge_requests", ["created_at"], :name => "index_merge_requests_on_created_at"
- add_index "merge_requests", ["milestone_id"], :name => "index_merge_requests_on_milestone_id"
- add_index "merge_requests", ["source_branch"], :name => "index_merge_requests_on_source_branch"
- add_index "merge_requests", ["source_project_id"], :name => "index_merge_requests_on_project_id"
- add_index "merge_requests", ["target_branch"], :name => "index_merge_requests_on_target_branch"
- add_index "merge_requests", ["title"], :name => "index_merge_requests_on_title"
-
- create_table "milestones", :force => true do |t|
- t.string "title", :null => false
- t.integer "project_id", :null => false
+ add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree
+ add_index "merge_requests", ["author_id"], name: "index_merge_requests_on_author_id", using: :btree
+ add_index "merge_requests", ["created_at"], name: "index_merge_requests_on_created_at", using: :btree
+ add_index "merge_requests", ["milestone_id"], name: "index_merge_requests_on_milestone_id", using: :btree
+ add_index "merge_requests", ["source_branch"], name: "index_merge_requests_on_source_branch", using: :btree
+ add_index "merge_requests", ["source_project_id"], name: "index_merge_requests_on_project_id", using: :btree
+ add_index "merge_requests", ["target_branch"], name: "index_merge_requests_on_target_branch", using: :btree
+ add_index "merge_requests", ["title"], name: "index_merge_requests_on_title", using: :btree
+
+ create_table "milestones", force: true do |t|
+ t.string "title", null: false
+ t.integer "project_id", null: false
t.text "description"
t.date "due_date"
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
t.string "state"
t.integer "iid"
end
- add_index "milestones", ["due_date"], :name => "index_milestones_on_due_date"
- add_index "milestones", ["project_id"], :name => "index_milestones_on_project_id"
+ add_index "milestones", ["due_date"], name: "index_milestones_on_due_date", using: :btree
+ add_index "milestones", ["project_id"], name: "index_milestones_on_project_id", using: :btree
- create_table "namespaces", :force => true do |t|
- t.string "name", :null => false
- t.string "path", :null => false
+ create_table "namespaces", force: true do |t|
+ t.string "name", null: false
+ t.string "path", null: false
t.integer "owner_id"
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
t.string "type"
- t.string "description", :default => "", :null => false
+ t.string "description", default: "", null: false
end
- add_index "namespaces", ["name"], :name => "index_namespaces_on_name"
- add_index "namespaces", ["owner_id"], :name => "index_namespaces_on_owner_id"
- add_index "namespaces", ["path"], :name => "index_namespaces_on_path"
- add_index "namespaces", ["type"], :name => "index_namespaces_on_type"
+ add_index "namespaces", ["name"], name: "index_namespaces_on_name", using: :btree
+ add_index "namespaces", ["owner_id"], name: "index_namespaces_on_owner_id", using: :btree
+ add_index "namespaces", ["path"], name: "index_namespaces_on_path", using: :btree
+ add_index "namespaces", ["type"], name: "index_namespaces_on_type", using: :btree
- create_table "notes", :force => true do |t|
+ create_table "notes", force: true do |t|
t.text "note"
t.string "noteable_type"
t.integer "author_id"
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
t.integer "project_id"
t.string "attachment"
t.string "line_code"
t.string "commit_id"
t.integer "noteable_id"
t.text "st_diff"
- t.boolean "system", :default => false, :null => false
+ t.boolean "system", default: false, null: false
end
- add_index "notes", ["author_id"], :name => "index_notes_on_author_id"
- add_index "notes", ["commit_id"], :name => "index_notes_on_commit_id"
- add_index "notes", ["created_at"], :name => "index_notes_on_created_at"
- add_index "notes", ["noteable_id", "noteable_type"], :name => "index_notes_on_noteable_id_and_noteable_type"
- add_index "notes", ["noteable_type"], :name => "index_notes_on_noteable_type"
- add_index "notes", ["project_id", "noteable_type"], :name => "index_notes_on_project_id_and_noteable_type"
- add_index "notes", ["project_id"], :name => "index_notes_on_project_id"
+ add_index "notes", ["author_id"], name: "index_notes_on_author_id", using: :btree
+ add_index "notes", ["commit_id"], name: "index_notes_on_commit_id", using: :btree
+ add_index "notes", ["created_at"], name: "index_notes_on_created_at", using: :btree
+ add_index "notes", ["noteable_id", "noteable_type"], name: "index_notes_on_noteable_id_and_noteable_type", using: :btree
+ add_index "notes", ["noteable_type"], name: "index_notes_on_noteable_type", using: :btree
+ add_index "notes", ["project_id", "noteable_type"], name: "index_notes_on_project_id_and_noteable_type", using: :btree
+ add_index "notes", ["project_id"], name: "index_notes_on_project_id", using: :btree
- create_table "projects", :force => true do |t|
+ create_table "projects", force: true do |t|
t.string "name"
t.string "path"
t.text "description"
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
t.integer "creator_id"
- t.string "default_branch"
- t.boolean "issues_enabled", :default => true, :null => false
- t.boolean "wall_enabled", :default => true, :null => false
- t.boolean "merge_requests_enabled", :default => true, :null => false
- t.boolean "wiki_enabled", :default => true, :null => false
+ t.boolean "issues_enabled", default: true, null: false
+ t.boolean "wall_enabled", default: true, null: false
+ t.boolean "merge_requests_enabled", default: true, null: false
+ t.boolean "wiki_enabled", default: true, null: false
t.integer "namespace_id"
- t.boolean "public", :default => false, :null => false
- t.string "issues_tracker", :default => "gitlab", :null => false
+ t.string "issues_tracker", default: "gitlab", null: false
t.string "issues_tracker_id"
- t.boolean "snippets_enabled", :default => true, :null => false
+ t.boolean "snippets_enabled", default: true, null: false
t.datetime "last_activity_at"
- t.boolean "imported", :default => false, :null => false
+ t.boolean "imported", default: false, null: false
t.string "import_url"
+ t.integer "visibility_level", default: 0, null: false
+ t.boolean "archived", default: false, null: false
end
- add_index "projects", ["creator_id"], :name => "index_projects_on_owner_id"
- add_index "projects", ["last_activity_at"], :name => "index_projects_on_last_activity_at"
- add_index "projects", ["namespace_id"], :name => "index_projects_on_namespace_id"
+ add_index "projects", ["creator_id"], name: "index_projects_on_owner_id", using: :btree
+ add_index "projects", ["last_activity_at"], name: "index_projects_on_last_activity_at", using: :btree
+ add_index "projects", ["namespace_id"], name: "index_projects_on_namespace_id", using: :btree
- create_table "protected_branches", :force => true do |t|
- t.integer "project_id", :null => false
- t.string "name", :null => false
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
+ create_table "protected_branches", force: true do |t|
+ t.integer "project_id", null: false
+ t.string "name", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
end
- add_index "protected_branches", ["project_id"], :name => "index_protected_branches_on_project_id"
+ add_index "protected_branches", ["project_id"], name: "index_protected_branches_on_project_id", using: :btree
- create_table "services", :force => true do |t|
+ create_table "services", force: true do |t|
t.string "type"
t.string "title"
t.string "token"
- t.integer "project_id", :null => false
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
- t.boolean "active", :default => false, :null => false
+ t.integer "project_id", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.boolean "active", default: false, null: false
t.string "project_url"
t.string "subdomain"
t.string "room"
+ t.text "recipients"
end
- add_index "services", ["project_id"], :name => "index_services_on_project_id"
+ add_index "services", ["project_id"], name: "index_services_on_project_id", using: :btree
- create_table "snippets", :force => true do |t|
+ create_table "snippets", force: true do |t|
t.string "title"
- t.text "content", :limit => 2147483647
- t.integer "author_id", :null => false
+ t.text "content", limit: 2147483647
+ t.integer "author_id", null: false
t.integer "project_id"
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
t.string "file_name"
t.datetime "expires_at"
- t.boolean "private", :default => true, :null => false
+ t.boolean "private", default: true, null: false
t.string "type"
end
- add_index "snippets", ["author_id"], :name => "index_snippets_on_author_id"
- add_index "snippets", ["created_at"], :name => "index_snippets_on_created_at"
- add_index "snippets", ["expires_at"], :name => "index_snippets_on_expires_at"
- add_index "snippets", ["project_id"], :name => "index_snippets_on_project_id"
+ add_index "snippets", ["author_id"], name: "index_snippets_on_author_id", using: :btree
+ add_index "snippets", ["created_at"], name: "index_snippets_on_created_at", using: :btree
+ add_index "snippets", ["expires_at"], name: "index_snippets_on_expires_at", using: :btree
+ add_index "snippets", ["project_id"], name: "index_snippets_on_project_id", using: :btree
- create_table "taggings", :force => true do |t|
+ create_table "taggings", force: true do |t|
t.integer "tag_id"
t.integer "taggable_id"
t.string "taggable_type"
@@ -242,45 +254,45 @@ ActiveRecord::Schema.define(:version => 20131009115346) do
t.datetime "created_at"
end
- add_index "taggings", ["tag_id"], :name => "index_taggings_on_tag_id"
- add_index "taggings", ["taggable_id", "taggable_type", "context"], :name => "index_taggings_on_taggable_id_and_taggable_type_and_context"
+ add_index "taggings", ["tag_id"], name: "index_taggings_on_tag_id", using: :btree
+ add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree
- create_table "tags", :force => true do |t|
+ create_table "tags", force: true do |t|
t.string "name"
end
- create_table "users", :force => true do |t|
- t.string "email", :default => "", :null => false
- t.string "encrypted_password", :default => "", :null => false
+ create_table "users", force: true do |t|
+ t.string "email", default: "", null: false
+ t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
- t.integer "sign_in_count", :default => 0
+ t.integer "sign_in_count", default: 0
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
t.string "name"
- t.boolean "admin", :default => false, :null => false
- t.integer "projects_limit", :default => 10
- t.string "skype", :default => "", :null => false
- t.string "linkedin", :default => "", :null => false
- t.string "twitter", :default => "", :null => false
+ t.boolean "admin", default: false, null: false
+ t.integer "projects_limit", default: 10
+ t.string "skype", default: "", null: false
+ t.string "linkedin", default: "", null: false
+ t.string "twitter", default: "", null: false
t.string "authentication_token"
- t.integer "theme_id", :default => 1, :null => false
+ t.integer "theme_id", default: 1, null: false
t.string "bio"
- t.integer "failed_attempts", :default => 0
+ t.integer "failed_attempts", default: 0
t.datetime "locked_at"
t.string "extern_uid"
t.string "provider"
t.string "username"
- t.boolean "can_create_group", :default => true, :null => false
- t.boolean "can_create_team", :default => true, :null => false
+ t.boolean "can_create_group", default: true, null: false
+ t.boolean "can_create_team", default: true, null: false
t.string "state"
- t.integer "color_scheme_id", :default => 1, :null => false
- t.integer "notification_level", :default => 1, :null => false
+ t.integer "color_scheme_id", default: 1, null: false
+ t.integer "notification_level", default: 1, null: false
t.datetime "password_expires_at"
t.integer "created_by_id"
t.string "avatar"
@@ -288,50 +300,54 @@ ActiveRecord::Schema.define(:version => 20131009115346) do
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "unconfirmed_email"
+ t.boolean "hide_no_ssh_key", default: false
end
- add_index "users", ["admin"], :name => "index_users_on_admin"
- add_index "users", ["authentication_token"], :name => "index_users_on_authentication_token", :unique => true
- add_index "users", ["confirmation_token"], :name => "index_users_on_confirmation_token", :unique => true
- add_index "users", ["email"], :name => "index_users_on_email", :unique => true
- add_index "users", ["extern_uid", "provider"], :name => "index_users_on_extern_uid_and_provider", :unique => true
- add_index "users", ["name"], :name => "index_users_on_name"
- add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true
- add_index "users", ["username"], :name => "index_users_on_username"
-
- create_table "users_groups", :force => true do |t|
- t.integer "group_access", :null => false
- t.integer "group_id", :null => false
- t.integer "user_id", :null => false
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
- t.integer "notification_level", :default => 3, :null => false
+ add_index "users", ["admin"], name: "index_users_on_admin", using: :btree
+ add_index "users", ["authentication_token"], name: "index_users_on_authentication_token", unique: true, using: :btree
+ add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree
+ add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
+ add_index "users", ["extern_uid", "provider"], name: "index_users_on_extern_uid_and_provider", unique: true, using: :btree
+ add_index "users", ["name"], name: "index_users_on_name", using: :btree
+ add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
+ add_index "users", ["username"], name: "index_users_on_username", using: :btree
+
+ create_table "users_groups", force: true do |t|
+ t.integer "group_access", null: false
+ t.integer "group_id", null: false
+ t.integer "user_id", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.integer "notification_level", default: 3, null: false
end
- add_index "users_groups", ["user_id"], :name => "index_users_groups_on_user_id"
+ add_index "users_groups", ["user_id"], name: "index_users_groups_on_user_id", using: :btree
- create_table "users_projects", :force => true do |t|
- t.integer "user_id", :null => false
- t.integer "project_id", :null => false
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
- t.integer "project_access", :default => 0, :null => false
- t.integer "notification_level", :default => 3, :null => false
+ create_table "users_projects", force: true do |t|
+ t.integer "user_id", null: false
+ t.integer "project_id", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.integer "project_access", default: 0, null: false
+ t.integer "notification_level", default: 3, null: false
end
- add_index "users_projects", ["project_access"], :name => "index_users_projects_on_project_access"
- add_index "users_projects", ["project_id"], :name => "index_users_projects_on_project_id"
- add_index "users_projects", ["user_id"], :name => "index_users_projects_on_user_id"
+ add_index "users_projects", ["project_access"], name: "index_users_projects_on_project_access", using: :btree
+ add_index "users_projects", ["project_id"], name: "index_users_projects_on_project_id", using: :btree
+ add_index "users_projects", ["user_id"], name: "index_users_projects_on_user_id", using: :btree
- create_table "web_hooks", :force => true do |t|
+ create_table "web_hooks", force: true do |t|
t.string "url"
t.integer "project_id"
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
- t.string "type", :default => "ProjectHook"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.string "type", default: "ProjectHook"
t.integer "service_id"
+ t.boolean "push_events", default: true, null: false
+ t.boolean "issues_events", default: false, null: false
+ t.boolean "merge_requests_events", default: false, null: false
end
- add_index "web_hooks", ["project_id"], :name => "index_web_hooks_on_project_id"
+ add_index "web_hooks", ["project_id"], name: "index_web_hooks_on_project_id", using: :btree
end
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 3ae9af59fc3..53acc4a025e 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -2,7 +2,7 @@
### List projects
-Get a list of projects owned by the authenticated user.
+Get a list of projects accessible by the authenticated user.
```
GET /projects
@@ -15,6 +15,7 @@ GET /projects
"description": null,
"default_branch": "master",
"public": false,
+ "visibility_level": 0,
"ssh_url_to_repo": "git@example.com:diaspora/diaspora-client.git",
"http_url_to_repo": "http://example.com/diaspora/diaspora-client.git",
"web_url": "http://example.com/diaspora/diaspora-client",
@@ -49,6 +50,7 @@ GET /projects
"description": null,
"default_branch": "master",
"public": false,
+ "visibility_level": 0,
"ssh_url_to_repo": "git@example.com:brightbox/puppet.git",
"http_url_to_repo": "http://example.com/brightbox/puppet.git",
"web_url": "http://example.com/brightbox/puppet",
@@ -82,6 +84,22 @@ GET /projects
```
+#### List owned projects
+
+Get a list of projects owned by the authenticated user.
+
+```
+GET /projects/owned
+```
+
+#### List ALL projects
+
+Get a list of all GitLab projects (admin only).
+
+```
+GET /projects/all
+```
+
### Get single project
Get a specific project, identified by project ID or NAMESPACE/PROJECT_NAME , which is owned by the authentication user.
@@ -101,6 +119,7 @@ Parameters:
"description": null,
"default_branch": "master",
"public": false,
+ "visibility_level": 0,
"ssh_url_to_repo": "git@example.com:diaspora/diaspora-project-site.git",
"http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git",
"web_url": "http://example.com/diaspora/diaspora-project-site",
@@ -213,13 +232,13 @@ Parameters:
+ `name` (required) - new project name
+ `description` (optional) - short project description
-+ `default_branch` (optional) - 'master' by default
+ `issues_enabled` (optional)
+ `wall_enabled` (optional)
+ `merge_requests_enabled` (optional)
+ `wiki_enabled` (optional)
+ `snippets_enabled` (optional)
-+ `public` (optional)
++ `public` (optional) - if `true` same as setting visibility_level = 20
++ `visibility_level` (optional)
### Create project for user
@@ -241,7 +260,8 @@ Parameters:
+ `merge_requests_enabled` (optional)
+ `wiki_enabled` (optional)
+ `snippets_enabled` (optional)
-+ `public` (optional)
++ `public` (optional) - if `true` same as setting visibility_level = 20
++ `visibility_level` (optional)
## Remove project
@@ -382,6 +402,10 @@ Parameters:
{
"id": 1,
"url": "http://example.com/hook",
+ "project_id": 3,
+ "push_events": "true",
+ "issues_events": "true",
+ "merge_requests_events": "true",
"created_at": "2012-10-12T17:04:47Z"
}
```
@@ -399,6 +423,9 @@ Parameters:
+ `id` (required) - The ID or NAME of a project
+ `url` (required) - The hook URL
++ `push_events` - Trigger hook on push events
++ `issues_events` - Trigger hook on issues events
++ `merge_requests_events` - Trigger hook on merge_requests events
### Edit project hook
@@ -414,6 +441,9 @@ Parameters:
+ `id` (required) - The ID or NAME of a project
+ `hook_id` (required) - The ID of a project hook
+ `url` (required) - The hook URL
++ `push_events` - Trigger hook on push events
++ `issues_events` - Trigger hook on issues events
++ `merge_requests_events` - Trigger hook on merge_requests events
### Delete project hook
@@ -458,7 +488,7 @@ Parameters:
"id":"3f94fc7c85061973edc9906ae170cc269b07ca55"
}],
"tree": "c68537c6534a02cc2b176ca1549f4ffa190b58ee",
- "message":"give caolan his credit where it's due (up top)",
+ "message":"give caolan credit where it's due (up top)",
"author": {
"name":"Jeremy Ashkenas",
"email":"jashkenas@example.com"
diff --git a/doc/api/repositories.md b/doc/api/repositories.md
index 2769c22d6aa..6b3a43b869e 100644
--- a/doc/api/repositories.md
+++ b/doc/api/repositories.md
@@ -343,9 +343,9 @@ Parameters:
```
-## Raw blob content
+## Raw file content
-Get the raw file contents for a file.
+Get the raw file contents for a file by commit sha and path.
```
GET /projects/:id/repository/blobs/:sha
@@ -358,6 +358,20 @@ Parameters:
+ `filepath` (required) - The path the file
+## Raw blob content
+
+Get the raw file contents for a blob by blob sha.
+
+```
+GET /projects/:id/repository/raw_blobs/:sha
+```
+
+Parameters:
+
++ `id` (required) - The ID of a project
++ `sha` (required) - The blob sha
+
+
## Get file archive
Get a an archive of the repository
@@ -368,4 +382,43 @@ GET /projects/:id/repository/archive
Parameters:
+ `id` (required) - The ID of a project
-+ `sha` (optional) - The commit sha to download defaults to the tip of the default branch \ No newline at end of file
++ `sha` (optional) - The commit sha to download defaults to the tip of the default branch
+
+
+## Create new file in repository
+
+```
+POST /projects/:id/repository/files
+```
+
+Parameters:
+
++ `file_path` (optional) - Full path to new file. Ex. lib/class.rb
++ `branch_name` (required) - The name of branch
++ `content` (required) - File content
++ `commit_message` (required) - Commit message
+
+## Update existing file in repository
+
+```
+PUT /projects/:id/repository/files
+```
+
+Parameters:
+
++ `file_path` (required) - Full path to file. Ex. lib/class.rb
++ `branch_name` (required) - The name of branch
++ `content` (required) - New file content
++ `commit_message` (required) - Commit message
+
+## Delete existing file in repository
+
+```
+DELETE /projects/:id/repository/files
+```
+
+Parameters:
+
++ `file_path` (required) - Full path to file. Ex. lib/class.rb
++ `branch_name` (required) - The name of branch
++ `commit_message` (required) - Commit message
diff --git a/doc/development/architecture.md b/doc/development/architecture.md
new file mode 100644
index 00000000000..db22f0bda85
--- /dev/null
+++ b/doc/development/architecture.md
@@ -0,0 +1,23 @@
+# GitLab project architecture
+
+GitLab project consists of two parts: GitLab and GitLab shell.
+
+## GitLab
+
+Web application with background jobs workers.
+Provides you with UI and most of functionality.
+For some operations like repo creation - uses GitLab shell.
+
+Uses:
+ * Ruby as main language for application code and most libraries.
+ * [Rails](http://rubyonrails.org/) web framework as main framework for application.
+ * Mysql or postgres as main databases. Used for persistent data storage(users, project, issues etc).
+ * Redis database. Used for cache and exchange data between some components.
+ * Python2 because of [pygments](http://pygments.org/) as code syntax highlighter.
+
+## GitLab shell
+
+Command line ruby application. Used by GitLab through shell commands.
+It provides interface to all kind of manipulations with repositories and ssh keys.
+Full list of commands you can find in README of GitLab shell repo.
+Works on pure ruby and do not require any additional software.
diff --git a/doc/install/databases.md b/doc/install/databases.md
index be7bc0aad2e..6016e97ede5 100644
--- a/doc/install/databases.md
+++ b/doc/install/databases.md
@@ -25,19 +25,19 @@ GitLab supports the following databases:
# Create a user for GitLab
# do not type the 'mysql>', this is part of the prompt
# change $password in the command below to a real password you pick
- mysql> CREATE USER 'gitlab'@'localhost' IDENTIFIED BY '$password';
+ mysql> CREATE USER 'git'@'localhost' IDENTIFIED BY '$password';
# Create the GitLab production database
mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`;
# Grant the GitLab user necessary permissions on the table.
- mysql> GRANT SELECT, LOCK TABLES, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER ON `gitlabhq_production`.* TO 'gitlab'@'localhost';
+ mysql> GRANT SELECT, LOCK TABLES, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER ON `gitlabhq_production`.* TO 'git'@'localhost';
# Quit the database session
mysql> \q
# Try connecting to the new database with the new user
- sudo -u git -H mysql -u gitlab -p -D gitlabhq_production
+ sudo -u git -H mysql -u git -p -D gitlabhq_production
# Type the password you replaced $password with earlier
diff --git a/doc/install/installation.md b/doc/install/installation.md
index a63b7d725bb..6961e461482 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -1,6 +1,6 @@
# Select Version to Install
Make sure you view this installation guide from the branch (version) of GitLab you would like to install. In most cases
-this should be the highest numbered stable branch (example shown below).
+this should be the highest numbered stable branch (example shown below).
![capture](https://f.cloud.github.com/assets/1192780/564911/2f9f3e1e-c5b7-11e2-9f89-98e527d1adec.png)
@@ -14,7 +14,7 @@ This is the official installation guide to set up a production server. To set up
The following steps have been known to work. Please **use caution when you deviate** from this guide. Make sure you don't violate any assumptions GitLab makes about its environment. For example many people run into permission problems because they changed the location of directories or run services as the wrong user.
-If you find a bug/error in this guide please **submit a pull request** following the [contributing guide](../../CONTRIBUTING.md).
+If you find a bug/error in this guide please **submit a merge request** following the [contributing guide](../../CONTRIBUTING.md).
- - -
@@ -105,7 +105,7 @@ Is the system packaged Git too old? Remove it and compile from source.
mail server. By default, Debian is shipped with exim4 whereas Ubuntu
does not ship with one. The recommended mail server is postfix and you can install it with:
- sudo apt-get install -y postfix
+ sudo apt-get install -y postfix
Then select 'Internet Site' and press enter to confirm the hostname.
@@ -118,8 +118,8 @@ Remove the old Ruby 1.8 if present
Download Ruby and compile it:
mkdir /tmp/ruby && cd /tmp/ruby
- curl --progress ftp://ftp.ruby-lang.org/pub/ruby/2.0/ruby-2.0.0-p247.tar.gz | tar xz
- cd ruby-2.0.0-p247
+ curl --progress ftp://ftp.ruby-lang.org/pub/ruby/2.0/ruby-2.0.0-p353.tar.gz | tar xz
+ cd ruby-2.0.0-p353
./configure --disable-install-rdoc
make
sudo make install
@@ -144,13 +144,10 @@ GitLab Shell is an ssh access and repository management software developed speci
cd /home/git
# Clone gitlab shell
- sudo -u git -H git clone https://github.com/gitlabhq/gitlab-shell.git
+ sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-shell.git -b v1.8.0
cd gitlab-shell
- # switch to right version
- sudo -u git -H git checkout v1.7.1
-
sudo -u git -H cp config.yml.example config.yml
# Edit config and replace gitlab_url
@@ -174,16 +171,13 @@ To setup the MySQL/PostgreSQL database and dependencies please see [`doc/install
## Clone the Source
# Clone GitLab repository
- sudo -u git -H git clone https://github.com/gitlabhq/gitlabhq.git gitlab
+ sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 6-4-stable gitlab
# Go to gitlab dir
cd /home/git/gitlab
- # Checkout to stable release
- sudo -u git -H git checkout 6-2-stable
-
**Note:**
-You can change `6-2-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
+You can change `6-4-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
## Configure it
@@ -227,10 +221,6 @@ You can change `6-2-stable` to `master` if you want the *bleeding edge* version,
# Copy the example Rack attack config
sudo -u git -H cp config/initializers/rack_attack.rb.example config/initializers/rack_attack.rb
- # Enable rack attack middleware
- # Find and uncomment the line 'config.middleware.use Rack::Attack'
- sudo -u git -H editor config/application.rb
-
# Configure Git global settings for git user, useful when editing via web
# Edit user.email according to what is set in gitlab.yml
sudo -u git -H git config --global user.name "GitLab"
@@ -257,7 +247,7 @@ Make sure to edit both `gitlab.yml` and `unicorn.rb` to match your setup.
# PostgreSQL
sudo -u git cp config/database.yml.postgresql config/database.yml
-
+
# Make config/database.yml readable to git only
sudo -u git -H chmod o-rwx config/database.yml
@@ -265,8 +255,6 @@ Make sure to edit both `gitlab.yml` and `unicorn.rb` to match your setup.
cd /home/git/gitlab
- sudo gem install charlock_holmes --version '0.6.9.4'
-
# For MySQL (note, the option says "without ... postgres")
sudo -u git -H bundle install --deployment --without development test postgres aws
@@ -288,7 +276,12 @@ Make sure to edit both `gitlab.yml` and `unicorn.rb` to match your setup.
Download the init script (will be /etc/init.d/gitlab):
sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
- sudo chmod +x /etc/init.d/gitlab
+
+And if you are installing with a non-default folder or user copy and edit the defaults file:
+
+ sudo cp lib/support/init.d/gitlab.default.example /etc/default/gitlab
+
+If you installed gitlab in another directory or as a user other than the default you should change these settings in /etc/default/gitlab. Do not edit /etc/init.d/gitlab as it will be changed on upgrade.
Make GitLab start on boot:
@@ -310,21 +303,17 @@ Check if GitLab and its environment are configured correctly:
# or
sudo /etc/init.d/gitlab restart
-## Double-check Application Status
-
-To make sure you didn't miss anything run a more thorough check with:
- sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+## Compile assets
-If all items are green, then congratulations on successfully installing GitLab!
-However there are still a few steps left.
+ sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
# 7. Nginx
**Note:**
Nginx is the officially supported web server for GitLab. If you cannot or do not want to use Nginx as your web server, have a look at the
-[GitLab recipes](https://github.com/gitlabhq/gitlab-recipes).
+[GitLab recipes](https://gitlab.com/gitlab-org/gitlab-recipes/).
## Installation
sudo apt-get install -y nginx
@@ -349,7 +338,17 @@ Make sure to edit the config file to match your setup:
# Done!
-Visit YOUR_SERVER for your first GitLab login.
+## Double-check Application Status
+
+To make sure you didn't miss anything run a more thorough check with:
+
+ sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+
+If all items are green, then congratulations on successfully installing GitLab!
+
+## Initial Login
+
+Visit YOUR_SERVER in your web browser for your first GitLab login.
The setup has created an admin account for you. You can use it to log in:
admin@local.host
@@ -376,7 +375,7 @@ a different host, you can configure its connection string via the
# example
production: redis://redis.example.tld:6379
-If you want to connect the Redis server via socket, then use the "unix:" URL scheme
+If you want to connect the Redis server via socket, then use the "unix:" URL scheme
and the path to the Redis socket file in the `config/resque.yml` file.
# example
@@ -409,10 +408,10 @@ These steps are fairly general and you will need to figure out the exact details
* Stop GitLab
`sudo service gitlab stop`
-* Add provider specific configuration options to your `config/gitlab.yml` (you can use the [auth providers section of the example config](https://github.com/gitlabhq/gitlabhq/blob/master/config/gitlab.yml.example) as a reference)
+* Add provider specific configuration options to your `config/gitlab.yml` (you can use the [auth providers section of the example config](https://gitlab.com/gitlab-org/gitlab-ce/blob/masterconfig/gitlab.yml.example) as a reference)
-* Add the gem to your [Gemfile](https://github.com/gitlabhq/gitlabhq/blob/master/Gemfile)
- `gem "omniauth-your-auth-provider"`
+* Add the gem to your [Gemfile](https://gitlab.com/gitlab-org/gitlab-ce/blob/masterGemfile)
+ `gem "omniauth-your-auth-provider"`
* If you're using MySQL, install the new Omniauth provider gem by running the following command:
`sudo -u git -H bundle install --without development test postgres --path vendor/bundle --no-deployment`
@@ -428,5 +427,5 @@ These steps are fairly general and you will need to figure out the exact details
### Examples
If you have successfully set up a provider that is not shipped with GitLab itself, please let us know.
-You can help others by reporting successful configurations and probably share a few insights or provide warnings for common errors or pitfalls by sharing your experience [in the public Wiki](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Working-Custom-Omniauth-Provider-Configurations).
+You can help others by reporting successful configurations and probably share a few insights or provide warnings for common errors or pitfalls by sharing your experience [in the public Wiki](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Custom-omniauth-provider-configurations).
While we can't officially support every possible auth mechanism out there, we'd like to at least help those with special needs.
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index 30a95646741..e9c95ba2ef9 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -1,40 +1,45 @@
# Operating Systems
-## Linux
-
GitLab is developed for the Linux operating system.
-GitLab officially supports (recent versions of) these Linux distributions:
+## GitLab officially supports
- Ubuntu Linux
- Debian/GNU Linux
-It should also work on (though they are not officially supported):
+## GitLab.com offers paid support for
-- Arch
+- Red Hat Enterprise Linux (RHEL)
- CentOS
+- Oracle Linux
+
+## Not officially supported
+
+- Arch Linux
- Fedora
- Gentoo
-- RedHat
-## Other Unix Systems
+On the above distributions it is pretty easy to install GitLab yourself.
+
+## Unsupported Unix Systems
-There is nothing that prevents GitLab from running on other Unix operating
-systems. This means you may get it to work on systems running FreeBSD or OS X.
-**If you want to try, please proceed with caution!**
+There is nothing that prevents GitLab from running on other Unix operating systems.
+This means you may get it to work on systems running FreeBSD or OS X.
+If you want to do this, please be aware it could be a lot of work.
+Please consider using a virtual machine to run GitLab.
-## Windows
+## Other operating systems such as Windows
-GitLab does **not** run on Windows and we have no plans of supporting it in the
-near future. Please consider using a virtual machine to run GitLab.
+GitLab does **not** run on Windows and we have no plans of supporting it in the near future.
+Please consider using a virtual machine to run GitLab.
-# Rubies
+# Ruby versions
-GitLab requires Ruby (MRI) 1.9.3 and several Gems with native components.
-While it is generally possible to use other Rubies (like
-[JRuby](http://jruby.org/) or [Rubinius](http://rubini.us/)) it might require
-some work on your part.
+GitLab requires Ruby (MRI) 1.9.3 or 2.0+.
+While it is generally possible to use other Rubies
+(like [JRuby](http://jruby.org/) or [Rubinius](http://rubini.us/))
+it might require some work since GitLab uses several Gems that have native extensions.
# Hardware requirements
diff --git a/doc/legal/corporate_contributor_license_agreement.md b/doc/legal/corporate_contributor_license_agreement.md
new file mode 100644
index 00000000000..bbc274f3b0c
--- /dev/null
+++ b/doc/legal/corporate_contributor_license_agreement.md
@@ -0,0 +1,25 @@
+You accept and agree to the following terms and conditions for Your present and future Contributions submitted to GitLab.com. Except for the license granted herein to GitLab.com and recipients of software distributed by GitLab.com, You reserve all right, title, and interest in and to Your Contributions.
+
+1. Definitions.
+
+ "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with GitLab.com. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "Contribution" shall mean the code, documentation or other original works of authorship expressly identified in Schedule B, as well as any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to GitLab.com for inclusion in, or documentation of, any of the products owned or managed by GitLab.com (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to GitLab.com or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, GitLab.com for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution."
+
+2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to GitLab.com and to recipients of software distributed by GitLab.com a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works.
+
+3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to GitLab.com and to recipients of software distributed by GitLab.com a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed.
+
+4. You represent that You are legally entitled to grant the above license. You represent further that each employee of the Corporation designated on Schedule A below (or in a subsequent written modification to that Schedule) is authorized to submit Contributions on behalf of the Corporation.
+
+5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others).
+
+6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE.
+
+7. Should You wish to submit work that is not Your original creation, You may submit it to GitLab.com separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]".
+
+8. It is your responsibility to notify GitLab.com when any change is required to the list of designated employees authorized to submit Contributions on behalf of the Corporation, or to the Corporation's Point of Contact with GitLab.com.
+
+---------------------------------------
+
+This text is licensed under the [Creative Commons Attribution 3.0 License](http://creativecommons.org/licenses/by/3.0/) and the original source is the Google Open Source Programs Office.
diff --git a/doc/legal/individual_contributor_license_agreement.md b/doc/legal/individual_contributor_license_agreement.md
new file mode 100644
index 00000000000..eaf5812ca4c
--- /dev/null
+++ b/doc/legal/individual_contributor_license_agreement.md
@@ -0,0 +1,25 @@
+You accept and agree to the following terms and conditions for Your present and future Contributions submitted to GitLab.com. Except for the license granted herein to GitLab.com and recipients of software distributed by GitLab.com, You reserve all right, title, and interest in and to Your Contributions.
+
+1. Definitions.
+
+ "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with GitLab.com. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to GitLab.com for inclusion in, or documentation of, any of the products owned or managed by GitLab.com (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to GitLab.com or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, GitLab.com for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution."
+
+2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to GitLab.com and to recipients of software distributed by GitLab.com a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works.
+
+3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to GitLab.com and to recipients of software distributed by GitLab.com a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed.
+
+4. You represent that you are legally entitled to grant the above license. If your employer(s) has rights to intellectual property that you create that includes your Contributions, you represent that you have received permission to make Contributions on behalf of that employer, that your employer has waived such rights for your Contributions to GitLab.com, or that your employer has executed a separate Corporate CLA with GitLab.com.
+
+5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions.
+
+6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON- INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE.
+
+7. Should You wish to submit work that is not Your original creation, You may submit it to GitLab.com separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [[]named here]".
+
+8. You agree to notify GitLab.com of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect.
+
+---------------------------------------
+
+This text is licensed under the [Creative Commons Attribution 3.0 License](http://creativecommons.org/licenses/by/3.0/) and the original source is the Google Open Source Programs Office.
diff --git a/doc/markdown/markdown.md b/doc/markdown/markdown.md
index a84222f9fc2..6b5308afc4f 100644
--- a/doc/markdown/markdown.md
+++ b/doc/markdown/markdown.md
@@ -187,7 +187,7 @@ GFM will recognize the following:
* !123 : for merge requests
* $123 : for snippets
* 1234567 : for commits
-* [file](path/to/file) : for file references
+* \[file\](path/to/file) : for file references
<a name="standard"/>
diff --git a/doc/make_release.md b/doc/release/monthly.md
index 7f19a17da6d..1175942cfff 100644
--- a/doc/make_release.md
+++ b/doc/release/monthly.md
@@ -1,4 +1,4 @@
-# Things to do when creating new release
+# Things to do when creating new monthly minor or major release
NOTE: This is a guide for GitLab developers. If you are trying to install GitLab see the latest stable [installation guide](install/installation.md) and if you are trying to upgrade, see the [upgrade guides](update).
## Install guide up to date?
@@ -27,16 +27,16 @@ NOTE: This is a guide for GitLab developers. If you are trying to install GitLab
Check if any of these changed since last release (~22nd of last month depending on when last release branch was created):
-* https://github.com/gitlabhq/gitlabhq/commits/master/lib/support/nginx/gitlab
-* https://github.com/gitlabhq/gitlab-shell/commits/master/config.yml.example
-* https://github.com/gitlabhq/gitlabhq/commits/master/config/gitlab.yml.example
-* https://github.com/gitlabhq/gitlabhq/commits/master/config/unicorn.rb.example
-* https://github.com/gitlabhq/gitlabhq/commits/master/config/database.yml.mysql
-* https://github.com/gitlabhq/gitlabhq/commits/master/config/database.yml.postgresql
+* https://gitlab.com/gitlab-org/gitlab-ce/commits/master/lib/support/nginx/gitlab
+* https://gitlab.com/gitlab-org/gitlab-shell/commits/master/config.yml.example
+* https://gitlab.com/gitlab-org/gitlab-ce/commits/master/config/gitlab.yml.example
+* https://gitlab.com/gitlab-org/gitlab-ce/commits/master/config/unicorn.rb.example
+* https://gitlab.com/gitlab-org/gitlab-ce/commits/master/config/database.yml.mysql
+* https://gitlab.com/gitlab-org/gitlab-ce/commits/master/config/database.yml.postgresql
#### 8. Need to update init script?
-Check if changed since last release (~22nd of last month depending on when last release branch was created): https://github.com/gitlabhq/gitlabhq/commits/master/lib/support/init.d/gitlab
+Check if changed since last release (~22nd of last month depending on when last release branch was created): https://gitlab.com/gitlab-org/gitlab-ce/commits/master/lib/support/init.d/gitlab
#### 9. Start application
@@ -58,14 +58,21 @@ Check if changed since last release (~22nd of last month depending on when last
After making the release branch new commits are cherry-picked from master. When the release gets closer we get more selective what is cherry-picked.
-- 5 days before release: feature freeze
-- 3 days before release: UI freeze
-- 1 day before release: code freeze
+* 5 days before release: feature freeze (stop merging new features)
+* 4 days before release: UI freeze (stop merging changes to the user interface)
+* 3 days before release: code freeze (stop merging non-essential code improvements)
+* 2 days before release: release candidate 1 (tag and tweet about x.x.rc1)
+* 1 day before release: release candidate 2 (optional, only if rc1 had problems)
+
+# Write a blog post
+
+* Mention what GitLab is on the second line: GitLab is open source software to collaborate on code.
+* Select and thank the the Most Valuable Person (MVP) of this release.
+* Note if there are security fixes: This release fixes an important security issue and we advise everyone to upgrade as soon as possible.
## Last actions
-1. Write a blog post (mention what GitLab is on the first line, select a MVP)
1. Update VERSION and CHANGELOG
1. Create a git tag vX.X.X
1. Publish the blog post
-1. Tweet about the release
+1. Tweet about the release \ No newline at end of file
diff --git a/doc/release/security.md b/doc/release/security.md
new file mode 100644
index 00000000000..a77cbae3eaa
--- /dev/null
+++ b/doc/release/security.md
@@ -0,0 +1,76 @@
+# Things to do when doing an out-of-bound security release
+NOTE: This is a guide for GitLab developers. If you are trying to install GitLab see the latest stable [installation guide](install/installation.md) and if you are trying to upgrade, see the [upgrade guides](update).
+
+## When to do a security release
+
+Do a security release when there is a critical issue that needs to be adresses before the next monthly release. Otherwise include it in the monthly release and note there was a security fix in the release announcement.
+
+## Security vulnerability disclosure
+
+Please report suspected security vulnerabilities in private to support@gitlab.com, also see the [disclosure section on the GitLab.com website](http://www.gitlab.com/disclosure/). Please do NOT create publicly viewable issues for suspected security vulnerabilities.
+
+## Release Procedure
+
+1. Verify that the issue can be repoduced
+1. Acknowledge the issue to the researcher that disclosed it
+1. Fix the issue on a feature branch, do this on the private GitLab development server and update the VERSION and CHANGELOG in this branch
+1. Consider creating and testing workarounds
+1. Create feature branches for the blog posts on GitLab.org and GitLab.com and link them from the code branch
+1. Merge the code feature branch into master
+1. Cherry-pick the code into the latest stable branch
+1. Create a git tag vX.X.X for CE and another patch release for EE
+1. Push the code and the tags to all the CE and EE repositories
+1. Apply the patch to GitLab Cloud and the private GitLab development server
+1. Merge and publish the blog posts
+1. Send tweets about the release from @gitlabhq and @git_lab
+1. Send out an email to the subscribers mailing list on MailChimp
+1. Send out an email to [the community google mailing list](https://groups.google.com/forum/#!forum/gitlabhq)
+1. Send out an email to [the GitLab newsletter list](http://gitlab.us5.list-manage.com/subscribe?u=498dccd07cf3e9482bee33ba4&id=98a9a4992c)
+1. Post a signed copy of our complete announcement to [oss-security](http://www.openwall.com/lists/oss-security/) and request a CVE number
+1. Add the security researcher to the [Security Researcher Acknowledgments list](http://www.gitlab.com/vulnerability-acknowledgements/)
+1. Thank the security researcher in an email for their cooperation
+1. Update the blogpost and the CHANGELOG when we receive the CVE number
+
+The timing of the code merge into master should be coordinated in advance.
+After the merge we strive to publish the announcements within 60 minutes.
+
+## Blog post template
+
+XXX Security Advisory for GitLab
+
+A recently discovered critical vulnerability in GitLab allows [unauthenticated API access|remote code execution|unauthorized access to repositories|XXX|PICKSOMETHING]. All users should update GitLab and gitlab-shell immediately.
+We [have|haven't|XXX|PICKSOMETHING|] heard of this vulnerability being actively exploited.
+
+### Version affected
+
+GitLab Community Edition XXX and lower
+GitLab Enterprise Edition XXX and lower
+
+### Fixed versions
+
+GitLab Community Edition XXX and up
+GitLab Enterprise Edition XXX and up
+
+### Impact
+
+On GitLab installations which use MySQL as their database backend it is possible for an attacker to assume the identity of any existing GitLab user in certain API calls. This attack can be performed by [unauthenticated|authenticated|XXX|PICKSOMETHING] users.
+
+### Workarounds
+
+If you are unable to upgrade you should apply the following patch and restart GitLab.
+
+XXX
+
+### Credit
+
+We want to thank XXX of XXX for the reponsible disclosure of this vulnerability.
+
+## Email template
+
+We just announced a security advisory for GitLab at XXX
+
+Please contact us at support@gitlab.com if you have any questions.
+
+## Tweet template
+
+We just announced a security advisory for GitLab at XXX
diff --git a/doc/update/4.2-to-5.0.md b/doc/update/4.2-to-5.0.md
index 90f59e1fd1a..5bf8c367734 100644
--- a/doc/update/4.2-to-5.0.md
+++ b/doc/update/4.2-to-5.0.md
@@ -1,5 +1,8 @@
# From 4.2 to 5.0
+## Warning
+GitLab 5.0 is affected by critical security vulnerability CVE-2013-4490. Please update to GitLab 5.4 immediately.
+
## Important changes
* We don't use `gitlab` user any more. Everything will be moved to `git` user
diff --git a/doc/update/5.0-to-5.1.md b/doc/update/5.0-to-5.1.md
index 45fc3436ebe..24d96e43bad 100644
--- a/doc/update/5.0-to-5.1.md
+++ b/doc/update/5.0-to-5.1.md
@@ -1,5 +1,8 @@
# From 5.0 to 5.1
+## Warning
+GitLab 5.1 is affected by critical security vulnerability CVE-2013-4490. Please [update to GitLab 5.4 immediately](5.1-to-5.4.md).
+
## Release notes:
* `unicorn` replaced with `puma`
diff --git a/doc/update/5.1-to-5.2.md b/doc/update/5.1-to-5.2.md
index 27f992ecfe6..e4eaee91b8e 100644
--- a/doc/update/5.1-to-5.2.md
+++ b/doc/update/5.1-to-5.2.md
@@ -1,5 +1,8 @@
# From 5.1 to 5.2
+## Warning
+GitLab 5.2 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489. Please [update to GitLab 5.4 directly](5.1-to-5.4.md).
+
### 0. Backup
It's useful to make a backup just in case things go south:
@@ -48,8 +51,8 @@ sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
### 5. Update config files
-* Make `/home/git/gitlab/config/gitlab.yml` same as https://github.com/gitlabhq/gitlabhq/blob/5-2-stable/config/gitlab.yml.example but with your settings.
-* Make `/home/git/gitlab/config/puma.rb` same as https://github.com/gitlabhq/gitlabhq/blob/5-2-stable/config/puma.rb.example but with your settings.
+* Make `/home/git/gitlab/config/gitlab.yml` same as https://gitlab.com/gitlab-org/gitlab-ce/blob/5-2-stable/config/gitlab.yml.example but with your settings.
+* Make `/home/git/gitlab/config/puma.rb` same as https://gitlab.com/gitlab-org/gitlab-ce/blob/5-2-stable/config/puma.rb.example but with your settings.
### 6. Update Init script
diff --git a/doc/update/5.1-to-5.4.md b/doc/update/5.1-to-5.4.md
new file mode 100644
index 00000000000..39cacd381a3
--- /dev/null
+++ b/doc/update/5.1-to-5.4.md
@@ -0,0 +1,103 @@
+# From 5.1 to 5.4
+Also works starting from 5.2.
+
+## Notice
+Security vulnerabilities CVE-2013-4490 and CVE-2013-4489 have been patched in the latest version of GitLab 5.4.
+
+### 0. Backup
+
+It's useful to make a backup just in case things go south:
+(With MySQL, this may require granting "LOCK TABLES" privileges to the GitLab user on the database version)
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
+```
+
+### 1. Stop server
+
+ sudo service gitlab stop
+
+### 2. Get latest code
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H git fetch
+sudo -u git -H git checkout 5-4-stable # Latest version of 5-4-stable addresses CVE-2013-4489
+```
+
+### 3. Update gitlab-shell
+
+```bash
+cd /home/git/gitlab-shell
+sudo -u git -H git fetch
+sudo -u git -H git checkout v1.7.9 # Addresses multiple critical security vulnerabilities
+```
+
+### 4. Install libs, migrations, etc.
+
+```bash
+cd /home/git/gitlab
+
+# MySQL
+sudo -u git -H bundle install --without development test postgres --deployment
+
+#PostgreSQL
+sudo -u git -H bundle install --without development test mysql --deployment
+
+sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
+
+sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
+```
+
+### 5. Update config files
+
+* Make `/home/git/gitlab/config/gitlab.yml` same as https://gitlab.com/gitlab-org/gitlab-ce/blob/5-4-stable/config/gitlab.yml.example but with your settings.
+* Make `/home/git/gitlab/config/puma.rb` same as https://gitlab.com/gitlab-org/gitlab-ce/blob/5-4-stable/config/puma.rb.example but with your settings.
+
+### 6. Update Init script
+
+```bash
+sudo rm /etc/init.d/gitlab
+sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
+sudo chmod +x /etc/init.d/gitlab
+```
+
+### 7. Create uploads directory
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H mkdir public/uploads
+sudo chmod -R u+rwX public/uploads
+```
+
+
+### 8. Start application
+
+ sudo service gitlab start
+ sudo service nginx restart
+
+### 9. Check application status
+
+Check if GitLab and its environment are configured correctly:
+
+ sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
+
+To make sure you didn't miss anything run a more thorough check with:
+
+ sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+
+If all items are green, then congratulations upgrade complete!
+
+## Things went south? Revert to previous version (5.3)
+
+### 1. Revert the code to the previous version
+Follow the [`upgrade guide from 5.2 to 5.3`](5.2-to-5.3.md), except for the database migration
+(The backup is already migrated to the previous version)
+
+### 2. Restore from the backup:
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
+```
diff --git a/doc/update/5.1-to-6.0.md b/doc/update/5.1-to-6.0.md
index 6105d4b53bc..fa0f9ce54b6 100644
--- a/doc/update/5.1-to-6.0.md
+++ b/doc/update/5.1-to-6.0.md
@@ -1,5 +1,8 @@
# From 5.1 to 6.0
+## Warning
+GitLab 6.0 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489. Please [update to GitLab 6.2 immediately](6.0-to-6.2.md).
+
### Deprecations
#### Global projects
@@ -44,7 +47,7 @@ sudo -u git -H git checkout 6-0-stable
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch
-sudo -u git -H git checkout v1.7.0
+sudo -u git -H git checkout v1.7.9
```
### 4. Install additional packages
@@ -84,8 +87,8 @@ sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
Note: We switched from Puma in GitLab 5.x to unicorn in GitLab 6.0.
-* Make `/home/git/gitlab/config/gitlab.yml` the same as https://github.com/gitlabhq/gitlabhq/blob/master/config/gitlab.yml.example but with your settings.
-* Make `/home/git/gitlab/config/unicorn.rb` the same as https://github.com/gitlabhq/gitlabhq/blob/master/config/unicorn.rb.example but with your settings.
+* Make `/home/git/gitlab/config/gitlab.yml` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/masterconfig/gitlab.yml.example but with your settings.
+* Make `/home/git/gitlab/config/unicorn.rb` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/masterconfig/unicorn.rb.example but with your settings.
### 7. Update Init script
diff --git a/doc/update/5.2-to-5.3.md b/doc/update/5.2-to-5.3.md
index a8bb530902c..7f89f6bf887 100644
--- a/doc/update/5.2-to-5.3.md
+++ b/doc/update/5.2-to-5.3.md
@@ -1,5 +1,8 @@
# From 5.2 to 5.3
+## Warning
+GitLab 5.3 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489. Please [update to GitLab 5.4 directly](5.1-to-5.4.md).
+
### 0. Backup
It's useful to make a backup just in case things go south:
@@ -40,8 +43,8 @@ sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
### 4. Update config files
-* Make `/home/git/gitlab/config/gitlab.yml` same as https://github.com/gitlabhq/gitlabhq/blob/5-3-stable/config/gitlab.yml.example but with your settings.
-* Make `/home/git/gitlab/config/puma.rb` same as https://github.com/gitlabhq/gitlabhq/blob/5-3-stable/config/puma.rb.example but with your settings.
+* Make `/home/git/gitlab/config/gitlab.yml` same as https://gitlab.com/gitlab-org/gitlab-ce/blob/5-3-stable/config/gitlab.yml.example but with your settings.
+* Make `/home/git/gitlab/config/puma.rb` same as https://gitlab.com/gitlab-org/gitlab-ce/blob/5-3-stable/config/puma.rb.example but with your settings.
### 5. Update Init script
diff --git a/doc/update/5.3-to-5.4.md b/doc/update/5.3-to-5.4.md
index 315bf03a6de..7a24c11c223 100644
--- a/doc/update/5.3-to-5.4.md
+++ b/doc/update/5.3-to-5.4.md
@@ -1,5 +1,8 @@
# From 5.3 to 5.4
+## Notice
+Security vulnerabilities CVE-2013-4490 and CVE-2013-4489 have been patched in the latest version of GitLab 5.4.
+
### 0. Backup
It's useful to make a backup just in case things go south:
@@ -19,7 +22,7 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
```bash
cd /home/git/gitlab
sudo -u git -H git fetch
-sudo -u git -H git checkout 5-4-stable
+sudo -u git -H git checkout 5-4-stable # Latest version of 5-4-stable addresses CVE-2013-4489
```
### 3. Update gitlab-shell
@@ -27,7 +30,7 @@ sudo -u git -H git checkout 5-4-stable
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch
-sudo -u git -H git checkout v1.5.0
+sudo -u git -H git checkout v1.7.9 # Addresses multiple critical security vulnerabilities
```
### 4. Install libs, migrations, etc.
@@ -48,8 +51,8 @@ sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
### 5. Update config files
-* Make `/home/git/gitlab/config/gitlab.yml` same as https://github.com/gitlabhq/gitlabhq/blob/5-4-stable/config/gitlab.yml.example but with your settings.
-* Make `/home/git/gitlab/config/puma.rb` same as https://github.com/gitlabhq/gitlabhq/blob/5-4-stable/config/puma.rb.example but with your settings.
+* Make `/home/git/gitlab/config/gitlab.yml` same as https://gitlab.com/gitlab-org/gitlab-ce/blob/5-4-stable/config/gitlab.yml.example but with your settings.
+* Make `/home/git/gitlab/config/puma.rb` same as https://gitlab.com/gitlab-org/gitlab-ce/blob/5-4-stable/config/puma.rb.example but with your settings.
### 6. Update Init script
diff --git a/doc/update/5.4-to-6.0.md b/doc/update/5.4-to-6.0.md
index 3072a1da716..bcba3ee4d05 100644
--- a/doc/update/5.4-to-6.0.md
+++ b/doc/update/5.4-to-6.0.md
@@ -1,5 +1,8 @@
# From 5.4 to 6.0
+## Warning
+GitLab 6.0 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489. Please [update to GitLab 6.2 immediately](6.0-to-6.2.md).
+
### Deprecations
#### Global projects
@@ -44,7 +47,7 @@ sudo -u git -H git checkout 6-0-stable
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch
-sudo -u git -H git checkout v1.7.0
+sudo -u git -H git checkout v1.7.9
```
### 4. Install additional packages
@@ -84,8 +87,8 @@ sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
Note: We switched from Puma in GitLab 5.4 to unicorn in GitLab 6.0.
-* Make `/home/git/gitlab/config/gitlab.yml` the same as https://github.com/gitlabhq/gitlabhq/blob/master/config/gitlab.yml.example but with your settings.
-* Make `/home/git/gitlab/config/unicorn.rb` the same as https://github.com/gitlabhq/gitlabhq/blob/master/config/unicorn.rb.example but with your settings.
+* Make `/home/git/gitlab/config/gitlab.yml` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/masterconfig/gitlab.yml.example but with your settings.
+* Make `/home/git/gitlab/config/unicorn.rb` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/masterconfig/unicorn.rb.example but with your settings.
### 7. Update Init script
diff --git a/doc/update/6.0-to-6.1.md b/doc/update/6.0-to-6.1.md
index c3851a10df0..53bb69ddff8 100644
--- a/doc/update/6.0-to-6.1.md
+++ b/doc/update/6.0-to-6.1.md
@@ -1,5 +1,8 @@
# From 6.0 to 6.1
+## Warning
+GitLab 6.1 is affected by critical security vulnerabilities CVE-2013-4490 and CVE-2013-4489. Please [update to GitLab 6.2 directly](6.0-to-6.2.md).
+
# In 6.1 we remove a lot of deprecated code.
# You should update to 6.0 before installing 6.1 so all the necessary conversions are run.
@@ -36,7 +39,7 @@ sudo -u git -H git checkout 6-1-stable
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch
-sudo -u git -H git checkout v1.7.1
+sudo -u git -H git checkout v1.7.9
```
### 4. Install libs, migrations, etc.
@@ -60,8 +63,8 @@ sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
### 5. Update config files
-* Make `/home/git/gitlab/config/gitlab.yml` same as https://github.com/gitlabhq/gitlabhq/blob/6-1-stable/config/gitlab.yml.example but with your settings.
-* Make `/home/git/gitlab/config/unicorn.rb` same as https://github.com/gitlabhq/gitlabhq/blob/6-1-stable/config/unicorn.rb.example but with your settings.
+* Make `/home/git/gitlab/config/gitlab.yml` same as https://gitlab.com/gitlab-org/gitlab-ce/blob/6-1-stable/config/gitlab.yml.example but with your settings.
+* Make `/home/git/gitlab/config/unicorn.rb` same as https://gitlab.com/gitlab-org/gitlab-ce/blob/6-1-stable/config/unicorn.rb.example but with your settings.
### 6. Update Init script
diff --git a/doc/update/6.0-to-6.4.md b/doc/update/6.0-to-6.4.md
new file mode 100644
index 00000000000..c1c5abed6d6
--- /dev/null
+++ b/doc/update/6.0-to-6.4.md
@@ -0,0 +1,128 @@
+# From 6.0 to 6.4
+
+# In 6.1 we remove a lot of deprecated code.
+# You should update to 6.0 before installing 6.1 or higher so all the necessary conversions are run.
+
+### Deprecations
+
+#### Global issue numbers
+
+As of 6.1 issue numbers are project specific. This means all issues are renumbered and get a new number in their url. If you use an old issue number url and the issue number does not exist yet you are redirected to the new one. This conversion does not trigger if the old number already exists for this project, this is unlikely but will happen with old issues and large projects.
+
+### 0. Backup
+
+It's useful to make a backup just in case things go south:
+(With MySQL, this may require granting "LOCK TABLES" privileges to the GitLab user on the database version)
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
+```
+
+### 1. Stop server
+
+ sudo service gitlab stop
+
+### 2. Get latest code
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H git fetch
+sudo -u git -H git checkout 6-4-stable
+```
+
+
+### 3. Install additional packages
+
+```bash
+# Add support for lograte for better log file handling
+sudo apt-get install logrotate
+```
+
+### 4. Update gitlab-shell
+
+```bash
+cd /home/git/gitlab-shell
+sudo -u git -H git fetch
+sudo -u git -H git checkout v1.8.0 # Addresses multiple critical security vulnerabilities
+```
+
+### 5. Install libs, migrations, etc.
+
+```bash
+cd /home/git/gitlab
+
+# MySQL
+sudo -u git -H bundle install --without development test postgres --deployment
+
+#PostgreSQL
+sudo -u git -H bundle install --without development test mysql --deployment
+
+
+sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
+sudo -u git -H bundle exec rake migrate_iids RAILS_ENV=production
+sudo -u git -H bundle exec rake assets:clean RAILS_ENV=production
+sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
+sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
+```
+
+### 6. Update config files
+
+TIP: to see what changed in gitlab.yml.example in this release use next command:
+
+```
+git diff 6-0-stable:config/gitlab.yml.example 6-4-stable:config/gitlab.yml.example
+```
+
+* Make `/home/git/gitlab/config/gitlab.yml` same as https://gitlab.com/gitlab-org/gitlab-ce/blob/6-4-stable/config/gitlab.yml.example but with your settings.
+* Make `/home/git/gitlab/config/unicorn.rb` same as https://gitlab.com/gitlab-org/gitlab-ce/blob/6-4-stable/config/unicorn.rb.example but with your settings.
+* Copy rack attack middleware config
+
+```bash
+sudo -u git -H cp config/initializers/rack_attack.rb.example config/initializers/rack_attack.rb
+```
+* Uncomment `config.middleware.use Rack::Attack` in `/home/git/gitlab/config/application.rb`
+* Set up logrotate
+
+```bash
+sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab
+```
+
+### 7. Update Init script
+
+```bash
+sudo rm /etc/init.d/gitlab
+sudo curl --output /etc/init.d/gitlab https://raw.github.com/gitlabhq/gitlabhq/6-4-stable/lib/support/init.d/gitlab
+sudo chmod +x /etc/init.d/gitlab
+```
+
+### 8. Start application
+
+ sudo service gitlab start
+ sudo service nginx restart
+
+### 9. Check application status
+
+Check if GitLab and its environment are configured correctly:
+
+ cd /home/git/gitlab
+ sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
+
+To make sure you didn't miss anything run a more thorough check with:
+
+ sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+
+If all items are green, then congratulations upgrade complete!
+
+## Things went south? Revert to previous version (6.0)
+
+### 1. Revert the code to the previous version
+Follow the [`upgrade guide from 5.4 to 6.0`](5.4-to-6.0.md), except for the database migration
+(The backup is already migrated to the previous version)
+
+### 2. Restore from the backup:
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
+```
diff --git a/doc/update/6.1-to-6.2.md b/doc/update/6.1-to-6.2.md
index 6f0514a8610..a3849faf065 100644
--- a/doc/update/6.1-to-6.2.md
+++ b/doc/update/6.1-to-6.2.md
@@ -1,5 +1,8 @@
# From 6.1 to 6.2
+## Notice
+Security vulnerabilities CVE-2013-4490 and CVE-2013-4489 have been patched in the latest version of GitLab 6.2.
+
# You should update to 6.1 before installing 6.2 so all the necessary conversions are run.
### 0. Backup
@@ -21,17 +24,25 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
```bash
cd /home/git/gitlab
sudo -u git -H git fetch
-sudo -u git -H git checkout 6-2-stable
+sudo -u git -H git checkout 6-2-stable # Latest version of 6-2-stable addresses CVE-2013-4489
+```
+
+### 3. Update gitlab-shell
+
+```bash
+cd /home/git/gitlab-shell
+sudo -u git -H git fetch
+sudo -u git -H git checkout v1.7.9 # Addresses multiple critical security vulnerabilities
```
-### 3. Install additional packages
+### 4. Install additional packages
```bash
# Add support for lograte for better log file handling
sudo apt-get install logrotate
```
-### 4. Install libs, migrations, etc.
+### 5. Install libs, migrations, etc.
```bash
cd /home/git/gitlab
@@ -49,7 +60,7 @@ sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
```
-### 5. Update config files
+### 6. Update config files
TIP: to see what changed in gitlab.yml.example in this release use next command:
@@ -57,8 +68,8 @@ TIP: to see what changed in gitlab.yml.example in this release use next command:
git diff 6-1-stable:config/gitlab.yml.example 6-2-stable:config/gitlab.yml.example
```
-* Make `/home/git/gitlab/config/gitlab.yml` same as https://github.com/gitlabhq/gitlabhq/blob/6-2-stable/config/gitlab.yml.example but with your settings.
-* Make `/home/git/gitlab/config/unicorn.rb` same as https://github.com/gitlabhq/gitlabhq/blob/6-2-stable/config/unicorn.rb.example but with your settings.
+* Make `/home/git/gitlab/config/gitlab.yml` same as https://gitlab.com/gitlab-org/gitlab-ce/blob/6-2-stable/config/gitlab.yml.example but with your settings.
+* Make `/home/git/gitlab/config/unicorn.rb` same as https://gitlab.com/gitlab-org/gitlab-ce/blob/6-2-stable/config/unicorn.rb.example but with your settings.
* Copy rack attack middleware config
```bash
@@ -71,7 +82,7 @@ sudo -u git -H cp config/initializers/rack_attack.rb.example config/initializers
sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab
```
-### 6. Update Init script
+### 7. Update Init script
```bash
sudo rm /etc/init.d/gitlab
@@ -79,12 +90,12 @@ sudo curl --output /etc/init.d/gitlab https://raw.github.com/gitlabhq/gitlabhq/6
sudo chmod +x /etc/init.d/gitlab
```
-### 7. Start application
+### 8. Start application
sudo service gitlab start
sudo service nginx restart
-### 8. Check application status
+### 9. Check application status
Check if GitLab and its environment are configured correctly:
diff --git a/doc/update/6.2-to-6.3.md b/doc/update/6.2-to-6.3.md
new file mode 100644
index 00000000000..5b02b4cb4dd
--- /dev/null
+++ b/doc/update/6.2-to-6.3.md
@@ -0,0 +1,108 @@
+# From 6.2 to 6.3
+
+## Requires version: 6.1 or 6.2
+
+### 0. Backup
+
+It's useful to make a backup just in case things go south:
+(With MySQL, this may require granting "LOCK TABLES" privileges to the GitLab user on the database version)
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
+```
+
+### 1. Stop server
+
+ sudo service gitlab stop
+
+### 2. Get latest code
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H git fetch
+sudo -u git -H git checkout 6-3-stable
+```
+
+### 3. Update gitlab-shell (and its config)
+
+```bash
+cd /home/git/gitlab-shell
+sudo -u git -H git fetch
+sudo -u git -H git checkout v1.7.9 # Addresses multiple critical security vulnerabilities
+```
+
+The Gitlab-shell config changed recently, so check for config file changes and make `/home/git/gitlab-shell/config.yml` the same as https://github.com/gitlabhq/gitlab-shell/blob/master/config.yml.example
+
+### 4. Install libs, migrations, etc.
+
+```bash
+cd /home/git/gitlab
+
+# MySQL
+sudo -u git -H bundle install --without development test postgres --deployment
+
+# PostgreSQL
+sudo -u git -H bundle install --without development test mysql --deployment
+
+
+# Run database migrations
+sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
+
+# Clean up assets and cache
+sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
+```
+
+### 5. Update config files
+
+TIP: to see what changed in gitlab.yml.example in this release use next command:
+
+```
+git diff 6-2-stable:config/gitlab.yml.example 6-3-stable:config/gitlab.yml.example
+```
+
+* Make `/home/git/gitlab/config/gitlab.yml` same as https://gitlab.com/gitlab-org/gitlab-ce/blob/6-3-stable/config/gitlab.yml.example but with your settings.
+* Make `/home/git/gitlab/config/unicorn.rb` same as https://gitlab.com/gitlab-org/gitlab-ce/blob/6-3-stable/config/unicorn.rb.example but with your settings.
+
+```bash
+# Copy rack attack middleware config
+cd /home/git/gitlab
+sudo -u git -H cp config/initializers/rack_attack.rb.example config/initializers/rack_attack.rb
+```
+
+### 6. Update Init script
+
+```bash
+sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
+sudo chmod +x /etc/init.d/gitlab
+```
+
+### 7. Start application
+
+ sudo service gitlab start
+ sudo service nginx restart
+
+### 8. Check application status
+
+Check if GitLab and its environment are configured correctly:
+
+ sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
+
+To make sure you didn't miss anything run a more thorough check with:
+
+ sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+
+If all items are green, then congratulations upgrade complete!
+
+## Things went south? Revert to previous version (6.2)
+
+### 1. Revert the code to the previous version
+Follow the [`upgrade guide from 6.1 to 6.2`](6.1-to-6.2.md), except for the database migration
+(The backup is already migrated to the previous version)
+
+### 2. Restore from the backup:
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
+```
diff --git a/doc/update/6.3-to-6.4.md b/doc/update/6.3-to-6.4.md
new file mode 100644
index 00000000000..69d184722cb
--- /dev/null
+++ b/doc/update/6.3-to-6.4.md
@@ -0,0 +1,80 @@
+# From 6.3 to 6.4
+
+### 0. Backup
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
+```
+
+### 1. Stop server
+
+ sudo service gitlab stop
+
+### 2. Get latest code
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H git fetch
+sudo -u git -H git checkout 6-4-stable
+```
+
+### 3. Update gitlab-shell (and its config)
+
+```bash
+cd /home/git/gitlab-shell
+sudo -u git -H git fetch
+sudo -u git -H git checkout v1.8.0
+```
+
+### 4. Install libs, migrations, etc.
+
+```bash
+cd /home/git/gitlab
+
+# MySQL
+sudo -u git -H bundle install --without development test postgres --deployment
+
+# PostgreSQL
+sudo -u git -H bundle install --without development test mysql --deployment
+
+
+# Run database migrations
+sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
+
+# Clean up assets and cache
+sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
+
+# Update init.d script
+sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
+```
+
+### 5. Start application
+
+ sudo service gitlab start
+ sudo service nginx restart
+
+### 8. Check application status
+
+Check if GitLab and its environment are configured correctly:
+
+ sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
+
+To make sure you didn't miss anything run a more thorough check with:
+
+ sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+
+If all items are green, then congratulations upgrade complete!
+
+## Things went south? Revert to previous version (6.3)
+
+### 1. Revert the code to the previous version
+Follow the [`upgrade guide from 6.2 to 6.3`](6.2-to-6.3.md), except for the database migration
+(The backup is already migrated to the previous version)
+
+### 2. Restore from the backup:
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
+```
diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md
index 797fc81c92b..b284ff48365 100644
--- a/doc/update/patch_versions.md
+++ b/doc/update/patch_versions.md
@@ -1,4 +1,4 @@
-# Universal update guide for patch versions. Ex. from From 6.2.0 to 6.2.1
+# Universal update guide for patch versions. For example from 6.2.0 to 6.2.1, also see the [semantic versioning specification](http://semver.org/).
### 0. Backup
@@ -14,14 +14,26 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
sudo service gitlab stop
-### 2. Get latest code for your current stable branch
+### 2. Get latest code for the stable branch
```bash
cd /home/git/gitlab
-sudo -u git -H git pull origin 6-2-stable
+sudo -u git -H git pull origin STABLE_BRANCH
```
-### 3. Install libs, migrations, etc.
+Replace STABLE_BRANCH with the minor version you want to upgrade to, for example `6-3-stable`.
+
+### 3. Update gitlab-shell if it is not the latest version
+
+```bash
+cd /home/git/gitlab-shell
+sudo -u git -H git fetch
+sudo -u git -H git checkout LATEST_TAG
+```
+
+Replace LATEST_TAG with the latest GitLab Shell tag you want to upgrade to, for example `v1.7.9`.
+
+### 4. Install libs, migrations, etc.
```bash
cd /home/git/gitlab
@@ -38,12 +50,12 @@ sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
```
-### 4. Start application
+### 5. Start application
sudo service gitlab start
sudo service nginx restart
-### 5. Check application status
+### 6. Check application status
Check if GitLab and its environment are configured correctly:
diff --git a/doc/update/ruby.md b/doc/update/ruby.md
new file mode 100644
index 00000000000..3fc068c0ae2
--- /dev/null
+++ b/doc/update/ruby.md
@@ -0,0 +1,54 @@
+# Updating Ruby from source
+
+This guide explains how to update Ruby in case you installed it from source according to the instructions in https://gitlab.com/gitlab-org/gitlab-ce/blob/masterdoc/install/installation.md#2-ruby .
+
+### 1. Look for Ruby versions
+This guide will only update `/usr/local/bin/ruby`. You can see which Ruby binaries are installed on your system by running:
+
+```bash
+ls -l $(which -a ruby)
+```
+
+### 2. Stop GitLab
+
+```bash
+sudo service gitlab stop
+```
+
+### 3. Install or update dependencies
+Here we are assuming you are using Debian/Ubuntu.
+
+```bash
+sudo apt-get install build-essential zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libreadline-dev libncurses5-dev libffi-dev curl
+```
+
+### 4. Download, compile and install Ruby
+Find the latest stable version of Ruby 1.9 or 2.0 at https://www.ruby-lang.org/en/downloads/ . We recommend at least 2.0.0-p353, which is patched against [CVE-2013-4164](https://www.ruby-lang.org/en/news/2013/11/22/heap-overflow-in-floating-point-parsing-cve-2013-4164/).
+
+```bash
+cd /tmp
+curl --progress http://cache.ruby-lang.org/pub/ruby/2.0/ruby-2.0.0-p353.tar.gz | tar xz
+cd ruby-2.0.0-p353
+./configure --disable-install-rdoc
+make
+sudo make install # overwrite the existing Ruby in /usr/local/bin
+sudo gem install bundler
+```
+
+### 5. Reinstall GitLab gem bundle
+Just to be sure we will reinstall the gems used by GitLab. Note that the `bundle install` command [depends on your choice of database](https://gitlab.com/gitlab-org/gitlab-ce/blob/masterdoc/install/installation.md#install-gems).
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H rm -rf vendor/bundle # remove existing Gem bundle
+sudo -u git -H bundle install --deployment --without development test postgres aws # Assuming MySQL
+```
+
+### 6. Start GitLab
+We are now ready to restart GitLab.
+
+```bash
+sudo service gitlab start
+```
+
+### Done
diff --git a/doc/update/upgrader.md b/doc/update/upgrader.md
new file mode 100644
index 00000000000..1f51005a8e9
--- /dev/null
+++ b/doc/update/upgrader.md
@@ -0,0 +1,30 @@
+# GitLab Upgrader
+
+GitLab Upgrader - ruby script that allows you easily upgrade GitLab to latest minor version.
+Ex. it can update your application from 6.4 to latest GitLab 6 version (like 6.5.1).
+You still need to create backup and manually restart GitLab but all other operations can be done by upgrade tool.
+
+__GitLab Upgrader is available only for version 6.4.2 or higher__
+
+### 0. Backup
+
+ cd /home/git/gitlab
+ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
+
+### 1. Stop server
+
+ sudo service gitlab stop
+
+### 2. Run gitlab upgrade tool
+
+ cd /home/git/gitlab
+ sudo -u git -H ruby script/upgrade.rb
+
+ # it also supports -y option to avouid user input
+ # sudo -u git -H ruby script/upgrade.rb -y
+
+
+### 3. Start application
+
+ sudo service gitlab start
+ sudo service nginx restart
diff --git a/features/admin/active_tab.feature b/features/admin/active_tab.feature
index 226d3d5d5b5..15fcda45e40 100644
--- a/features/admin/active_tab.feature
+++ b/features/admin/active_tab.feature
@@ -27,6 +27,11 @@ Feature: Admin active tab
Then the active main tab should be Logs
And no other main tabs should be active
+ Scenario: On Admin Messages
+ Given I visit admin messages page
+ Then the active main tab should be Messages
+ And no other main tabs should be active
+
Scenario: On Admin Hooks
Given I visit admin hooks page
Then the active main tab should be Hooks
diff --git a/features/admin/broadcast_messages.feature b/features/admin/broadcast_messages.feature
new file mode 100644
index 00000000000..5f16120b7cc
--- /dev/null
+++ b/features/admin/broadcast_messages.feature
@@ -0,0 +1,20 @@
+Feature: Admin Broadcast Messages
+ Background:
+ Given I sign in as an admin
+ And application already has admin messages
+ And I visit admin messages page
+
+ Scenario: See broadcast messages list
+ Then I should be all broadcast messages
+
+ Scenario: Create a broadcast message
+ When submit form with new broadcast message
+ Then I should be redirected to admin messages page
+ And I should see newly created broadcast message
+
+ Scenario: Create a customized broadcast message
+ When submit form with new customized broadcast message
+ Then I should be redirected to admin messages page
+ And I should see newly created broadcast message
+ Then I visit dashboard page
+ And I should see a customized broadcast message
diff --git a/features/dashboard/archived_projects.feature b/features/dashboard/archived_projects.feature
new file mode 100644
index 00000000000..399c9b53d81
--- /dev/null
+++ b/features/dashboard/archived_projects.feature
@@ -0,0 +1,16 @@
+Feature: Dashboard with archived projects
+ Background:
+ Given I sign in as a user
+ And I own project "Shop"
+ And I own project "Forum"
+ And project "Forum" is archived
+ And I visit dashboard page
+
+ Scenario: I should see non-archived projects on dashboard
+ Then I should see "Shop" project link
+ And I should not see "Forum" project link
+
+ Scenario: I should see all projects on projects page
+ And I visit dashboard projects page
+ Then I should see "Shop" project link
+ And I should see "Forum" project link
diff --git a/features/dashboard/issues.feature b/features/dashboard/issues.feature
index 895b89aa38a..d316b2d9205 100644
--- a/features/dashboard/issues.feature
+++ b/features/dashboard/issues.feature
@@ -1,8 +1,18 @@
Feature: Dashboard Issues
Background:
Given I sign in as a user
+ And I have authored issues
And I have assigned issues
+ And I have other issues
And I visit dashboard issues page
- Scenario: I should see issues list
+ Scenario: I should see assigned issues
Then I should see issues assigned to me
+
+ Scenario: I should see authored issues
+ When I click "Authored by me" link
+ Then I should see issues authored by me
+
+ Scenario: I should see all issues
+ When I click "All" link
+ Then I should see all issues
diff --git a/features/dashboard/merge_requests.feature b/features/dashboard/merge_requests.feature
index cad65b0d79a..de560300735 100644
--- a/features/dashboard/merge_requests.feature
+++ b/features/dashboard/merge_requests.feature
@@ -2,7 +2,17 @@ Feature: Dashboard Merge Requests
Background:
Given I sign in as a user
And I have authored merge requests
+ And I have assigned merge requests
+ And I have other merge requests
And I visit dashboard merge requests page
- Scenario: I should see projects list
- Then I should see my merge requests
+ Scenario: I should see assigned merge_requests
+ Then I should see merge requests assigned to me
+
+ Scenario: I should see authored merge_requests
+ When I click "Authored by me" link
+ Then I should see merge requests authored by me
+
+ Scenario: I should see all merge_requests
+ When I click "All" link
+ Then I should see all merge requests
diff --git a/features/profile/profile.feature b/features/profile/profile.feature
index 6198fd2b306..6b0421a20b3 100644
--- a/features/profile/profile.feature
+++ b/features/profile/profile.feature
@@ -26,6 +26,14 @@ Feature: Profile
Given I visit profile page
Then I change my avatar
And I should see new avatar
+ And I should see the "Remove avatar" button
+
+ Scenario: I remove my avatar
+ Given I visit profile page
+ And I have an avatar
+ When I remove my avatar
+ Then I should see my gravatar
+ And I should not see the "Remove avatar" button
Scenario: My password is expired
Given my password is expired
diff --git a/features/project/archived_projects.feature b/features/project/archived_projects.feature
new file mode 100644
index 00000000000..9aac29384ba
--- /dev/null
+++ b/features/project/archived_projects.feature
@@ -0,0 +1,39 @@
+Feature: Project Archived
+ Background:
+ Given I sign in as a user
+ And I own project "Shop"
+ And I own project "Forum"
+
+ Scenario: I should not see archived on project page of not-archive project
+ And project "Forum" is archived
+ And I visit project "Shop" page
+ Then I should not see "Archived"
+
+ Scenario: I should see archived on project page of archive project
+ And project "Forum" is archived
+ And I visit project "Forum" page
+ Then I should see "Archived"
+
+ Scenario: I should not see archived on projects page with no archived projects
+ And I visit dashboard projects page
+ Then I should not see "Archived"
+
+ Scenario: I should see archived on projects page with archived projects
+ And project "Forum" is archived
+ And I visit dashboard projects page
+ Then I should see "Archived"
+
+ Scenario: I archive project
+ When project "Shop" has push event
+ And I visit project "Shop" page
+ And I visit edit project "Shop" page
+ And I set project archived
+ Then I should see "Archived"
+
+ Scenario: I unarchive project
+ When project "Shop" has push event
+ And project "Shop" is archived
+ And I visit project "Shop" page
+ And I visit edit project "Shop" page
+ And I set project unarchived
+ Then I should not see "Archived"
diff --git a/features/project/commits/commits.feature b/features/project/commits/commits.feature
index fe470f5ac99..cbe8b321507 100644
--- a/features/project/commits/commits.feature
+++ b/features/project/commits/commits.feature
@@ -14,6 +14,12 @@ Feature: Project Browse commits
Scenario: I browse commit from list
Given I click on commit link
Then I see commit info
+ And I see side-by-side diff button
+
+ Scenario: I browse commit with side-by-side diff view
+ Given I click on commit link
+ And I click side-by-side diff button
+ Then I see inline diff button
Scenario: I compare refs
Given I visit compare refs page
diff --git a/features/project/edit_issuetracker.feature b/features/project/edit_issuetracker.feature
new file mode 100644
index 00000000000..b5477d3c7ab
--- /dev/null
+++ b/features/project/edit_issuetracker.feature
@@ -0,0 +1,18 @@
+Feature: Project Issue Tracker
+ Background:
+ Given I sign in as a user
+ And I own project "Shop"
+ And project "Shop" has issues enabled
+ And I visit project "Shop" page
+
+ Scenario: I set the issue tracker to "GitLab"
+ When I visit edit project "Shop" page
+ And change the issue tracker to "GitLab"
+ And I save project
+ Then I the project should have "GitLab" as issue tracker
+
+ Scenario: I set the issue tracker to "Redmine"
+ When I visit edit project "Shop" page
+ And change the issue tracker to "Redmine"
+ And I save project
+ Then I the project should have "Redmine" as issue tracker \ No newline at end of file
diff --git a/features/project/network.feature b/features/project/network.feature
index ceae08c1074..22beb1c50bc 100644
--- a/features/project/network.feature
+++ b/features/project/network.feature
@@ -29,11 +29,11 @@ Feature: Project Network Graph
@javascript
Scenario: I should filter selected tag
When I switch ref to "v2.1.0"
- Then page should have content not cotaining "v2.1.0"
+ Then page should have content not containing "v2.1.0"
When click "Show only selected branch" checkbox
- Then page should not have content not cotaining "v2.1.0"
+ Then page should not have content not containing "v2.1.0"
When click "Show only selected branch" checkbox
- Then page should have content not cotaining "v2.1.0"
+ Then page should have content not containing "v2.1.0"
Scenario: I should fail to look for a commit
When I look for a commit by ";"
diff --git a/features/project/project.feature b/features/project/project.feature
index 59eda4a781d..d8bb1d55e2d 100644
--- a/features/project/project.feature
+++ b/features/project/project.feature
@@ -19,3 +19,8 @@ Feature: Project Feature
And change project settings
And I save project
Then I should see project with new settings
+
+ Scenario: I change project path
+ When I visit edit project "Shop" page
+ And change project path settings
+ Then I should see project with new path settings
diff --git a/features/project/redirects.feature b/features/project/redirects.feature
new file mode 100644
index 00000000000..ce197912f64
--- /dev/null
+++ b/features/project/redirects.feature
@@ -0,0 +1,26 @@
+Feature: Project Redirects
+ Background:
+ Given public project "Community"
+ And private project "Enterprise"
+
+ Scenario: I visit public project page
+ When I visit project "Community" page
+ Then I should see project "Community" home page
+
+ Scenario: I visit private project page
+ When I visit project "Enterprise" page
+ Then I should be redirected to sign in page
+
+ Scenario: I visit a non-existent project page
+ When I visit project "CommunityDoesNotExist" page
+ Then I should be redirected to sign in page
+
+ Scenario: I visit a non-existent project page as user
+ Given I sign in as a user
+ When I visit project "CommunityDoesNotExist" page
+ Then page status code should be 404
+
+ Scenario: I visit unauthorized project page as user
+ Given I sign in as a user
+ When I visit project "Enterprise" page
+ Then page status code should be 404
diff --git a/features/project/service.feature b/features/project/service.feature
index 4805d2befbe..46b983e8f9a 100644
--- a/features/project/service.feature
+++ b/features/project/service.feature
@@ -30,3 +30,15 @@ Feature: Project Services
And I click Flowdock service link
And I fill Flowdock settings
Then I should see Flowdock service settings saved
+
+ Scenario: Activate Assembla service
+ When I visit project "Shop" services page
+ And I click Assembla service link
+ And I fill Assembla settings
+ Then I should see Assembla service settings saved
+
+ Scenario: Activate email on push service
+ When I visit project "Shop" services page
+ And I click email on push service link
+ And I fill email on push settings
+ Then I should see email on push service settings saved
diff --git a/features/project/source/browse_files.feature b/features/project/source/browse_files.feature
index ee26f5371a9..fd9a2f01a28 100644
--- a/features/project/source/browse_files.feature
+++ b/features/project/source/browse_files.feature
@@ -20,6 +20,10 @@ Feature: Project Browse files
And I click link "raw"
Then I should see raw file content
+ Scenario: I can create file
+ Given I click on "new file" link in repo
+ Then I can see new file page
+
@javascript
Scenario: I can edit file
Given I click on "Gemfile.lock" file in repo
diff --git a/features/project/source/multiselect_blob.feature b/features/project/source/multiselect_blob.feature
new file mode 100644
index 00000000000..3038c0814ad
--- /dev/null
+++ b/features/project/source/multiselect_blob.feature
@@ -0,0 +1,86 @@
+Feature: Project Multiselect Blob
+ Background:
+ Given I sign in as a user
+ And I own project "Shop"
+ And I visit project source page
+ And I click on "Gemfile.lock" file in repo
+
+ @javascript
+ Scenario: I click line 1 in file
+ When I click line 1 in file
+ Then I should see "L1" as URI fragment
+ And I should see line 1 highlighted
+
+ @javascript
+ Scenario: I shift-click line 1 in file
+ When I shift-click line 1 in file
+ Then I should see "L1" as URI fragment
+ And I should see line 1 highlighted
+
+ @javascript
+ Scenario: I click line 1 then click line 2 in file
+ When I click line 1 in file
+ Then I should see "L1" as URI fragment
+ And I should see line 1 highlighted
+ Then I click line 2 in file
+ Then I should see "L2" as URI fragment
+ And I should see line 2 highlighted
+
+ @javascript
+ Scenario: I click various line numbers to test multiselect
+ Then I click line 1 in file
+ Then I should see "L1" as URI fragment
+ And I should see line 1 highlighted
+ Then I shift-click line 2 in file
+ Then I should see "L1-2" as URI fragment
+ And I should see lines 1-2 highlighted
+ Then I shift-click line 3 in file
+ Then I should see "L1-3" as URI fragment
+ And I should see lines 1-3 highlighted
+ Then I click line 3 in file
+ Then I should see "L3" as URI fragment
+ And I should see line 3 highlighted
+ Then I shift-click line 1 in file
+ Then I should see "L1-3" as URI fragment
+ And I should see lines 1-3 highlighted
+ Then I shift-click line 5 in file
+ Then I should see "L1-5" as URI fragment
+ And I should see lines 1-5 highlighted
+ Then I shift-click line 4 in file
+ Then I should see "L1-4" as URI fragment
+ And I should see lines 1-4 highlighted
+ Then I click line 5 in file
+ Then I should see "L5" as URI fragment
+ And I should see line 5 highlighted
+ Then I shift-click line 3 in file
+ Then I should see "L3-5" as URI fragment
+ And I should see lines 3-5 highlighted
+ Then I shift-click line 1 in file
+ Then I should see "L1-3" as URI fragment
+ And I should see lines 1-3 highlighted
+ Then I shift-click line 1 in file
+ Then I should see "L1" as URI fragment
+ And I should see line 1 highlighted
+
+ @javascript
+ Scenario: I multiselect lines 1-5 and then go back and forward in history
+ When I click line 1 in file
+ And I shift-click line 3 in file
+ And I shift-click line 2 in file
+ And I shift-click line 5 in file
+ Then I should see "L1-5" as URI fragment
+ And I should see lines 1-5 highlighted
+ Then I go back in history
+ Then I should see "L1-2" as URI fragment
+ And I should see lines 1-2 highlighted
+ Then I go back in history
+ Then I should see "L1-3" as URI fragment
+ And I should see lines 1-3 highlighted
+ Then I go back in history
+ Then I should see "L1" as URI fragment
+ And I should see line 1 highlighted
+ Then I go forward in history
+ And I go forward in history
+ And I go forward in history
+ Then I should see "L1-5" as URI fragment
+ And I should see lines 1-5 highlighted \ No newline at end of file
diff --git a/features/public/public_projects.feature b/features/public/public_projects.feature
index 178a769194c..d6574ca900e 100644
--- a/features/public/public_projects.feature
+++ b/features/public/public_projects.feature
@@ -1,17 +1,63 @@
Feature: Public Projects Feature
Background:
Given public project "Community"
+ And internal project "Internal"
And private project "Enterprise"
Scenario: I visit public area
When I visit the public projects area
Then I should see project "Community"
+ And I should not see project "Internal"
And I should not see project "Enterprise"
Scenario: I visit public project page
When I visit project "Community" page
Then I should see project "Community" home page
+ Scenario: I visit internal project page
+ When I visit project "Internal" page
+ Then I should be redirected to sign in page
+
+ Scenario: I visit private project page
+ When I visit project "Enterprise" page
+ Then I should be redirected to sign in page
+
+ Scenario: I visit an empty public project page
+ Given public empty project "Empty Public Project"
+ When I visit empty project page
+ Then I should see empty public project details
+ And I should see empty public project details with http clone info
+
+ Scenario: I visit an empty public project page as user
+ Given I sign in as a user
+ And public empty project "Empty Public Project"
+ When I visit empty project page
+ Then I should see empty public project details
+ And I should see empty public project details with ssh clone info
+
+ Scenario: I visit public area as user
+ Given I sign in as a user
+ When I visit the public projects area
+ Then I should see project "Community"
+ And I should see project "Internal"
+ And I should not see project "Enterprise"
+
+ Scenario: I visit internal project page as user
+ Given I sign in as a user
+ When I visit project "Internal" page
+ Then I should see project "Internal" home page
+
+ Scenario: I visit public project page
+ When I visit project "Community" page
+ Then I should see project "Community" home page
+ And I should see an http link to the repository
+
+ Scenario: I visit public project page as user
+ Given I sign in as a user
+ When I visit project "Community" page
+ Then I should see project "Community" home page
+ And I should see an ssh link to the repository
+
Scenario: I visit an empty public project page
Given public empty project "Empty Public Project"
When I visit empty project page
diff --git a/features/steps/admin/admin_active_tab.rb b/features/steps/admin/admin_active_tab.rb
index f14c5f396be..ccafe09c18f 100644
--- a/features/steps/admin/admin_active_tab.rb
+++ b/features/steps/admin/admin_active_tab.rb
@@ -30,4 +30,8 @@ class AdminActiveTab < Spinach::FeatureSteps
Then 'the active main tab should be Resque' do
ensure_active_main_tab('Background Jobs')
end
+
+ Then 'the active main tab should be Messages' do
+ ensure_active_main_tab('Messages')
+ end
end
diff --git a/features/steps/admin/admin_broadcast_messages.rb b/features/steps/admin/admin_broadcast_messages.rb
new file mode 100644
index 00000000000..a35fa34a3a2
--- /dev/null
+++ b/features/steps/admin/admin_broadcast_messages.rb
@@ -0,0 +1,41 @@
+class Spinach::Features::AdminBroadcastMessages < Spinach::FeatureSteps
+ include SharedAuthentication
+ include SharedPaths
+ include SharedAdmin
+
+ step 'application already has admin messages' do
+ FactoryGirl.create(:broadcast_message, message: "Migration to new server")
+ end
+
+ step 'I should be all broadcast messages' do
+ page.should have_content "Migration to new server"
+ end
+
+ step 'submit form with new broadcast message' do
+ fill_in 'broadcast_message_message', with: 'Application update from 4:00 CST to 5:00 CST'
+ select '2018', from: "broadcast_message_ends_at_1i"
+ click_button "Add broadcast message"
+ end
+
+ step 'I should be redirected to admin messages page' do
+ current_path.should == admin_broadcast_messages_path
+ end
+
+ step 'I should see newly created broadcast message' do
+ page.should have_content 'Application update from 4:00 CST to 5:00 CST'
+ end
+
+ step 'submit form with new customized broadcast message' do
+ fill_in 'broadcast_message_message', with: 'Application update from 4:00 CST to 5:00 CST'
+ click_link "Customize colors"
+ fill_in 'broadcast_message_color', with: '#f2dede'
+ fill_in 'broadcast_message_font', with: '#b94a48'
+ select '2018', from: "broadcast_message_ends_at_1i"
+ click_button "Add broadcast message"
+ end
+
+ step 'I should see a customized broadcast message' do
+ page.should have_content 'Application update from 4:00 CST to 5:00 CST'
+ page.should have_selector %(div[style="background-color:#f2dede;color:#b94a48"])
+ end
+end
diff --git a/features/steps/dashboard/dashboard_issues.rb b/features/steps/dashboard/dashboard_issues.rb
index fcf4296ad11..9d486bd8156 100644
--- a/features/steps/dashboard/dashboard_issues.rb
+++ b/features/steps/dashboard/dashboard_issues.rb
@@ -2,19 +2,73 @@ class DashboardIssues < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
- Then 'I should see issues assigned to me' do
- issues = @user.issues
- issues.each do |issue|
- page.should have_content(issue.title[0..10])
- page.should have_content(issue.project.name)
- page.should have_link(issue.project.name)
+ step 'I should see issues assigned to me' do
+ should_see(assigned_issue)
+ should_not_see(authored_issue)
+ should_not_see(other_issue)
+ end
+
+ step 'I should see issues authored by me' do
+ should_see(authored_issue)
+ should_not_see(assigned_issue)
+ should_not_see(other_issue)
+ end
+
+ step 'I should see all issues' do
+ should_see(authored_issue)
+ should_see(assigned_issue)
+ should_see(other_issue)
+ end
+
+ step 'I have authored issues' do
+ authored_issue
+ end
+
+ step 'I have assigned issues' do
+ assigned_issue
+ end
+
+ step 'I have other issues' do
+ other_issue
+ end
+
+ step 'I click "Authored by me" link' do
+ within ".scope-filter" do
+ click_link 'Created by me'
end
end
- And 'I have assigned issues' do
- project = create :project
- project.team << [@user, :master]
+ step 'I click "All" link' do
+ within ".scope-filter" do
+ click_link 'All'
+ end
+ end
+
+ def should_see(issue)
+ page.should have_content(issue.title[0..10])
+ end
+
+ def should_not_see(issue)
+ page.should_not have_content(issue.title[0..10])
+ end
+
+ def assigned_issue
+ @assigned_issue ||= create :issue, assignee: current_user, project: project
+ end
+
+ def authored_issue
+ @authored_issue ||= create :issue, author: current_user, project: project
+ end
+
+ def other_issue
+ @other_issue ||= create :issue, project: project
+ end
- 2.times { create :issue, author: @user, assignee: @user, project: project }
+ def project
+ @project ||= begin
+ project =create :project_with_code
+ project.team << [current_user, :master]
+ project
+ end
end
end
diff --git a/features/steps/dashboard/dashboard_merge_requests.rb b/features/steps/dashboard/dashboard_merge_requests.rb
index 6c1fa39f081..f6fe47470b3 100644
--- a/features/steps/dashboard/dashboard_merge_requests.rb
+++ b/features/steps/dashboard/dashboard_merge_requests.rb
@@ -2,28 +2,73 @@ class DashboardMergeRequests < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
- Then 'I should see my merge requests' do
- merge_requests = @user.merge_requests
- merge_requests.each do |mr|
- page.should have_content(mr.title[0..10])
- page.should have_content(mr.target_project.name)
- page.should have_content(mr.source_project.name)
+ step 'I should see merge requests assigned to me' do
+ should_see(assigned_merge_request)
+ should_not_see(authored_merge_request)
+ should_not_see(other_merge_request)
+ end
+
+ step 'I should see merge requests authored by me' do
+ should_see(authored_merge_request)
+ should_not_see(assigned_merge_request)
+ should_not_see(other_merge_request)
+ end
+
+ step 'I should see all merge requests' do
+ should_see(authored_merge_request)
+ should_see(assigned_merge_request)
+ should_see(other_merge_request)
+ end
+
+ step 'I have authored merge requests' do
+ authored_merge_request
+ end
+
+ step 'I have assigned merge requests' do
+ assigned_merge_request
+ end
+
+ step 'I have other merge requests' do
+ other_merge_request
+ end
+
+ step 'I click "Authored by me" link' do
+ within ".scope-filter" do
+ click_link 'Created by me'
+ end
+ end
+
+ step 'I click "All" link' do
+ within ".scope-filter" do
+ click_link 'All'
end
end
- And 'I have authored merge requests' do
- project1_source = create :project
- project1_target= create :project
- project2_source = create :project
- project2_target = create :project
+ def should_see(merge_request)
+ page.should have_content(merge_request.title[0..10])
+ end
+ def should_not_see(merge_request)
+ page.should_not have_content(merge_request.title[0..10])
+ end
- project1_source.team << [@user, :master]
- project1_target.team << [@user, :master]
- project2_source.team << [@user, :master]
- project2_target.team << [@user, :master]
+ def assigned_merge_request
+ @assigned_merge_request ||= create :merge_request, assignee: current_user, target_project: project
+ end
+
+ def authored_merge_request
+ @authored_merge_request ||= create :merge_request, author: current_user, target_project: project
+ end
+
+ def other_merge_request
+ @other_merge_request ||= create :merge_request, target_project: project
+ end
- merge_request1 = create :merge_request, author: @user, source_project: project1_source, target_project: project1_target
- merge_request2 = create :merge_request, author: @user, source_project: project2_source, target_project: project2_target
+ def project
+ @project ||= begin
+ project =create :project_with_code
+ project.team << [current_user, :master]
+ project
+ end
end
end
diff --git a/features/steps/dashboard/dashboard_with_archived_projects.rb b/features/steps/dashboard/dashboard_with_archived_projects.rb
new file mode 100644
index 00000000000..700f4b426c3
--- /dev/null
+++ b/features/steps/dashboard/dashboard_with_archived_projects.rb
@@ -0,0 +1,22 @@
+class DashboardWithArchivedProjects < Spinach::FeatureSteps
+ include SharedAuthentication
+ include SharedPaths
+ include SharedProject
+
+ When 'project "Forum" is archived' do
+ project = Project.find_by_name "Forum"
+ project.update_attribute(:archived, true)
+ end
+
+ Then 'I should see "Shop" project link' do
+ page.should have_link "Shop"
+ end
+
+ Then 'I should not see "Forum" project link' do
+ page.should_not have_link "Forum"
+ end
+
+ Then 'I should see "Forum" project link' do
+ page.should have_link "Forum"
+ end
+end
diff --git a/features/steps/profile/profile.rb b/features/steps/profile/profile.rb
index 753e2c19bcb..7bb4aebde28 100644
--- a/features/steps/profile/profile.rb
+++ b/features/steps/profile/profile.rb
@@ -31,26 +31,49 @@ class Profile < Spinach::FeatureSteps
@user.avatar.url.should == "/uploads/user/avatar/#{ @user.id }/gitlab_logo.png"
end
+ step 'I should see the "Remove avatar" button' do
+ page.should have_link("Remove avatar")
+ end
+
+ step 'I have an avatar' do
+ attach_file(:user_avatar, File.join(Rails.root, 'public', 'gitlab_logo.png'))
+ click_button "Save changes"
+ @user.reload
+ end
+
+ step 'I remove my avatar' do
+ click_link "Remove avatar"
+ @user.reload
+ end
+
+ step 'I should see my gravatar' do
+ @user.avatar?.should be_false
+ end
+
+ step 'I should not see the "Remove avatar" button' do
+ page.should_not have_link("Remove avatar")
+ end
+
step 'I try change my password w/o old one' do
within '.update-password' do
- fill_in "user_password", with: "222333"
- fill_in "user_password_confirmation", with: "222333"
+ fill_in "user_password", with: "22233344"
+ fill_in "user_password_confirmation", with: "22233344"
click_button "Save"
end
end
step 'I change my password' do
within '.update-password' do
- fill_in "user_current_password", with: "123456"
- fill_in "user_password", with: "222333"
- fill_in "user_password_confirmation", with: "222333"
+ fill_in "user_current_password", with: "12345678"
+ fill_in "user_password", with: "22233344"
+ fill_in "user_password_confirmation", with: "22233344"
click_button "Save"
end
end
step 'I unsuccessfully change my password' do
within '.update-password' do
- fill_in "user_current_password", with: "123456"
+ fill_in "user_current_password", with: "12345678"
fill_in "user_password", with: "password"
fill_in "user_password_confirmation", with: "confirmation"
click_button "Save"
@@ -62,11 +85,7 @@ class Profile < Spinach::FeatureSteps
end
step "I should see a password error message" do
- page.should have_content "Password doesn't match confirmation"
- end
-
- step 'I should be redirected to sign in page' do
- current_path.should == new_user_session_path
+ page.should have_content "Password confirmation doesn't match"
end
step 'I reset my token' do
diff --git a/features/steps/project/project.rb b/features/steps/project/project.rb
index a96b086fae5..92728d474b2 100644
--- a/features/steps/project/project.rb
+++ b/features/steps/project/project.rb
@@ -3,16 +3,25 @@ class ProjectFeature < Spinach::FeatureSteps
include SharedProject
include SharedPaths
- And 'change project settings' do
+ step 'change project settings' do
fill_in 'project_name', with: 'NewName'
uncheck 'project_issues_enabled'
end
- And 'I save project' do
+ step 'I save project' do
click_button 'Save changes'
end
- Then 'I should see project with new settings' do
+ step 'I should see project with new settings' do
find_field('project_name').value.should == 'NewName'
end
+
+ step 'change project path settings' do
+ fill_in "project_path", with: "new-path"
+ click_button "Rename"
+ end
+
+ step 'I should see project with new path settings' do
+ project.path.should == "new-path"
+ end
end
diff --git a/features/steps/project/project_archived.rb b/features/steps/project/project_archived.rb
new file mode 100644
index 00000000000..149d293cd08
--- /dev/null
+++ b/features/steps/project/project_archived.rb
@@ -0,0 +1,37 @@
+class ProjectArchived < Spinach::FeatureSteps
+ include SharedAuthentication
+ include SharedProject
+ include SharedPaths
+
+ When 'project "Forum" is archived' do
+ project = Project.find_by_name "Forum"
+ project.update_attribute(:archived, true)
+ end
+
+ When 'project "Shop" is archived' do
+ project = Project.find_by_name "Shop"
+ project.update_attribute(:archived, true)
+ end
+
+ When 'I visit project "Forum" page' do
+ project = Project.find_by_name "Forum"
+ visit project_path(project)
+ end
+
+ Then 'I should not see "Archived"' do
+ page.should_not have_content "Archived"
+ end
+
+ Then 'I should see "Archived"' do
+ page.should have_content "Archived"
+ end
+
+ When 'I set project archived' do
+ click_link "Archive"
+ end
+
+ When 'I set project unarchived' do
+ click_link "Unarchive"
+ end
+
+end \ No newline at end of file
diff --git a/features/steps/project/project_browse_commits.rb b/features/steps/project/project_browse_commits.rb
index 650bc3a16f7..d667b58240f 100644
--- a/features/steps/project/project_browse_commits.rb
+++ b/features/steps/project/project_browse_commits.rb
@@ -88,4 +88,17 @@ class ProjectBrowseCommits < Spinach::FeatureSteps
links[0]['href'].should =~ %r{blob/bc3735004cb45cec5e0e4fa92710897a910a5957}
links[1]['href'].should =~ %r{blob/cc1ba255d6c5ffdce87a357ba7ccc397a4f4026b}
end
+
+ Given 'I click side-by-side diff button' do
+ click_link "Side-by-side Diff"
+ end
+
+ Then 'I see side-by-side diff button' do
+ page.should have_content "Side-by-side Diff"
+ end
+
+ Then 'I see inline diff button' do
+ page.should have_content "Inline Diff"
+ end
+
end
diff --git a/features/steps/project/project_browse_files.rb b/features/steps/project/project_browse_files.rb
index 71360fb6bd5..069086d5eac 100644
--- a/features/steps/project/project_browse_files.rb
+++ b/features/steps/project/project_browse_files.rb
@@ -3,42 +3,51 @@ class ProjectBrowseFiles < Spinach::FeatureSteps
include SharedProject
include SharedPaths
- Then 'I should see files from repository' do
+ step 'I should see files from repository' do
page.should have_content "app"
page.should have_content "history"
page.should have_content "Gemfile"
end
- Then 'I should see files from repository for "8470d70"' do
+ step 'I should see files from repository for "8470d70"' do
current_path.should == project_tree_path(@project, "8470d70")
page.should have_content "app"
page.should have_content "history"
page.should have_content "Gemfile"
end
- Given 'I click on "Gemfile.lock" file in repo' do
+ step 'I click on "Gemfile.lock" file in repo' do
click_link "Gemfile.lock"
end
- Then 'I should see it content' do
+ step 'I should see it content' do
page.should have_content "DEPENDENCIES"
end
- And 'I click link "raw"' do
+ step 'I click link "raw"' do
click_link "raw"
end
- Then 'I should see raw file content' do
+ step 'I should see raw file content' do
page.source.should == ValidCommit::BLOB_FILE
end
- Given 'I click button "edit"' do
+ step 'I click button "edit"' do
click_link 'edit'
end
- Then 'I can edit code' do
+ step 'I can edit code' do
page.execute_script('editor.setValue("GitlabFileEditor")')
page.evaluate_script('editor.getValue()').should == "GitlabFileEditor"
end
+ step 'I click on "new file" link in repo' do
+ click_link 'new-file-link'
+ end
+
+ step 'I can see new file page' do
+ page.should have_content "New file"
+ page.should have_content "File name"
+ page.should have_content "Commit message"
+ end
end
diff --git a/features/steps/project/project_issue_tracker.rb b/features/steps/project/project_issue_tracker.rb
new file mode 100644
index 00000000000..a05d7a0bc37
--- /dev/null
+++ b/features/steps/project/project_issue_tracker.rb
@@ -0,0 +1,31 @@
+class ProjectIssueTracker < Spinach::FeatureSteps
+ include SharedAuthentication
+ include SharedProject
+ include SharedPaths
+
+ step 'project "Shop" has issues enabled' do
+ @project = Project.find_by_name "Shop"
+ @project ||= create(:project_with_code, name: "Shop", namespace: @user.namespace)
+ @project.issues_enabled = true
+ end
+
+ step 'change the issue tracker to "GitLab"' do
+ select 'GitLab', from: 'project_issues_tracker'
+ end
+
+ step 'I the project should have "GitLab" as issue tracker' do
+ find_field('project_issues_tracker').value.should == 'gitlab'
+ end
+
+ step 'change the issue tracker to "Redmine"' do
+ select 'Redmine', from: 'project_issues_tracker'
+ end
+
+ step 'I the project should have "Redmine" as issue tracker' do
+ find_field('project_issues_tracker').value.should == 'redmine'
+ end
+
+ And 'I save project' do
+ click_button 'Save changes'
+ end
+end
diff --git a/features/steps/project/project_markdown_render.rb b/features/steps/project/project_markdown_render.rb
index 95d73c93621..da044e46ebd 100644
--- a/features/steps/project/project_markdown_render.rb
+++ b/features/steps/project/project_markdown_render.rb
@@ -124,7 +124,7 @@ class Spinach::Features::ProjectMarkdownRender < Spinach::FeatureSteps
Then 'I see new wiki page named test' do
current_path.should == project_wiki_path(@project, "test")
- page.should have_content "Editing page"
+ page.should have_content "Editing"
end
When 'I go back to wiki page home' do
diff --git a/features/steps/project/project_merge_requests.rb b/features/steps/project/project_merge_requests.rb
index 3dc934a6118..7b0f8cc7ad2 100644
--- a/features/steps/project/project_merge_requests.rb
+++ b/features/steps/project/project_merge_requests.rb
@@ -115,19 +115,26 @@ class ProjectMergeRequests < Spinach::FeatureSteps
And 'I leave a comment on the diff page' do
init_diff_note
- within('.js-temp-notes-holder') do
+ within('.js-discussion-note-form') do
fill_in "note_note", with: "One comment to rule them all"
click_button "Add Comment"
end
+
+ within ".note-text" do
+ page.should have_content "One comment to rule them all"
+ end
end
And 'I leave a comment like "Line is wrong" on line 185 of the first file' do
init_diff_note
- within(".js-temp-notes-holder") do
+ within(".js-discussion-note-form") do
fill_in "note_note", with: "Line is wrong"
click_button "Add Comment"
- sleep 0.05
+ end
+
+ within ".note-text" do
+ page.should have_content "Line is wrong"
end
end
diff --git a/features/steps/project/project_multiselect_blob.rb b/features/steps/project/project_multiselect_blob.rb
new file mode 100644
index 00000000000..3d330e837c1
--- /dev/null
+++ b/features/steps/project/project_multiselect_blob.rb
@@ -0,0 +1,58 @@
+class ProjectMultiselectBlob < Spinach::FeatureSteps
+ include SharedAuthentication
+ include SharedProject
+ include SharedPaths
+
+ class << self
+ def click_line_steps(*line_numbers)
+ line_numbers.each do |line_number|
+ step "I click line #{line_number} in file" do
+ find("#L#{line_number}").click
+ end
+
+ step "I shift-click line #{line_number} in file" do
+ script = "$('#L#{line_number}').trigger($.Event('click', { shiftKey: true }));"
+ page.evaluate_script(script)
+ end
+ end
+ end
+
+ def check_state_steps(*ranges)
+ ranges.each do |range|
+ fragment = range.kind_of?(Array) ? "L#{range.first}-#{range.last}" : "L#{range}"
+ pluralization = range.kind_of?(Array) ? "s" : ""
+
+ step "I should see \"#{fragment}\" as URI fragment" do
+ URI.parse(current_url).fragment.should == fragment
+ end
+
+ step "I should see line#{pluralization} #{fragment[1..-1]} highlighted" do
+ ids = Array(range).map { |n| "LC#{n}" }
+ extra = false
+
+ highlighted = all("#tree-content-holder .highlight .line.hll")
+ highlighted.each do |element|
+ extra ||= ids.delete(element[:id]).nil?
+ end
+
+ extra.should be_false and ids.should be_empty
+ end
+ end
+ end
+ end
+
+ click_line_steps *Array(1..5)
+ check_state_steps *Array(1..5), Array(1..2), Array(1..3), Array(1..4), Array(1..5), Array(3..5)
+
+ step 'I go back in history' do
+ page.evaluate_script("window.history.back()")
+ end
+
+ step 'I go forward in history' do
+ page.evaluate_script("window.history.forward()")
+ end
+
+ step 'I click on "Gemfile.lock" file in repo' do
+ click_link "Gemfile.lock"
+ end
+end \ No newline at end of file
diff --git a/features/steps/project/project_network_graph.rb b/features/steps/project/project_network_graph.rb
index 127adecf7ed..76a62acab82 100644
--- a/features/steps/project/project_network_graph.rb
+++ b/features/steps/project/project_network_graph.rb
@@ -43,13 +43,13 @@ class ProjectNetworkGraph < Spinach::FeatureSteps
sleep 2
end
- Then 'page should have content not cotaining "v2.1.0"' do
+ Then 'page should have content not containing "v2.1.0"' do
within '.network-graph' do
page.should have_content 'cleaning'
end
end
- Then 'page should not have content not cotaining "v2.1.0"' do
+ Then 'page should not have content not containing "v2.1.0"' do
within '.network-graph' do
page.should_not have_content 'cleaning'
end
@@ -70,7 +70,7 @@ class ProjectNetworkGraph < Spinach::FeatureSteps
end
When 'I looking for a commit by SHA of "v2.1.0"' do
- within ".content .search" do
+ within ".network-form" do
fill_in 'extended_sha1', with: '98d6492'
find('button').click
end
@@ -84,7 +84,7 @@ class ProjectNetworkGraph < Spinach::FeatureSteps
end
When 'I look for a commit by ";"' do
- within ".content .search" do
+ within ".network-form" do
fill_in 'extended_sha1', with: ';'
find('button').click
end
diff --git a/features/steps/project/project_services.rb b/features/steps/project/project_services.rb
index 70eafc875d4..54b3f18e084 100644
--- a/features/steps/project/project_services.rb
+++ b/features/steps/project/project_services.rb
@@ -3,73 +3,101 @@ class ProjectServices < Spinach::FeatureSteps
include SharedProject
include SharedPaths
- When 'I visit project "Shop" services page' do
+ step 'I visit project "Shop" services page' do
visit project_services_path(@project)
end
- Then 'I should see list of available services' do
- page.should have_content 'Services'
+ step 'I should see list of available services' do
+ page.should have_content 'Project services'
page.should have_content 'Campfire'
page.should have_content 'Hipchat'
page.should have_content 'GitLab CI'
+ page.should have_content 'Assembla'
end
- And 'I click gitlab-ci service link' do
+ step 'I click gitlab-ci service link' do
click_link 'GitLab CI'
end
- And 'I fill gitlab-ci settings' do
+ step 'I fill gitlab-ci settings' do
check 'Active'
fill_in 'Project url', with: 'http://ci.gitlab.org/projects/3'
fill_in 'Token', with: 'verySecret'
click_button 'Save'
end
- Then 'I should see service settings saved' do
+ step 'I should see service settings saved' do
find_field('Project url').value.should == 'http://ci.gitlab.org/projects/3'
end
- And 'I click hipchat service link' do
+ step 'I click hipchat service link' do
click_link 'Hipchat'
end
- And 'I fill hipchat settings' do
+ step 'I fill hipchat settings' do
check 'Active'
fill_in 'Room', with: 'gitlab'
fill_in 'Token', with: 'verySecret'
click_button 'Save'
end
- Then 'I should see hipchat service settings saved' do
+ step 'I should see hipchat service settings saved' do
find_field('Room').value.should == 'gitlab'
end
- And 'I click pivotaltracker service link' do
+ step 'I click pivotaltracker service link' do
click_link 'PivotalTracker'
end
- And 'I fill pivotaltracker settings' do
+ step 'I fill pivotaltracker settings' do
check 'Active'
fill_in 'Token', with: 'verySecret'
click_button 'Save'
end
- Then 'I should see pivotaltracker service settings saved' do
+ step 'I should see pivotaltracker service settings saved' do
find_field('Token').value.should == 'verySecret'
end
- And 'I click Flowdock service link' do
+ step 'I click Flowdock service link' do
click_link 'Flowdock'
end
- And 'I fill Flowdock settings' do
+ step 'I fill Flowdock settings' do
check 'Active'
fill_in 'Token', with: 'verySecret'
click_button 'Save'
end
- Then 'I should see Flowdock service settings saved' do
+ step 'I should see Flowdock service settings saved' do
find_field('Token').value.should == 'verySecret'
end
+
+ step 'I click Assembla service link' do
+ click_link 'Assembla'
+ end
+
+ step 'I fill Assembla settings' do
+ check 'Active'
+ fill_in 'Token', with: 'verySecret'
+ click_button 'Save'
+ end
+
+ step 'I should see Assembla service settings saved' do
+ find_field('Token').value.should == 'verySecret'
+ end
+
+ step 'I click email on push service link' do
+ click_link 'Emails on push'
+ end
+
+ step 'I fill email on push settings' do
+ fill_in 'Recipients', with: 'qa@company.name'
+ click_button 'Save'
+ end
+
+ step 'I should see email on push service settings saved' do
+ find_field('Recipients').value.should == 'qa@company.name'
+ end
end
diff --git a/features/steps/project/project_wiki.rb b/features/steps/project/project_wiki.rb
index 7aba412d751..6146599cc4a 100644
--- a/features/steps/project/project_wiki.rb
+++ b/features/steps/project/project_wiki.rb
@@ -25,7 +25,7 @@ class ProjectWiki < Spinach::FeatureSteps
page.should have_content "link test"
click_link "link test"
- page.should have_content "Editing page"
+ page.should have_content "Editing"
end
Given 'I have an existing Wiki page' do
diff --git a/features/steps/project/redirects.rb b/features/steps/project/redirects.rb
new file mode 100644
index 00000000000..4ac53075704
--- /dev/null
+++ b/features/steps/project/redirects.rb
@@ -0,0 +1,35 @@
+class Spinach::Features::ProjectRedirects < Spinach::FeatureSteps
+ include SharedAuthentication
+ include SharedPaths
+ include SharedProject
+
+ step 'public project "Community"' do
+ create :project_with_code, name: 'Community', visibility_level: Gitlab::VisibilityLevel::PUBLIC
+ end
+
+ step 'private project "Enterprise"' do
+ create :project, name: 'Enterprise'
+ end
+
+ step 'I visit project "Community" page' do
+ project = Project.find_by_name('Community')
+ visit project_path(project)
+ end
+
+ step 'I should see project "Community" home page' do
+ within '.project-home-title' do
+ page.should have_content 'Community'
+ end
+ end
+
+ step 'I visit project "Enterprise" page' do
+ project = Project.find_by_name('Enterprise')
+ visit project_path(project)
+ end
+
+ step 'I visit project "CommunityDoesNotExist" page' do
+ project = Project.find_by_name('Community')
+ visit project_path(project) + 'DoesNotExist'
+ end
+end
+
diff --git a/features/steps/public/projects_feature.rb b/features/steps/public/projects_feature.rb
index 8abc6ae9f23..47e52f47d07 100644
--- a/features/steps/public/projects_feature.rb
+++ b/features/steps/public/projects_feature.rb
@@ -1,5 +1,7 @@
class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps
+ include SharedAuthentication
include SharedPaths
+ include SharedProject
step 'I should see project "Community"' do
page.should have_content "Community"
@@ -23,11 +25,11 @@ class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps
end
step 'public project "Community"' do
- create :project_with_code, name: 'Community', public: true, default_branch: 'master'
+ create :project_with_code, name: 'Community', visibility_level: Gitlab::VisibilityLevel::PUBLIC
end
step 'public empty project "Empty Public Project"' do
- create :project, name: 'Empty Public Project', public: true
+ create :project, name: 'Empty Public Project', visibility_level: Gitlab::VisibilityLevel::PUBLIC
end
step 'I visit empty project page' do
@@ -44,20 +46,66 @@ class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps
page.should have_content 'Git global setup'
end
+ step 'I should see empty public project details with http clone info' do
+ project = Project.find_by_name('Empty Public Project')
+ page.all(:css, '.git-empty .clone').each do |element|
+ element.text.should include(project.http_url_to_repo)
+ end
+ end
+
+ step 'I should see empty public project details with ssh clone info' do
+ project = Project.find_by_name('Empty Public Project')
+ page.all(:css, '.git-empty .clone').each do |element|
+ element.text.should include(project.url_to_repo)
+ end
+ end
+
step 'private project "Enterprise"' do
create :project, name: 'Enterprise'
end
+ step 'I visit project "Enterprise" page' do
+ project = Project.find_by_name('Enterprise')
+ visit project_path(project)
+ end
+
step 'I should see project "Community" home page' do
within '.project-home-title' do
page.should have_content 'Community'
end
end
- private
+ step 'internal project "Internal"' do
+ create :project_with_code, name: 'Internal', visibility_level: Gitlab::VisibilityLevel::INTERNAL
+ end
+
+ step 'I should see project "Internal"' do
+ page.should have_content "Internal"
+ end
+
+ step 'I should not see project "Internal"' do
+ page.should_not have_content "Internal"
+ end
+
+ step 'I visit project "Internal" page' do
+ project = Project.find_by_name('Internal')
+ visit project_path(project)
+ end
+
+ step 'I should see project "Internal" home page' do
+ within '.project-home-title' do
+ page.should have_content 'Internal'
+ end
+ end
+
+ step 'I should see an http link to the repository' do
+ project = Project.find_by_name 'Community'
+ page.should have_field('project_clone', with: project.http_url_to_repo)
+ end
- def project
- @project ||= Project.find_by_name("Community")
+ step 'I should see an ssh link to the repository' do
+ project = Project.find_by_name 'Community'
+ page.should have_field('project_clone', with: project.url_to_repo)
end
end
diff --git a/features/steps/shared/authentication.rb b/features/steps/shared/authentication.rb
index 8c501bbc537..df05754c287 100644
--- a/features/steps/shared/authentication.rb
+++ b/features/steps/shared/authentication.rb
@@ -12,6 +12,10 @@ module SharedAuthentication
login_as :admin
end
+ step 'I should be redirected to sign in page' do
+ current_path.should == new_user_session_path
+ end
+
def current_user
@user || User.first
end
diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb
index 156fa5bab4e..987cd3120c9 100644
--- a/features/steps/shared/paths.rb
+++ b/features/steps/shared/paths.rb
@@ -105,6 +105,10 @@ module SharedPaths
visit admin_logs_path
end
+ step 'I visit admin messages page' do
+ visit admin_broadcast_messages_path
+ end
+
step 'I visit admin hooks page' do
visit admin_hooks_path
end
diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb
index cef66b038db..3dc4932a09a 100644
--- a/features/steps/shared/project.rb
+++ b/features/steps/shared/project.rb
@@ -14,6 +14,13 @@ module SharedProject
@project.team << [@user, :master]
end
+ # Create another specific project called "Forum"
+ And 'I own project "Forum"' do
+ @project = Project.find_by_name "Forum"
+ @project ||= create(:project_with_code, name: "Forum", namespace: @user.namespace, path: 'forum_project')
+ @project.team << [@user, :master]
+ end
+
And 'project "Shop" has push event' do
@project = Project.find_by_name("Shop")
diff --git a/features/steps/snippets/snippets.rb b/features/steps/snippets/snippets.rb
index bbdf5b97c84..1aea01f6cdf 100644
--- a/features/steps/snippets/snippets.rb
+++ b/features/steps/snippets/snippets.rb
@@ -19,7 +19,7 @@ class SnippetsFeature < Spinach::FeatureSteps
end
And 'I click link "Destroy"' do
- click_link "Destroy"
+ click_link "Remove"
end
And 'I submit new snippet "Personal snippet three"' do
@@ -46,7 +46,7 @@ class SnippetsFeature < Spinach::FeatureSteps
end
And 'I uncheck "Private" checkbox' do
- find(:xpath, "//input[@id='personal_snippet_private']").set true
+ choose "Public"
click_button "Save"
end
diff --git a/features/support/env.rb b/features/support/env.rb
index d27a73edab8..64cdc7f5bf1 100644
--- a/features/support/env.rb
+++ b/features/support/env.rb
@@ -9,6 +9,7 @@ ENV['RAILS_ENV'] = 'test'
require './config/environment'
require 'rspec'
+require 'rspec/expectations'
require 'database_cleaner'
require 'spinach/capybara'
require 'sidekiq/testing/inline'
diff --git a/lib/api/api.rb b/lib/api/api.rb
index 4db81f42b4c..283f7642f67 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -39,5 +39,7 @@ module API
mount DeployKeys
mount ProjectHooks
mount Services
+ mount Files
+ mount Namespaces
end
end
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 429083d75be..7daf8ace242 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -24,6 +24,10 @@ module API
expose :id, :url, :created_at
end
+ class ProjectHook < Hook
+ expose :project_id, :push_events, :issues_events, :merge_requests_events
+ end
+
class ForkedFromProject < Grape::Entity
expose :id
expose :name, :name_with_namespace
@@ -31,11 +35,13 @@ module API
end
class Project < Grape::Entity
- expose :id, :description, :default_branch, :public, :ssh_url_to_repo, :http_url_to_repo, :web_url
+ expose :id, :description, :default_branch
+ expose :public?, as: :public
+ expose :visibility_level, :ssh_url_to_repo, :http_url_to_repo, :web_url
expose :owner, using: Entities::UserBasic
expose :name, :name_with_namespace
expose :path, :path_with_namespace
- expose :issues_enabled, :merge_requests_enabled, :wall_enabled, :wiki_enabled, :snippets_enabled, :created_at, :last_activity_at, :public
+ expose :issues_enabled, :merge_requests_enabled, :wall_enabled, :wiki_enabled, :snippets_enabled, :created_at, :last_activity_at
expose :namespace
expose :forked_from_project, using: Entities::ForkedFromProject, :if => lambda{ | project, options | project.forked? }
end
@@ -136,5 +142,9 @@ module API
expose :target_id, :target_type, :author_id
expose :data, :target_title
end
+
+ class Namespace < Grape::Entity
+ expose :id, :path, :kind
+ end
end
end
diff --git a/lib/api/files.rb b/lib/api/files.rb
new file mode 100644
index 00000000000..6a5419a580f
--- /dev/null
+++ b/lib/api/files.rb
@@ -0,0 +1,99 @@
+module API
+ # Projects API
+ class Files < Grape::API
+ before { authenticate! }
+ before { authorize! :push_code, user_project }
+
+ resource :projects do
+ # Create new file in repository
+ #
+ # Parameters:
+ # file_path (optional) - The path to new file. Ex. lib/class.rb
+ # branch_name (required) - The name of branch
+ # content (required) - File content
+ # commit_message (required) - Commit message
+ #
+ # Example Request:
+ # POST /projects/:id/repository/files
+ #
+ post ":id/repository/files" do
+ required_attributes! [:file_path, :branch_name, :content, :commit_message]
+ attrs = attributes_for_keys [:file_path, :branch_name, :content, :commit_message]
+ branch_name = attrs.delete(:branch_name)
+ file_path = attrs.delete(:file_path)
+ result = ::Files::CreateContext.new(user_project, current_user, attrs, branch_name, file_path).execute
+
+ if result[:status] == :success
+ status(201)
+
+ {
+ file_path: file_path,
+ branch_name: branch_name
+ }
+ else
+ render_api_error!(result[:error], 400)
+ end
+ end
+
+ # Update existing file in repository
+ #
+ # Parameters:
+ # file_path (optional) - The path to file. Ex. lib/class.rb
+ # branch_name (required) - The name of branch
+ # content (required) - File content
+ # commit_message (required) - Commit message
+ #
+ # Example Request:
+ # PUT /projects/:id/repository/files
+ #
+ put ":id/repository/files" do
+ required_attributes! [:file_path, :branch_name, :content, :commit_message]
+ attrs = attributes_for_keys [:file_path, :branch_name, :content, :commit_message]
+ branch_name = attrs.delete(:branch_name)
+ file_path = attrs.delete(:file_path)
+ result = ::Files::UpdateContext.new(user_project, current_user, attrs, branch_name, file_path).execute
+
+ if result[:status] == :success
+ status(200)
+
+ {
+ file_path: file_path,
+ branch_name: branch_name
+ }
+ else
+ render_api_error!(result[:error], 400)
+ end
+ end
+
+ # Delete existing file in repository
+ #
+ # Parameters:
+ # file_path (optional) - The path to file. Ex. lib/class.rb
+ # branch_name (required) - The name of branch
+ # content (required) - File content
+ # commit_message (required) - Commit message
+ #
+ # Example Request:
+ # DELETE /projects/:id/repository/files
+ #
+ delete ":id/repository/files" do
+ required_attributes! [:file_path, :branch_name, :commit_message]
+ attrs = attributes_for_keys [:file_path, :branch_name, :commit_message]
+ branch_name = attrs.delete(:branch_name)
+ file_path = attrs.delete(:file_path)
+ result = ::Files::DeleteContext.new(user_project, current_user, attrs, branch_name, file_path).execute
+
+ if result[:status] == :success
+ status(200)
+
+ {
+ file_path: file_path,
+ branch_name: branch_name
+ }
+ else
+ render_api_error!(result[:error], 400)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index edc662eaaab..b0f8d5a6da9 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -6,19 +6,23 @@ module API
SUDO_PARAM = :sudo
def current_user
- @current_user ||= User.find_by_authentication_token(params[PRIVATE_TOKEN_PARAM] || env[PRIVATE_TOKEN_HEADER])
+ private_token = (params[PRIVATE_TOKEN_PARAM] || env[PRIVATE_TOKEN_HEADER]).to_s
+ @current_user ||= User.find_by_authentication_token(private_token)
identifier = sudo_identifier()
+
# If the sudo is the current user do nothing
if (identifier && !(@current_user.id == identifier || @current_user.username == identifier))
render_api_error!('403 Forbidden: Must be admin to use sudo', 403) unless @current_user.is_admin?
@current_user = User.by_username_or_id(identifier)
not_found!("No user id or username for: #{identifier}") if @current_user.nil?
end
+
@current_user
end
def sudo_identifier()
identifier ||= params[SUDO_PARAM] ||= env[SUDO_HEADER]
+
# Regex for integers
if (!!(identifier =~ /^[0-9]+$/))
identifier.to_i
@@ -29,6 +33,7 @@ module API
def set_current_user_for_thread
Thread.current[:current_user] = current_user
+
begin
yield
ensure
diff --git a/lib/api/namespaces.rb b/lib/api/namespaces.rb
new file mode 100644
index 00000000000..f9f2ed90ccc
--- /dev/null
+++ b/lib/api/namespaces.rb
@@ -0,0 +1,23 @@
+module API
+ # namespaces API
+ class Namespaces < Grape::API
+ before {
+ authenticate!
+ authenticated_as_admin!
+ }
+
+ resource :namespaces do
+ # Get a namespaces list
+ #
+ # Example Request:
+ # GET /namespaces
+ get do
+ @namespaces = Namespace.all
+ @namespaces = @namespaces.search(params[:search]) if params[:search].present?
+ @namespaces = paginate @namespaces
+
+ present @namespaces, with: Entities::Namespace
+ end
+ end
+ end
+end
diff --git a/lib/api/project_hooks.rb b/lib/api/project_hooks.rb
index 738974955f3..c271dd8b61b 100644
--- a/lib/api/project_hooks.rb
+++ b/lib/api/project_hooks.rb
@@ -22,7 +22,7 @@ module API
# GET /projects/:id/hooks
get ":id/hooks" do
@hooks = paginate user_project.hooks
- present @hooks, with: Entities::Hook
+ present @hooks, with: Entities::ProjectHook
end
# Get a project hook
@@ -34,7 +34,7 @@ module API
# GET /projects/:id/hooks/:hook_id
get ":id/hooks/:hook_id" do
@hook = user_project.hooks.find(params[:hook_id])
- present @hook, with: Entities::Hook
+ present @hook, with: Entities::ProjectHook
end
@@ -47,10 +47,11 @@ module API
# POST /projects/:id/hooks
post ":id/hooks" do
required_attributes! [:url]
+ attrs = attributes_for_keys [:url, :push_events, :issues_events, :merge_requests_events]
+ @hook = user_project.hooks.new(attrs)
- @hook = user_project.hooks.new({"url" => params[:url]})
if @hook.save
- present @hook, with: Entities::Hook
+ present @hook, with: Entities::ProjectHook
else
if @hook.errors[:url].present?
error!("Invalid url given", 422)
@@ -70,10 +71,10 @@ module API
put ":id/hooks/:hook_id" do
@hook = user_project.hooks.find(params[:hook_id])
required_attributes! [:url]
+ attrs = attributes_for_keys [:url, :push_events, :issues_events, :merge_requests_events]
- attrs = attributes_for_keys [:url]
if @hook.update_attributes attrs
- present @hook, with: Entities::Hook
+ present @hook, with: Entities::ProjectHook
else
if @hook.errors[:url].present?
error!("Invalid url given", 422)
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 221f1f1e23c..003533fb59a 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -11,6 +11,13 @@ module API
end
not_found!
end
+
+ def map_public_to_visibility_level(attrs)
+ publik = attrs.delete(:public)
+ publik = [ true, 1, '1', 't', 'T', 'true', 'TRUE', 'on', 'ON' ].include?(publik)
+ attrs[:visibility_level] = Gitlab::VisibilityLevel::PUBLIC if !attrs[:visibility_level].present? && publik == true
+ attrs
+ end
end
# Get a projects list for authenticated user
@@ -31,6 +38,16 @@ module API
present @projects, with: Entities::Project
end
+ # Get all projects for admin user
+ #
+ # Example Request:
+ # GET /projects/all
+ get '/all' do
+ authenticated_as_admin!
+ @projects = paginate Project
+ present @projects, with: Entities::Project
+ end
+
# Get a single project
#
# Parameters:
@@ -60,14 +77,14 @@ module API
# Parameters:
# name (required) - name for new project
# description (optional) - short project description
- # default_branch (optional) - 'master' by default
# issues_enabled (optional)
# wall_enabled (optional)
# merge_requests_enabled (optional)
# wiki_enabled (optional)
# snippets_enabled (optional)
# namespace_id (optional) - defaults to user namespace
- # public (optional) - false by default
+ # public (optional) - if true same as setting visibility_level = 20
+ # visibility_level (optional) - 0 by default
# Example Request
# POST /projects
post do
@@ -75,14 +92,15 @@ module API
attrs = attributes_for_keys [:name,
:path,
:description,
- :default_branch,
:issues_enabled,
:wall_enabled,
:merge_requests_enabled,
:wiki_enabled,
:snippets_enabled,
:namespace_id,
- :public]
+ :public,
+ :visibility_level]
+ attrs = map_public_to_visibility_level(attrs)
@project = ::Projects::CreateContext.new(current_user, attrs).execute
if @project.saved?
present @project, with: Entities::Project
@@ -106,7 +124,8 @@ module API
# merge_requests_enabled (optional)
# wiki_enabled (optional)
# snippets_enabled (optional)
- # public (optional)
+ # public (optional) - if true same as setting visibility_level = 20
+ # visibility_level (optional)
# Example Request
# POST /projects/user/:user_id
post "user/:user_id" do
@@ -120,7 +139,9 @@ module API
:merge_requests_enabled,
:wiki_enabled,
:snippets_enabled,
- :public]
+ :public,
+ :visibility_level]
+ attrs = map_public_to_visibility_level(attrs)
@project = ::Projects::CreateContext.new(user, attrs).execute
if @project.saved?
present @project, with: Entities::Project
@@ -282,7 +303,8 @@ module API
# GET /projects/search/:query
get "/search/:query" do
ids = current_user.authorized_projects.map(&:id)
- projects = Project.where("(id in (?) OR public = true) AND (name LIKE (?))", ids, "%#{params[:query]}%")
+ visibility_levels = [ Gitlab::VisibilityLevel::INTERNAL, Gitlab::VisibilityLevel::PUBLIC ]
+ projects = Project.where("(id in (?) OR visibility_level in (?)) AND (name LIKE (?))", ids, visibility_levels, "%#{params[:query]}%")
present paginate(projects), with: Entities::Project
end
end
diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb
index c9422fdb165..0a32135ff10 100644
--- a/lib/api/repositories.rb
+++ b/lib/api/repositories.rb
@@ -1,3 +1,5 @@
+require 'mime/types'
+
module API
# Projects API
class Repositories < Grape::API
@@ -177,6 +179,28 @@ module API
present blob.data
end
+ # Get a raw blob contents by blob sha
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # sha (required) - The blob's sha
+ # Example Request:
+ # GET /projects/:id/repository/raw_blobs/:sha
+ get ":id/repository/raw_blobs/:sha" do
+ ref = params[:sha]
+
+ repo = user_project.repository
+
+ blob = Gitlab::Git::Blob.raw(repo, ref)
+
+ not_found! "Blob" unless blob
+
+ env['api.format'] = :txt
+
+ content_type blob.mime_type
+ present blob.data
+ end
+
# Get a an archive of the repository
#
# Parameters:
@@ -184,18 +208,20 @@ module API
# sha (optional) - the commit sha to download defaults to the tip of the default branch
# Example Request:
# GET /projects/:id/repository/archive
- get ":id/repository/archive" do
+ get ":id/repository/archive", requirements: { format: Gitlab::Regex.archive_formats_regex } do
authorize! :download_code, user_project
repo = user_project.repository
ref = params[:sha]
+ format = params[:format]
storage_path = Rails.root.join("tmp", "repositories")
- file_path = repo.archive_repo(ref, storage_path)
+ file_path = repo.archive_repo(ref, storage_path, format)
if file_path && File.exists?(file_path)
data = File.open(file_path, 'rb').read
- header "Content-Disposition:", " infile; filename=\"#{File.basename(file_path)}\""
- content_type 'application/x-gzip'
+ header["Content-Disposition"] = "attachment; filename=\"#{File.basename(file_path)}\""
+
+ content_type MIME::Types.type_for(file_path).first.content_type
env['api.format'] = :binary
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 54d3aeecb70..475343a3edf 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -9,7 +9,7 @@ module API
# Example Request:
# GET /users
get do
- @users = User.scoped
+ @users = User.all
@users = @users.active if params[:active].present?
@users = @users.search(params[:search]) if params[:search].present?
@users = paginate @users
diff --git a/lib/backup/database.rb b/lib/backup/database.rb
index c4fb2e2e159..7af7140246a 100644
--- a/lib/backup/database.rb
+++ b/lib/backup/database.rb
@@ -13,20 +13,20 @@ module Backup
def dump
case config["adapter"]
when /^mysql/ then
- system("mysqldump #{mysql_args} #{config['database']} > #{db_file_name}")
+ system('mysqldump', *mysql_args, config['database'], out: db_file_name)
when "postgresql" then
pg_env
- system("pg_dump #{config['database']} > #{db_file_name}")
+ system('pg_dump', config['database'], out: db_file_name)
end
end
def restore
case config["adapter"]
when /^mysql/ then
- system("mysql #{mysql_args} #{config['database']} < #{db_file_name}")
+ system('mysql', *mysql_args, config['database'], in: db_file_name)
when "postgresql" then
pg_env
- system("psql #{config['database']} -f #{db_file_name}")
+ system('psql', config['database'], '-f', db_file_name)
end
end
@@ -45,7 +45,7 @@ module Backup
'encoding' => '--default-character-set',
'password' => '--password'
}
- args.map { |opt, arg| "#{arg}='#{config[opt]}'" if config[opt] }.compact.join(' ')
+ args.map { |opt, arg| "#{arg}=#{config[opt]}" if config[opt] }.compact
end
def pg_env
diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb
index 258a0fb2589..efaefa4ce44 100644
--- a/lib/backup/manager.rb
+++ b/lib/backup/manager.rb
@@ -1,5 +1,7 @@
module Backup
class Manager
+ BACKUP_CONTENTS = %w{repositories/ db/ uploads/ backup_information.yml}
+
def pack
# saving additional informations
s = {}
@@ -16,7 +18,7 @@ module Backup
# create archive
print "Creating backup archive: #{s[:backup_created_at].to_i}_gitlab_backup.tar ... "
- if Kernel.system("tar -cf #{s[:backup_created_at].to_i}_gitlab_backup.tar repositories/ db/ uploads/ backup_information.yml")
+ if Kernel.system('tar', '-cf', "#{s[:backup_created_at].to_i}_gitlab_backup.tar", *BACKUP_CONTENTS)
puts "done".green
else
puts "failed".red
@@ -25,7 +27,7 @@ module Backup
def cleanup
print "Deleting tmp directories ... "
- if Kernel.system("rm -rf repositories/ db/ uploads/ backup_information.yml")
+ if Kernel.system('rm', '-rf', *BACKUP_CONTENTS)
puts "done".green
else
puts "failed".red
@@ -44,7 +46,7 @@ module Backup
file_list.map! { |f| $1.to_i if f =~ /(\d+)_gitlab_backup.tar/ }
file_list.sort.each do |timestamp|
if Time.at(timestamp) < (Time.now - keep_time)
- if system("rm #{timestamp}_gitlab_backup.tar")
+ if Kernel.system(*%W(rm #{timestamp}_gitlab_backup.tar))
removed += 1
end
end
@@ -75,7 +77,7 @@ module Backup
end
print "Unpacking backup ... "
- unless Kernel.system("tar -xf #{tar_file}")
+ unless Kernel.system(*%W(tar -xf #{tar_file}))
puts "failed".red
exit 1
else
diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb
index 252201f11be..20fd5ba92a1 100644
--- a/lib/backup/repository.rb
+++ b/lib/backup/repository.rb
@@ -18,7 +18,7 @@ module Backup
# Create namespace dir if missing
FileUtils.mkdir_p(File.join(backup_repos_path, project.namespace.path)) if project.namespace
- if system("cd #{path_to_repo(project)} > /dev/null 2>&1 && git bundle create #{path_to_bundle(project)} --all > /dev/null 2>&1")
+ if system(*%W(git --git-dir=#{path_to_repo(project)} bundle create #{path_to_bundle(project)} --all), silent)
puts "[DONE]".green
else
puts "[FAILED]".red
@@ -30,7 +30,7 @@ module Backup
print " * #{wiki.path_with_namespace} ... "
if wiki.empty?
puts " [SKIPPED]".cyan
- elsif system("cd #{path_to_repo(wiki)} > /dev/null 2>&1 && git bundle create #{path_to_bundle(wiki)} --all > /dev/null 2>&1")
+ elsif system(*%W(git --git-dir=#{path_to_repo(wiki)} bundle create #{path_to_bundle(wiki)} --all), silent)
puts " [DONE]".green
else
puts " [FAILED]".red
@@ -53,7 +53,7 @@ module Backup
project.namespace.ensure_dir_exist if project.namespace
- if system("git clone --bare #{path_to_bundle(project)} #{path_to_repo(project)} > /dev/null 2>&1")
+ if system(*%W(git clone --bare #{path_to_bundle(project)} #{path_to_repo(project)}), silent)
puts "[DONE]".green
else
puts "[FAILED]".red
@@ -63,7 +63,7 @@ module Backup
if File.exists?(path_to_bundle(wiki))
print " * #{wiki.path_with_namespace} ... "
- if system("git clone --bare #{path_to_bundle(wiki)} #{path_to_repo(wiki)} > /dev/null 2>&1")
+ if system(*%W(git clone --bare #{path_to_bundle(wiki)} #{path_to_repo(wiki)}), silent)
puts " [DONE]".green
else
puts " [FAILED]".red
@@ -73,7 +73,7 @@ module Backup
print 'Put GitLab hooks in repositories dirs'.yellow
gitlab_shell_user_home = File.expand_path("~#{Gitlab.config.gitlab_shell.ssh_user}")
- if system("#{gitlab_shell_user_home}/gitlab-shell/support/rewrite-hooks.sh #{Gitlab.config.gitlab_shell.repos_path}")
+ if system("#{gitlab_shell_user_home}/gitlab-shell/support/rewrite-hooks.sh", Gitlab.config.gitlab_shell.repos_path)
puts " [DONE]".green
else
puts " [FAILED]".red
@@ -103,5 +103,9 @@ module Backup
FileUtils.rm_rf(backup_repos_path)
FileUtils.mkdir_p(backup_repos_path)
end
+
+ def silent
+ {err: '/dev/null', out: '/dev/null'}
+ end
end
end
diff --git a/lib/backup/uploads.rb b/lib/backup/uploads.rb
index 462d3f1e274..e79da7e8fd2 100644
--- a/lib/backup/uploads.rb
+++ b/lib/backup/uploads.rb
@@ -19,7 +19,7 @@ module Backup
FileUtils.cp_r(backup_uploads_dir, app_uploads_dir)
end
-
+
def backup_existing_uploads_dir
if File.exists?(app_uploads_dir)
FileUtils.mv(app_uploads_dir, Rails.root.join('public', "uploads.#{Time.now.to_i}"))
diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb
index e09cf311972..c629144118c 100644
--- a/lib/gitlab/backend/grack_auth.rb
+++ b/lib/gitlab/backend/grack_auth.rb
@@ -58,7 +58,7 @@ module Grack
end
else
- return unauthorized unless project.public
+ return unauthorized unless project.public?
end
if authorized_git_request?
@@ -80,15 +80,19 @@ module Grack
def authorize_request(service)
case service
when 'git-upload-pack'
- project.public || can?(user, :download_code, project)
+ can?(user, :download_code, project)
when'git-receive-pack'
- action = if project.protected_branch?(ref)
- :push_code_to_protected_branches
- else
- :push_code
- end
+ refs.each do |ref|
+ action = if project.protected_branch?(ref)
+ :push_code_to_protected_branches
+ else
+ :push_code
+ end
+
+ return false unless can?(user, action, project)
+ end
- can?(user, action, project)
+ true
else
false
end
@@ -108,11 +112,11 @@ module Grack
@project ||= project_by_path(@request.path_info)
end
- def ref
- @ref ||= parse_ref
+ def refs
+ @refs ||= parse_refs
end
- def parse_ref
+ def parse_refs
input = if @env["HTTP_CONTENT_ENCODING"] =~ /gzip/
Zlib::GzipReader.new(@request.body).read
else
@@ -121,7 +125,15 @@ module Grack
# Need to reset seek point
@request.body.rewind
- /refs\/heads\/([\/\w\.-]+)/n.match(input.force_encoding('ascii-8bit')).to_a.last
+
+ # Parse refs
+ refs = input.force_encoding('ascii-8bit').scan(/refs\/heads\/([\/\w\.-]+)/n).flatten.compact
+
+ # Cleanup grabare from refs
+ # if push to multiple branches
+ refs.map do |ref|
+ ref.gsub(/00.*/, "")
+ end
end
end
end
diff --git a/lib/gitlab/backend/grack_helpers.rb b/lib/gitlab/backend/grack_helpers.rb
index 5ac9e9f325b..cb747fe0137 100644
--- a/lib/gitlab/backend/grack_helpers.rb
+++ b/lib/gitlab/backend/grack_helpers.rb
@@ -1,7 +1,7 @@
module Grack
module Helpers
def project_by_path(path)
- if m = /^\/([\w\.\/-]+)\.git/.match(path).to_a
+ if m = /^([\w\.\/-]+)\.git/.match(path).to_a
path_with_namespace = m.last
path_with_namespace.gsub!(/\.wiki$/, '')
diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb
index c819ce56ac9..7121c8e40d2 100644
--- a/lib/gitlab/backend/shell.rb
+++ b/lib/gitlab/backend/shell.rb
@@ -10,7 +10,7 @@ module Gitlab
# add_repository("gitlab/gitlab-ci")
#
def add_repository(name)
- system "#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-projects", "add-project", "#{name}.git"
+ system "#{gitlab_shell_path}/bin/gitlab-projects", "add-project", "#{name}.git"
end
# Import repository
@@ -21,7 +21,7 @@ module Gitlab
# import_repository("gitlab/gitlab-ci", "https://github.com/randx/six.git")
#
def import_repository(name, url)
- system "#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-projects", "import-project", "#{name}.git", url
+ system "#{gitlab_shell_path}/bin/gitlab-projects", "import-project", "#{name}.git", url
end
# Move repository
@@ -33,7 +33,7 @@ module Gitlab
# mv_repository("gitlab/gitlab-ci", "randx/gitlab-ci-new.git")
#
def mv_repository(path, new_path)
- system "#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-projects", "mv-project", "#{path}.git", "#{new_path}.git"
+ system "#{gitlab_shell_path}/bin/gitlab-projects", "mv-project", "#{path}.git", "#{new_path}.git"
end
# Update HEAD for repository
@@ -45,7 +45,7 @@ module Gitlab
# update_repository_head("gitlab/gitlab-ci", "3-1-stable")
#
def update_repository_head(path, branch)
- system "#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-projects", "update-head", "#{path}.git", branch
+ system "#{gitlab_shell_path}/bin/gitlab-projects", "update-head", "#{path}.git", branch
end
# Fork repository to new namespace
@@ -57,7 +57,7 @@ module Gitlab
# fork_repository("gitlab/gitlab-ci", "randx")
#
def fork_repository(path, fork_namespace)
- system "#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-projects", "fork-project", "#{path}.git", fork_namespace
+ system "#{gitlab_shell_path}/bin/gitlab-projects", "fork-project", "#{path}.git", fork_namespace
end
# Remove repository from file system
@@ -68,7 +68,7 @@ module Gitlab
# remove_repository("gitlab/gitlab-ci")
#
def remove_repository(name)
- system "#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-projects", "rm-project", "#{name}.git"
+ system "#{gitlab_shell_path}/bin/gitlab-projects", "rm-project", "#{name}.git"
end
# Add repository branch from passed ref
@@ -81,7 +81,7 @@ module Gitlab
# add_branch("gitlab/gitlab-ci", "4-0-stable", "master")
#
def add_branch(path, branch_name, ref)
- system "#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-projects", "create-branch", "#{path}.git", branch_name, ref
+ system "#{gitlab_shell_path}/bin/gitlab-projects", "create-branch", "#{path}.git", branch_name, ref
end
# Remove repository branch
@@ -93,7 +93,7 @@ module Gitlab
# rm_branch("gitlab/gitlab-ci", "4-0-stable")
#
def rm_branch(path, branch_name)
- system "#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-projects", "rm-branch", "#{path}.git", branch_name
+ system "#{gitlab_shell_path}/bin/gitlab-projects", "rm-branch", "#{path}.git", branch_name
end
# Add repository tag from passed ref
@@ -106,7 +106,7 @@ module Gitlab
# add_tag("gitlab/gitlab-ci", "v4.0", "master")
#
def add_tag(path, tag_name, ref)
- system "#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-projects", "create-tag", "#{path}.git", tag_name, ref
+ system "#{gitlab_shell_path}/bin/gitlab-projects", "create-tag", "#{path}.git", tag_name, ref
end
# Remove repository tag
@@ -118,7 +118,7 @@ module Gitlab
# rm_tag("gitlab/gitlab-ci", "v4.0")
#
def rm_tag(path, tag_name)
- system "#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-projects", "rm-tag", "#{path}.git", tag_name
+ system "#{gitlab_shell_path}/bin/gitlab-projects", "rm-tag", "#{path}.git", tag_name
end
# Add new key to gitlab-shell
@@ -127,7 +127,7 @@ module Gitlab
# add_key("key-42", "sha-rsa ...")
#
def add_key(key_id, key_content)
- system "#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-keys", "add-key", key_id, key_content
+ system "#{gitlab_shell_path}/bin/gitlab-keys", "add-key", key_id, key_content
end
# Remove ssh key from gitlab shell
@@ -136,7 +136,7 @@ module Gitlab
# remove_key("key-342", "sha-rsa ...")
#
def remove_key(key_id, key_content)
- system "#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-keys", "rm-key", key_id, key_content
+ system "#{gitlab_shell_path}/bin/gitlab-keys", "rm-key", key_id, key_content
end
# Remove all ssh keys from gitlab shell
@@ -145,7 +145,7 @@ module Gitlab
# remove_all_keys
#
def remove_all_keys
- system "#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-keys", "clear"
+ system "#{gitlab_shell_path}/bin/gitlab-keys", "clear"
end
# Add empty directory for storing repositories
@@ -196,8 +196,21 @@ module Gitlab
Gitlab.config.gitlab_shell.ssh_path_prefix + "#{path}.git"
end
+ # Return GitLab shell version
+ def version
+ gitlab_shell_version_file = "#{gitlab_shell_path}/VERSION"
+
+ if File.readable?(gitlab_shell_version_file)
+ File.read(gitlab_shell_version_file)
+ end
+ end
+
protected
+ def gitlab_shell_path
+ Gitlab.config.gitlab_shell.path
+ end
+
def gitlab_shell_user_home
File.expand_path("~#{Gitlab.config.gitlab_shell.ssh_user}")
end
diff --git a/lib/gitlab/ldap/user.rb b/lib/gitlab/ldap/user.rb
index 78fc5dab9cb..59f0fa64a6a 100644
--- a/lib/gitlab/ldap/user.rb
+++ b/lib/gitlab/ldap/user.rb
@@ -23,8 +23,8 @@ module Gitlab
# Look for user with same emails
#
# Possible cases:
- # * When user already has account and need to link his LDAP account.
- # * LDAP uid changed for user with same email and we need to update his uid
+ # * When user already has account and need to link their LDAP account.
+ # * LDAP uid changed for user with same email and we need to update their uid
#
user = find_user(email)
@@ -47,7 +47,7 @@ module Gitlab
user = model.find_by_email(email)
# If no user found and allow_username_or_email_login is true
- # we look for user by extracting part of his email
+ # we look for user by extracting part of their email
if !user && email && ldap_conf['allow_username_or_email_login']
uname = email.partition('@').first
user = model.find_by_username(uname)
@@ -78,7 +78,7 @@ module Gitlab
# * when ldap account was deactivated by change of OU membership in 'dn'
def blocked?(dn)
ldap = OmniAuth::LDAP::Adaptor.new(ldap_conf)
- ldap.connection.search(base: dn, size: 1).blank?
+ ldap.connection.search(base: dn, scope: Net::LDAP::SearchScope_BaseObject, size: 1).blank?
end
private
diff --git a/lib/gitlab/oauth/user.rb b/lib/gitlab/oauth/user.rb
index ea9badba2c3..529753c4019 100644
--- a/lib/gitlab/oauth/user.rb
+++ b/lib/gitlab/oauth/user.rb
@@ -28,8 +28,8 @@ module Gitlab
}
user = model.build_user(opts, as: :admin)
+ user.skip_confirmation!
user.save!
- user.confirm!
log.info "(OAuth) Creating user #{email} from login with extern_uid => #{uid}"
if Gitlab.config.omniauth['block_auto_created_users'] && !ldap?
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index 55aa240a9f9..943dc9dc7ea 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -17,6 +17,11 @@ module Gitlab
def path_regex
default_regex
end
+
+ def archive_formats_regex
+ #|zip|tar| tar.gz | tar.bz2 |
+ /(zip|tar|tar\.gz|tgz|gz|tar\.bz2|tbz|tbz2|tb2|bz2)/
+ end
def git_reference_regex
# Valid git ref regex, see:
@@ -24,27 +29,27 @@ module Gitlab
%r{
(?!
- # doesn't begins with
- \/| # (rule #6)
- # doesn't contain
+ (?# doesn't begins with)
+ \/| (?# rule #6)
+ (?# doesn't contain)
.*(?:
- [\/.]\.| # (rule #1,3)
- \/\/| # (rule #6)
- @\{| # (rule #8)
- \\ # (rule #9)
+ [\/.]\.| (?# rule #1,3)
+ \/\/| (?# rule #6)
+ @\{| (?# rule #8)
+ \\ (?# rule #9)
)
)
- [^\000-\040\177~^:?*\[]+ # (rule #4-5)
- # doesn't end with
- (?<!\.lock) # (rule #1)
- (?<![\/.]) # (rule #6-7)
+ [^\000-\040\177~^:?*\[]+ (?# rule #4-5)
+ (?# doesn't end with)
+ (?<!\.lock) (?# rule #1)
+ (?<![\/.]) (?# rule #6-7)
}x
end
protected
def default_regex
- /\A[a-zA-Z0-9][a-zA-Z0-9_\-\.]*\z/
+ /\A[a-zA-Z0-9][a-zA-Z0-9_\-\.]*(?<!\.git)\z/
end
end
end
diff --git a/lib/gitlab/satellite/files/delete_file_action.rb b/lib/gitlab/satellite/files/delete_file_action.rb
new file mode 100644
index 00000000000..30462999aa3
--- /dev/null
+++ b/lib/gitlab/satellite/files/delete_file_action.rb
@@ -0,0 +1,50 @@
+require_relative 'file_action'
+
+module Gitlab
+ module Satellite
+ class DeleteFileAction < FileAction
+ # Deletes file and creates a new commit for it
+ #
+ # Returns false if committing the change fails
+ # Returns false if pushing from the satellite to bare repo failed or was rejected
+ # Returns true otherwise
+ def commit!(content, commit_message)
+ in_locked_and_timed_satellite do |repo|
+ prepare_satellite!(repo)
+
+ # create target branch in satellite at the corresponding commit from bare repo
+ repo.git.checkout({raise: true, timeout: true, b: true}, ref, "origin/#{ref}")
+
+ # update the file in the satellite's working dir
+ file_path_in_satellite = File.join(repo.working_dir, file_path)
+
+ # Prevent relative links
+ unless safe_path?(file_path_in_satellite)
+ Gitlab::GitLogger.error("FileAction: Relative path not allowed")
+ return false
+ end
+
+ File.delete(file_path_in_satellite)
+
+ # add removed file
+ repo.remove(file_path_in_satellite)
+
+ # commit the changes
+ # will raise CommandFailed when commit fails
+ repo.git.commit(raise: true, timeout: true, a: true, m: commit_message)
+
+
+ # push commit back to bare repo
+ # will raise CommandFailed when push fails
+ repo.git.push({raise: true, timeout: true}, :origin, ref)
+
+ # everything worked
+ true
+ end
+ rescue Grit::Git::CommandFailed => ex
+ Gitlab::GitLogger.error(ex.message)
+ false
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/satellite/edit_file_action.rb b/lib/gitlab/satellite/files/edit_file_action.rb
index d793d0ba8dc..f410ecb7984 100644
--- a/lib/gitlab/satellite/edit_file_action.rb
+++ b/lib/gitlab/satellite/files/edit_file_action.rb
@@ -1,32 +1,31 @@
+require_relative 'file_action'
+
module Gitlab
module Satellite
# GitLab server-side file update and commit
- class EditFileAction < Action
- attr_accessor :file_path, :ref
-
- def initialize(user, project, ref, file_path)
- super user, project, git_timeout: 10.seconds
- @file_path = file_path
- @ref = ref
- end
-
+ class EditFileAction < FileAction
# Updates the files content and creates a new commit for it
#
# Returns false if the ref has been updated while editing the file
# Returns false if committing the change fails
- # Returns false if pushing from the satellite to Gitolite failed or was rejected
+ # Returns false if pushing from the satellite to bare repo failed or was rejected
# Returns true otherwise
- def commit!(content, commit_message, last_commit)
- return false unless can_edit?(last_commit)
-
+ def commit!(content, commit_message)
in_locked_and_timed_satellite do |repo|
prepare_satellite!(repo)
- # create target branch in satellite at the corresponding commit from Gitolite
+ # create target branch in satellite at the corresponding commit from bare repo
repo.git.checkout({raise: true, timeout: true, b: true}, ref, "origin/#{ref}")
# update the file in the satellite's working dir
file_path_in_satellite = File.join(repo.working_dir, file_path)
+
+ # Prevent relative links
+ unless safe_path?(file_path_in_satellite)
+ Gitlab::GitLogger.error("FileAction: Relative path not allowed")
+ return false
+ end
+
File.open(file_path_in_satellite, 'w') { |f| f.write(content) }
# commit the changes
@@ -34,7 +33,7 @@ module Gitlab
repo.git.commit(raise: true, timeout: true, a: true, m: commit_message)
- # push commit back to Gitolite
+ # push commit back to bare repo
# will raise CommandFailed when push fails
repo.git.push({raise: true, timeout: true}, :origin, ref)
@@ -45,13 +44,6 @@ module Gitlab
Gitlab::GitLogger.error(ex.message)
false
end
-
- protected
-
- def can_edit?(last_commit)
- current_last_commit = Gitlab::Git::Commit.last_for_path(@project.repository, ref, file_path).sha
- last_commit == current_last_commit
- end
end
end
end
diff --git a/lib/gitlab/satellite/files/file_action.rb b/lib/gitlab/satellite/files/file_action.rb
new file mode 100644
index 00000000000..0f7afde647d
--- /dev/null
+++ b/lib/gitlab/satellite/files/file_action.rb
@@ -0,0 +1,17 @@
+module Gitlab
+ module Satellite
+ class FileAction < Action
+ attr_accessor :file_path, :ref
+
+ def initialize(user, project, ref, file_path)
+ super user, project, git_timeout: 10.seconds
+ @file_path = file_path
+ @ref = ref
+ end
+
+ def safe_path?(path)
+ File.absolute_path(path) == path
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/satellite/files/new_file_action.rb b/lib/gitlab/satellite/files/new_file_action.rb
new file mode 100644
index 00000000000..57d101ff535
--- /dev/null
+++ b/lib/gitlab/satellite/files/new_file_action.rb
@@ -0,0 +1,55 @@
+require_relative 'file_action'
+
+module Gitlab
+ module Satellite
+ class NewFileAction < FileAction
+ # Updates the files content and creates a new commit for it
+ #
+ # Returns false if the ref has been updated while editing the file
+ # Returns false if committing the change fails
+ # Returns false if pushing from the satellite to bare repo failed or was rejected
+ # Returns true otherwise
+ def commit!(content, commit_message)
+ in_locked_and_timed_satellite do |repo|
+ prepare_satellite!(repo)
+
+ # create target branch in satellite at the corresponding commit from bare repo
+ repo.git.checkout({raise: true, timeout: true, b: true}, ref, "origin/#{ref}")
+
+ file_path_in_satellite = File.join(repo.working_dir, file_path)
+ dir_name_in_satellite = File.dirname(file_path_in_satellite)
+
+ # Prevent relative links
+ unless safe_path?(file_path_in_satellite)
+ Gitlab::GitLogger.error("FileAction: Relative path not allowed")
+ return false
+ end
+
+ # Create dir if not exists
+ FileUtils.mkdir_p(dir_name_in_satellite)
+
+ # Write file
+ File.open(file_path_in_satellite, 'w') { |f| f.write(content) }
+
+ # add new file
+ repo.add(file_path_in_satellite)
+
+ # commit the changes
+ # will raise CommandFailed when commit fails
+ repo.git.commit(raise: true, timeout: true, a: true, m: commit_message)
+
+
+ # push commit back to bare repo
+ # will raise CommandFailed when push fails
+ repo.git.push({raise: true, timeout: true}, :origin, ref)
+
+ # everything worked
+ true
+ end
+ rescue Grit::Git::CommandFailed => ex
+ Gitlab::GitLogger.error(ex.message)
+ false
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/satellite/merge_action.rb b/lib/gitlab/satellite/merge_action.rb
index d74d4194ff6..54afd6ab95c 100644
--- a/lib/gitlab/satellite/merge_action.rb
+++ b/lib/gitlab/satellite/merge_action.rb
@@ -28,7 +28,7 @@ module Gitlab
in_locked_and_timed_satellite do |merge_repo|
prepare_satellite!(merge_repo)
if merge_in_satellite!(merge_repo)
- # push merge back to Gitolite
+ # push merge back to bare repo
# will raise CommandFailed when push fails
merge_repo.git.push(default_options, :origin, merge_request.target_branch)
# remove source branch
diff --git a/lib/gitlab/satellite/satellite.rb b/lib/gitlab/satellite/satellite.rb
index 6cb7814fae5..353c3024aad 100644
--- a/lib/gitlab/satellite/satellite.rb
+++ b/lib/gitlab/satellite/satellite.rb
@@ -123,7 +123,7 @@ module Gitlab
remotes.each { |name| repo.git.remote(default_options,'rm', name)}
end
- # Updates the satellite from Gitolite
+ # Updates the satellite from bare repo
#
# Note: this will only update remote branches (i.e. origin/*)
def update_from_source!
diff --git a/lib/gitlab/upgrader.rb b/lib/gitlab/upgrader.rb
new file mode 100644
index 00000000000..859923cb563
--- /dev/null
+++ b/lib/gitlab/upgrader.rb
@@ -0,0 +1,96 @@
+require_relative "version_info"
+
+module Gitlab
+ class Upgrader
+ def execute
+ puts "GitLab #{current_version.major} upgrade tool"
+ puts "Your version is #{current_version}"
+ puts "Latest available version for GitLab #{current_version.major} is #{latest_version}"
+
+ if latest_version?
+ puts "You use latest GitLab version"
+ else
+ puts "Newer GitLab version is available"
+ answer = if ARGV.first == "-y"
+ "yes"
+ else
+ prompt("Do you want to upgrade (yes/no)? ", %w{yes no})
+ end
+
+ if answer == "yes"
+ upgrade
+ else
+ exit 0
+ end
+ end
+ end
+
+ def latest_version?
+ current_version >= latest_version
+ end
+
+ def current_version
+ @current_version ||= Gitlab::VersionInfo.parse(current_version_raw)
+ end
+
+ def latest_version
+ @latest_version ||= Gitlab::VersionInfo.parse(latest_version_raw)
+ end
+
+ def current_version_raw
+ File.read(File.join(gitlab_path, "VERSION")).strip
+ end
+
+ def latest_version_raw
+ git_tags = `git ls-remote --tags origin | grep tags\/v#{current_version.major}`
+ git_tags = git_tags.lines.to_a.select { |version| version =~ /v\d\.\d\.\d\Z/ }
+ last_tag = git_tags.last.match(/v\d\.\d\.\d/).to_s
+ end
+
+ def update_commands
+ {
+ "Stash changed files" => "git stash",
+ "Get latest code" => "git fetch",
+ "Switch to new version" => "git checkout v#{latest_version}",
+ "Install gems" => "bundle",
+ "Migrate DB" => "bundle exec rake db:migrate RAILS_ENV=production",
+ "Recompile assets" => "bundle exec rake assets:clean assets:precompile RAILS_ENV=production",
+ "Clear cache" => "bundle exec rake cache:clear RAILS_ENV=production"
+ }
+ end
+
+ def upgrade
+ update_commands.each do |title, cmd|
+ puts title
+ puts " -> #{cmd}"
+ if system(cmd)
+ puts " -> OK"
+ else
+ puts " -> FAILED"
+ puts "Failed to upgrade. Try to repeat task or proceed with upgrade manually "
+ exit 1
+ end
+ end
+
+ puts "Done"
+ end
+
+ def gitlab_path
+ File.expand_path(File.join(File.dirname(__FILE__), '../..'))
+ end
+
+ # Prompt the user to input something
+ #
+ # message - the message to display before input
+ # choices - array of strings of acceptable answers or nil for any answer
+ #
+ # Returns the user's answer
+ def prompt(message, choices = nil)
+ begin
+ print(message)
+ answer = STDIN.gets.chomp
+ end while !choices.include?(answer)
+ answer
+ end
+ end
+end
diff --git a/lib/gitlab/visibility_level.rb b/lib/gitlab/visibility_level.rb
new file mode 100644
index 00000000000..eada9bcddf5
--- /dev/null
+++ b/lib/gitlab/visibility_level.rb
@@ -0,0 +1,42 @@
+# Gitlab::VisibilityLevel module
+#
+# Define allowed public modes that can be used for
+# GitLab projects to determine project public mode
+#
+module Gitlab
+ module VisibilityLevel
+ PRIVATE = 0
+ INTERNAL = 10
+ PUBLIC = 20
+
+ class << self
+ def values
+ options.values
+ end
+
+ def options
+ {
+ 'Private' => PRIVATE,
+ 'Internal' => INTERNAL,
+ 'Public' => PUBLIC
+ }
+ end
+
+ def allowed_for?(user, level)
+ user.is_admin? || !Gitlab.config.gitlab.restricted_visibility_levels.include?(level)
+ end
+ end
+
+ def private?
+ visibility_level_field == PRIVATE
+ end
+
+ def internal?
+ visibility_level_field == INTERNAL
+ end
+
+ def public?
+ visibility_level_field == PUBLIC
+ end
+ end
+end
diff --git a/lib/redcarpet/render/gitlab_html.rb b/lib/redcarpet/render/gitlab_html.rb
index b84c005524f..2d1e0aec5e5 100644
--- a/lib/redcarpet/render/gitlab_html.rb
+++ b/lib/redcarpet/render/gitlab_html.rb
@@ -36,7 +36,7 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
def preprocess(full_document)
if @project
- h.create_relative_links(full_document, @project.path_with_namespace, @ref, @request_path, is_wiki?)
+ h.create_relative_links(full_document, @project, @ref, @request_path, is_wiki?)
else
full_document
end
diff --git a/lib/support/deploy/deploy.sh b/lib/support/deploy/deploy.sh
index 0d2f8418bcf..b96f73058b6 100755
--- a/lib/support/deploy/deploy.sh
+++ b/lib/support/deploy/deploy.sh
@@ -28,17 +28,18 @@ sudo -u git -H git pull origin master
echo 'Deploy: Bundle and migrate'
# change it to your needs
-sudo -u git -H bundle --without postgres
+sudo -u git -H bundle --without aws development test postgres --deployment
sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
+sudo -u git -H bundle exec rake assets:clean RAILS_ENV=production
+sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
+sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
# return stashed changes (if necessary)
# sudo -u git -H git stash pop
-
echo 'Deploy: Starting GitLab server...'
sudo service gitlab start
-sleep 10
sudo -u git -H rm /home/git/gitlab/public/index.html
-echo 'Deploy: Done'
+echo 'Deploy: Done' \ No newline at end of file
diff --git a/lib/support/init.d/gitlab b/lib/support/init.d/gitlab
index fbb7380ac47..f1b94087b6a 100755
--- a/lib/support/init.d/gitlab
+++ b/lib/support/init.d/gitlab
@@ -15,11 +15,20 @@
# Description: GitLab git repository management
### END INIT INFO
+
+###
+# DO NOT EDIT THIS FILE!
+# This file will be overwritten on update.
+# Instead add/change your variables in /etc/default/gitlab
+# An example defaults file can be found in lib/support/default/gitlab
+###
+
+
### Environment variables
RAILS_ENV="production"
-# Script variable names should be lower-case not to conflict with internal
-# /bin/sh variables such as PATH, EDITOR or SHELL.
+# Script variable names should be lower-case not to conflict with
+# internal /bin/sh variables such as PATH, EDITOR or SHELL.
app_user="git"
app_root="/home/$app_user/gitlab"
pid_path="$app_root/tmp/pids"
@@ -27,21 +36,20 @@ socket_path="$app_root/tmp/sockets"
web_server_pid_path="$pid_path/unicorn.pid"
sidekiq_pid_path="$pid_path/sidekiq.pid"
-
-
-### Here ends user configuration ###
-
+# Read configuration variable file if it is present
+test -f /etc/default/gitlab && . /etc/default/gitlab
# Switch to the app_user if it is not he/she who is running the script.
if [ "$USER" != "$app_user" ]; then
sudo -u "$app_user" -H -i $0 "$@"; exit;
fi
-# Switch to the gitlab path, if it fails exit with an error.
+# Switch to the gitlab path, exit on failure.
if ! cd "$app_root" ; then
echo "Failed to cd into $app_root, exiting!"; exit 1
fi
+
### Init Script functions
## Gets the pids from the files
diff --git a/lib/support/init.d/gitlab.default.example b/lib/support/init.d/gitlab.default.example
new file mode 100755
index 00000000000..4230783a9d7
--- /dev/null
+++ b/lib/support/init.d/gitlab.default.example
@@ -0,0 +1,14 @@
+# Copy this lib/support/init.d/gitlab.default.example file to
+# /etc/default/gitlab in order for it to apply to your system.
+
+# RAILS_ENV defines the type of installation that is running.
+# Normal values are "production", "test" and "development".
+RAILS_ENV="production"
+
+# app_user defines the user that GitLab is run as.
+# The default is "git".
+app_user="git"
+
+# app_root defines the folder in which gitlab and it's components are installed.
+# The default is "/home/$app_user/gitlab"
+app_root="/home/$app_user/gitlab"
diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab
index 3e929c52990..d1d959e152e 100644
--- a/lib/support/nginx/gitlab
+++ b/lib/support/nginx/gitlab
@@ -11,6 +11,9 @@ server {
server_name YOUR_SERVER_FQDN; # e.g., server_name source.example.com;
server_tokens off; # don't show the version number, a security best practice
root /home/git/gitlab/public;
+
+ # Set value of client_max_body_size to at least the value of git.max_size in gitlab.yml
+ client_max_body_size 5m;
# individual nginx logs for this gitlab vhost
access_log /var/log/nginx/gitlab_access.log;
@@ -25,13 +28,14 @@ server {
# if a file, which is not found in the root folder is requested,
# then the proxy pass the request to the upsteam (gitlab unicorn)
location @gitlab {
- proxy_read_timeout 300; # https://github.com/gitlabhq/gitlabhq/issues/694
- proxy_connect_timeout 300; # https://github.com/gitlabhq/gitlabhq/issues/694
+ proxy_read_timeout 300; # Some requests take more than 30 seconds.
+ proxy_connect_timeout 300; # Some requests take more than 30 seconds.
proxy_redirect off;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://gitlab;
}
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index e01f3b23d03..20d5f03d6ef 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -3,6 +3,7 @@ namespace :gitlab do
task check: %w{gitlab:env:check
gitlab:gitlab_shell:check
gitlab:sidekiq:check
+ gitlab:ldap:check
gitlab:app:check}
@@ -611,10 +612,7 @@ namespace :gitlab do
end
def gitlab_shell_version
- gitlab_shell_version_file = "#{gitlab_shell_user_home}/gitlab-shell/VERSION"
- if File.readable?(gitlab_shell_version_file)
- File.read(gitlab_shell_version_file)
- end
+ Gitlab::Shell.new.version
end
def has_gitlab_shell3?
@@ -648,7 +646,7 @@ namespace :gitlab do
else
puts "no".red
try_fixing_it(
- sudo_gitlab("bundle exec rake sidekiq:start RAILS_ENV=production")
+ sudo_gitlab("RAILS_ENV=production script/background_jobs start")
)
for_more_information(
see_installation_guide_section("Install Init Script"),
@@ -682,6 +680,44 @@ namespace :gitlab do
end
end
+ namespace :ldap do
+ task :check, [:limit] => :environment do |t, args|
+ args.with_defaults(limit: 100)
+ warn_user_is_not_gitlab
+ start_checking "LDAP"
+
+ if ldap_config.enabled
+ print_users(args.limit)
+ else
+ puts 'LDAP is disabled in config/gitlab.yml'
+ end
+
+ finished_checking "LDAP"
+ end
+
+ def print_users(limit)
+ puts "LDAP users with access to your GitLab server (limit: #{limit}):"
+ ldap.search(attributes: attributes, filter: filter, size: limit, return_result: false) do |entry|
+ puts "DN: #{entry.dn}\t#{ldap_config.uid}: #{entry[ldap_config.uid]}"
+ end
+ end
+
+ def attributes
+ [ldap_config.uid]
+ end
+
+ def filter
+ Net::LDAP::Filter.present?(ldap_config.uid)
+ end
+
+ def ldap
+ @ldap ||= OmniAuth::LDAP::Adaptor.new(ldap_config).connection
+ end
+
+ def ldap_config
+ @ldap_config ||= Gitlab.config.ldap
+ end
+ end
# Helper methods
##########################
@@ -736,7 +772,7 @@ namespace :gitlab do
end
def check_gitlab_shell
- required_version = Gitlab::VersionInfo.new(1, 7, 1)
+ required_version = Gitlab::VersionInfo.new(1, 7, 9)
current_version = Gitlab::VersionInfo.parse(gitlab_shell_version)
print "GitLab Shell version >= #{required_version} ? ... "
diff --git a/lib/tasks/gitlab/task_helpers.rake b/lib/tasks/gitlab/task_helpers.rake
index ac2c4577c77..c46d5855faf 100644
--- a/lib/tasks/gitlab/task_helpers.rake
+++ b/lib/tasks/gitlab/task_helpers.rake
@@ -2,6 +2,16 @@ module Gitlab
class TaskAbortedByUserError < StandardError; end
end
+unless STDOUT.isatty
+ module Colored
+ extend self
+
+ def colorize(string, options={})
+ string
+ end
+ end
+end
+
namespace :gitlab do
# Ask if the user wants to continue
diff --git a/public/favicon.ico b/public/favicon.ico
index 057f74ac7ab..bfb74960c48 100644
--- a/public/favicon.ico
+++ b/public/favicon.ico
Binary files differ
diff --git a/script/upgrade.rb b/script/upgrade.rb
new file mode 100644
index 00000000000..a5caecf8526
--- /dev/null
+++ b/script/upgrade.rb
@@ -0,0 +1,3 @@
+require_relative "../lib/gitlab/upgrader"
+
+Gitlab::Upgrader.new.execute
diff --git a/spec/contexts/filter_context_spec.rb b/spec/contexts/filter_context_spec.rb
index db27742b9b5..06aef5d7ed1 100644
--- a/spec/contexts/filter_context_spec.rb
+++ b/spec/contexts/filter_context_spec.rb
@@ -1,38 +1,38 @@
require 'spec_helper'
describe FilterContext do
-
let(:user) { create :user }
let(:user2) { create :user }
- let(:project1) { create(:project, creator_id: user.id) }
- let(:project2) { create(:project, creator_id: user.id) }
- let(:merge_request1) { create(:merge_request, author_id: user.id, source_project: project1, target_project: project2) }
- let(:merge_request2) { create(:merge_request, author_id: user.id, source_project: project2, target_project: project1) }
- let(:merge_request3) { create(:merge_request, author_id: user.id, source_project: project2, target_project: project2) }
- let(:merge_request4) { create(:merge_request, author_id: user2.id, source_project: project2, target_project: project2, target_branch:"notes_refactoring") }
- let(:issue1) { create(:issue, assignee_id: user.id, project: project1) }
- let(:issue2) { create(:issue, assignee_id: user.id, project: project2) }
- let(:issue3) { create(:issue, assignee_id: user2.id, project: project2) }
+ let(:project1) { create(:project) }
+ let(:project2) { create(:project) }
+ let(:merge_request1) { create(:merge_request, author: user, source_project: project1, target_project: project2) }
+ let(:merge_request2) { create(:merge_request, author: user, source_project: project2, target_project: project1) }
+ let(:merge_request3) { create(:merge_request, author: user, source_project: project2, target_project: project2) }
+ let(:issue1) { create(:issue, assignee: user, project: project1) }
+ let(:issue2) { create(:issue, assignee: user, project: project2) }
+ let(:issue3) { create(:issue, assignee: user2, project: project2) }
+
+ before do
+ project1.team << [user, :master]
+ project2.team << [user, :developer]
+ end
describe 'merge requests' do
before :each do
merge_request1
merge_request2
merge_request3
- merge_request4
end
- it 'should by default filter properly' do
- merge_requests = user.cared_merge_requests
- params ={}
- merge_requests = FilterContext.new(merge_requests, params).execute
+ it 'should filter by scope' do
+ params = { scope: 'authored' }
+ merge_requests = FilterContext.new(MergeRequest, user, params).execute
merge_requests.size.should == 3
end
- it 'should apply blocks passed in on creation to the filters' do
- merge_requests = user.cared_merge_requests
- params = {:project_id => project1.id}
- merge_requests = FilterContext.new(merge_requests, params).execute
+ it 'should filter by project' do
+ params = { project_id: project1.id, scope: 'authored' }
+ merge_requests = FilterContext.new(MergeRequest, user, params).execute
merge_requests.size.should == 1
end
end
@@ -43,16 +43,22 @@ describe FilterContext do
issue2
issue3
end
- it 'should by default filter projects properly' do
- issues = user.assigned_issues
+
+ it 'should filter by all' do
+ params = { scope: "all" }
+ issues = FilterContext.new(Issue, user, params).execute
+ issues.size.should == 3
+ end
+
+ it 'should filter by assignee' do
params = {}
- issues = FilterContext.new(issues, params).execute
+ issues = FilterContext.new(Issue, user, params).execute
issues.size.should == 2
end
- it 'should apply blocks passed in on creation to the filters' do
- issues = user.assigned_issues
- params = {:project_id => project1.id}
- issues = FilterContext.new(issues, params).execute
+
+ it 'should filter by project' do
+ params = { project_id: project1.id }
+ issues = FilterContext.new(Issue, user, params).execute
issues.size.should == 1
end
end
diff --git a/spec/contexts/fork_context_spec.rb b/spec/contexts/fork_context_spec.rb
index ed51b0c3f8e..70f650bc83d 100644
--- a/spec/contexts/fork_context_spec.rb
+++ b/spec/contexts/fork_context_spec.rb
@@ -48,7 +48,7 @@ describe Projects::ForkContext do
def fork_project(from_project, user, fork_success = true)
context = Projects::ForkContext.new(from_project, user)
- shell = mock("gitlab_shell")
+ shell = double("gitlab_shell")
shell.stub(fork_repository: fork_success)
context.stub(gitlab_shell: shell)
context.execute
diff --git a/spec/contexts/issues/list_context_spec.rb b/spec/contexts/issues/list_context_spec.rb
new file mode 100644
index 00000000000..70ce956499c
--- /dev/null
+++ b/spec/contexts/issues/list_context_spec.rb
@@ -0,0 +1,73 @@
+require 'spec_helper'
+
+describe Issues::ListContext do
+
+ let(:user) { create(:user) }
+ let(:project) { create(:project, creator: user) }
+
+ titles = ['foo','bar','baz']
+ titles.each_with_index do |title, index|
+ let!(title.to_sym) { create(:issue, title: title, project: project, created_at: Time.now - (index * 60)) }
+ end
+
+ describe 'sorting' do
+ it 'sorts by newest' do
+ params = {sort: 'newest'}
+
+ issues = Issues::ListContext.new(project, user, params).execute
+ issues.first.should eq foo
+ end
+
+ it 'sorts by oldest' do
+ params = {sort: 'oldest'}
+
+ issues = Issues::ListContext.new(project, user, params).execute
+ issues.first.should eq baz
+ end
+
+ it 'sorts by recently updated' do
+ params = {sort: 'recently_updated'}
+ baz.updated_at = Time.now + 10
+ baz.save
+
+ issues = Issues::ListContext.new(project, user, params).execute
+ issues.first.should eq baz
+ end
+
+ it 'sorts by least recently updated' do
+ params = {sort: 'last_updated'}
+ bar.updated_at = Time.now - 10
+ bar.save
+
+ issues = Issues::ListContext.new(project, user, params).execute
+ issues.first.should eq bar
+ end
+
+ describe 'sorting by milestone' do
+ let(:newer_due_milestone) { create(:milestone, due_date: '2013-12-11') }
+ let(:later_due_milestone) { create(:milestone, due_date: '2013-12-12') }
+
+ before :each do
+ foo.milestone = newer_due_milestone
+ foo.save
+ bar.milestone = later_due_milestone
+ bar.save
+ end
+
+ it 'sorts by most recently due milestone' do
+ params = {sort: 'milestone_due_soon'}
+
+ issues = Issues::ListContext.new(project, user, params).execute
+ issues.first.should eq foo
+
+ end
+
+ it 'sorts by least recently due milestone' do
+ params = {sort: 'milestone_due_later'}
+
+ issues = Issues::ListContext.new(project, user, params).execute
+ issues.first.should eq bar
+ end
+ end
+ end
+end
diff --git a/spec/contexts/projects_create_context_spec.rb b/spec/contexts/projects_create_context_spec.rb
index 8b2a49dbee5..d5b1cb83510 100644
--- a/spec/contexts/projects_create_context_spec.rb
+++ b/spec/contexts/projects_create_context_spec.rb
@@ -7,6 +7,7 @@ describe Projects::CreateContext do
describe :create_by_user do
before do
@user = create :user
+ @admin = create :user, admin: true
@opts = {
name: "GitLab",
namespace: @user.namespace
@@ -37,7 +38,7 @@ describe Projects::CreateContext do
it { @project.namespace.should == @group }
end
- context 'respect configured public setting' do
+ context 'respect configured visibility setting' do
before(:each) do
@settings = double("settings")
@settings.stub(:issues) { true }
@@ -46,25 +47,90 @@ describe Projects::CreateContext do
@settings.stub(:wall) { true }
@settings.stub(:snippets) { true }
stub_const("Settings", Class.new)
+ @restrictions = double("restrictions")
+ @restrictions.stub(:restricted_visibility_levels) { [] }
+ Settings.stub_chain(:gitlab).and_return(@restrictions)
Settings.stub_chain(:gitlab, :default_projects_features).and_return(@settings)
end
context 'should be public when setting is public' do
before do
- @settings.stub(:public) { true }
+ @settings.stub(:visibility_level) { Gitlab::VisibilityLevel::PUBLIC }
@project = create_project(@user, @opts)
end
- it { @project.public.should be_true }
+ it { @project.public?.should be_true }
end
- context 'should be private when setting is not public' do
+ context 'should be private when setting is private' do
before do
- @settings.stub(:public) { false }
+ @settings.stub(:visibility_level) { Gitlab::VisibilityLevel::PRIVATE }
@project = create_project(@user, @opts)
end
- it { @project.public.should be_false }
+ it { @project.private?.should be_true }
+ end
+
+ context 'should be internal when setting is internal' do
+ before do
+ @settings.stub(:visibility_level) { Gitlab::VisibilityLevel::INTERNAL }
+ @project = create_project(@user, @opts)
+ end
+
+ it { @project.internal?.should be_true }
+ end
+ end
+
+ context 'respect configured visibility restrictions setting' do
+ before(:each) do
+ @settings = double("settings")
+ @settings.stub(:issues) { true }
+ @settings.stub(:merge_requests) { true }
+ @settings.stub(:wiki) { true }
+ @settings.stub(:wall) { true }
+ @settings.stub(:snippets) { true }
+ @settings.stub(:visibility_level) { Gitlab::VisibilityLevel::PRIVATE }
+ stub_const("Settings", Class.new)
+ @restrictions = double("restrictions")
+ @restrictions.stub(:restricted_visibility_levels) { [ Gitlab::VisibilityLevel::PUBLIC ] }
+ Settings.stub_chain(:gitlab).and_return(@restrictions)
+ Settings.stub_chain(:gitlab, :default_projects_features).and_return(@settings)
+ end
+
+ context 'should be private when option is public' do
+ before do
+ @opts.merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+ @project = create_project(@user, @opts)
+ end
+
+ it { @project.private?.should be_true }
+ end
+
+ context 'should be public when option is public for admin' do
+ before do
+ @opts.merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+ @project = create_project(@admin, @opts)
+ end
+
+ it { @project.public?.should be_true }
+ end
+
+ context 'should be private when option is private' do
+ before do
+ @opts.merge!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ @project = create_project(@user, @opts)
+ end
+
+ it { @project.private?.should be_true }
+ end
+
+ context 'should be internal when option is internal' do
+ before do
+ @opts.merge!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
+ @project = create_project(@user, @opts)
+ end
+
+ it { @project.internal?.should be_true }
end
end
end
@@ -73,3 +139,4 @@ describe Projects::CreateContext do
Projects::CreateContext.new(user, opts).execute
end
end
+
diff --git a/spec/contexts/projects_update_context_spec.rb b/spec/contexts/projects_update_context_spec.rb
new file mode 100644
index 00000000000..edcaf844e5d
--- /dev/null
+++ b/spec/contexts/projects_update_context_spec.rb
@@ -0,0 +1,111 @@
+require 'spec_helper'
+
+describe Projects::UpdateContext do
+ before(:each) { ActiveRecord::Base.observers.enable(:user_observer) }
+ after(:each) { ActiveRecord::Base.observers.disable(:user_observer) }
+
+ describe :update_by_user do
+ before do
+ @user = create :user
+ @admin = create :user, admin: true
+ @project = create :project, creator_id: @user.id, namespace: @user.namespace
+ @opts = { project: {} }
+ end
+
+ context 'should be private when updated to private' do
+ before do
+ @created_private = @project.private?
+
+ @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ update_project(@project, @user, @opts)
+ end
+
+ it { @created_private.should be_true }
+ it { @project.private?.should be_true }
+ end
+
+ context 'should be internal when updated to internal' do
+ before do
+ @created_private = @project.private?
+
+ @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
+ update_project(@project, @user, @opts)
+ end
+
+ it { @created_private.should be_true }
+ it { @project.internal?.should be_true }
+ end
+
+ context 'should be public when updated to public' do
+ before do
+ @created_private = @project.private?
+
+ @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+ update_project(@project, @user, @opts)
+ end
+
+ it { @created_private.should be_true }
+ it { @project.public?.should be_true }
+ end
+
+ context 'respect configured visibility restrictions setting' do
+ before(:each) do
+ @restrictions = double("restrictions")
+ @restrictions.stub(:restricted_visibility_levels) { [ Gitlab::VisibilityLevel::PUBLIC ] }
+ Settings.stub_chain(:gitlab).and_return(@restrictions)
+ end
+
+ context 'should be private when updated to private' do
+ before do
+ @created_private = @project.private?
+
+ @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ update_project(@project, @user, @opts)
+ end
+
+ it { @created_private.should be_true }
+ it { @project.private?.should be_true }
+ end
+
+ context 'should be internal when updated to internal' do
+ before do
+ @created_private = @project.private?
+
+ @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
+ update_project(@project, @user, @opts)
+ end
+
+ it { @created_private.should be_true }
+ it { @project.internal?.should be_true }
+ end
+
+ context 'should be private when updated to public' do
+ before do
+ @created_private = @project.private?
+
+ @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+ update_project(@project, @user, @opts)
+ end
+
+ it { @created_private.should be_true }
+ it { @project.private?.should be_true }
+ end
+
+ context 'should be public when updated to public by admin' do
+ before do
+ @created_private = @project.private?
+
+ @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+ update_project(@project, @admin, @opts)
+ end
+
+ it { @created_private.should be_true }
+ it { @project.public?.should be_true }
+ end
+ end
+ end
+
+ def update_project(project, user, opts)
+ Projects::UpdateContext.new(project, user, opts).execute
+ end
+end \ No newline at end of file
diff --git a/spec/contexts/search_context_spec.rb b/spec/contexts/search_context_spec.rb
new file mode 100644
index 00000000000..c25743e0032
--- /dev/null
+++ b/spec/contexts/search_context_spec.rb
@@ -0,0 +1,43 @@
+require 'spec_helper'
+
+describe SearchContext do
+ let(:found_namespace) { create(:namespace, name: 'searchable namespace', path:'another_thing') }
+ let(:user) { create(:user, namespace: found_namespace) }
+ let!(:found_project) { create(:project, name: 'searchable_project', creator_id: user.id, namespace: found_namespace, visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
+
+ let(:unfound_namespace) { create(:namespace, name: 'unfound namespace', path: 'yet_something_else') }
+ let!(:unfound_project) { create(:project, name: 'unfound_project', creator_id: user.id, namespace: unfound_namespace, visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
+
+ let(:internal_namespace) { create(:namespace, path: 'something_internal',name: 'searchable internal namespace') }
+ let(:internal_user) { create(:user, namespace: internal_namespace) }
+ let!(:internal_project) { create(:project, name: 'searchable_internal_project', creator_id: internal_user.id, namespace: internal_namespace, visibility_level: Gitlab::VisibilityLevel::INTERNAL) }
+
+ let(:public_namespace) { create(:namespace, path: 'something_public',name: 'searchable public namespace') }
+ let(:public_user) { create(:user, namespace: public_namespace) }
+ let!(:public_project) { create(:project, name: 'searchable_public_project', creator_id: public_user.id, namespace: public_namespace, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
+
+ describe '#execute' do
+ it 'public projects should be searchable' do
+ context = SearchContext.new([found_project.id], nil, {search_code: false, search: "searchable"})
+ results = context.execute
+ results[:projects].should == [found_project, public_project]
+ end
+
+ it 'internal projects should be searchable' do
+ context = SearchContext.new([found_project.id], user, {search_code: false, search: "searchable"})
+ results = context.execute
+ # can't seem to rely on the return order, so check this way
+ #subject { results[:projects] }
+ results[:projects].should have(3).items
+ results[:projects].should include(found_project)
+ results[:projects].should include(internal_project)
+ results[:projects].should include(public_project)
+ end
+
+ it 'namespace name should be searchable' do
+ context = SearchContext.new([found_project.id], user, {search_code: false, search: "searchable namespace"})
+ results = context.execute
+ results[:projects].should == [found_project]
+ end
+ end
+end
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index d528d12c66c..e1c0269b295 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -8,7 +8,7 @@ describe ApplicationController do
it 'should redirect if the user is over their password expiry' do
user.password_expires_at = Time.new(2002)
user.ldap_user?.should be_false
- controller.stub!(:current_user).and_return(user)
+ controller.stub(:current_user).and_return(user)
controller.should_receive(:redirect_to)
controller.should_receive(:new_profile_password_path)
controller.send(:check_password_expiration)
@@ -17,15 +17,15 @@ describe ApplicationController do
it 'should not redirect if the user is under their password expiry' do
user.password_expires_at = Time.now + 20010101
user.ldap_user?.should be_false
- controller.stub!(:current_user).and_return(user)
+ controller.stub(:current_user).and_return(user)
controller.should_not_receive(:redirect_to)
controller.send(:check_password_expiration)
end
it 'should not redirect if the user is over their password expiry but they are an ldap user' do
user.password_expires_at = Time.new(2002)
- user.stub!(:ldap_user?).and_return(true)
- controller.stub!(:current_user).and_return(user)
+ user.stub(:ldap_user?).and_return(true)
+ controller.stub(:current_user).and_return(user)
controller.should_not_receive(:redirect_to)
controller.send(:check_password_expiration)
end
diff --git a/spec/factories.rb b/spec/factories.rb
index 624cb0f7654..daf84173648 100644
--- a/spec/factories.rb
+++ b/spec/factories.rb
@@ -15,7 +15,7 @@ FactoryGirl.define do
email { Faker::Internet.email }
name
sequence(:username) { |n| "#{Faker::Internet.user_name}#{n}" }
- password "123456"
+ password "12345678"
password_confirmation { password }
confirmed_at { Time.now }
confirmation_token { nil }
@@ -66,6 +66,7 @@ FactoryGirl.define do
after :create do |project|
TestEnv.clear_repo_dir(project.namespace, project.path)
+ TestEnv.reset_satellite_dir
TestEnv.create_repo(project.namespace, project.path)
end
end
diff --git a/spec/factories/broadcast_messages.rb b/spec/factories/broadcast_messages.rb
new file mode 100644
index 00000000000..6339d5c4003
--- /dev/null
+++ b/spec/factories/broadcast_messages.rb
@@ -0,0 +1,27 @@
+# == Schema Information
+#
+# Table name: broadcast_messages
+#
+# id :integer not null, primary key
+# message :text default(""), not null
+# starts_at :datetime
+# ends_at :datetime
+# alert_type :integer
+# created_at :datetime not null
+# updated_at :datetime not null
+# color :string(255)
+# font :string(255)
+#
+
+# Read about factories at https://github.com/thoughtbot/factory_girl
+
+FactoryGirl.define do
+ factory :broadcast_message do
+ message "MyText"
+ starts_at "2013-11-12 13:43:25"
+ ends_at "2013-11-12 13:43:25"
+ alert_type 1
+ color "#555555"
+ font "#BBBBBB"
+ end
+end
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
index 8cb984946b4..538a6ee9fc9 100644
--- a/spec/features/issues_spec.rb
+++ b/spec/features/issues_spec.rb
@@ -95,4 +95,169 @@ describe "Issues" do
page.should have_content 'gitlab'
end
end
+
+ describe 'filter issue' do
+ titles = ['foo','bar','baz']
+ titles.each_with_index do |title, index|
+ let!(title.to_sym) { create(:issue, title: title, project: project, created_at: Time.now - (index * 60)) }
+ end
+ let(:newer_due_milestone) { create(:milestone, due_date: '2013-12-11') }
+ let(:later_due_milestone) { create(:milestone, due_date: '2013-12-12') }
+
+ it 'sorts by newest' do
+ visit project_issues_path(project, sort: 'newest')
+
+ first_issue.should include("foo")
+ last_issue.should include("baz")
+ end
+
+ it 'sorts by oldest' do
+ visit project_issues_path(project, sort: 'oldest')
+
+ first_issue.should include("baz")
+ last_issue.should include("foo")
+ end
+
+ it 'sorts by most recently updated' do
+ baz.updated_at = Time.now + 100
+ baz.save
+ visit project_issues_path(project, sort: 'recently_updated')
+
+ first_issue.should include("baz")
+ end
+
+ it 'sorts by least recently updated' do
+ baz.updated_at = Time.now - 100
+ baz.save
+ visit project_issues_path(project, sort: 'last_updated')
+
+ first_issue.should include("baz")
+ end
+
+ describe 'sorting by milestone' do
+ before :each do
+ foo.milestone = newer_due_milestone
+ foo.save
+ bar.milestone = later_due_milestone
+ bar.save
+ end
+
+ it 'sorts by recently due milestone' do
+ visit project_issues_path(project, sort: 'milestone_due_soon')
+
+ first_issue.should include("foo")
+ end
+
+ it 'sorts by least recently due milestone' do
+ visit project_issues_path(project, sort: 'milestone_due_later')
+
+ first_issue.should include("bar")
+ end
+ end
+
+ describe 'combine filter and sort' do
+ let(:user2) { create(:user) }
+
+ before :each do
+ foo.assignee = user2
+ foo.save
+ bar.assignee = user2
+ bar.save
+ end
+
+ it 'sorts with a filter applied' do
+ visit project_issues_path(project, sort: 'oldest', assignee_id: user2.id)
+
+ first_issue.should include("bar")
+ last_issue.should include("foo")
+ page.should_not have_content 'baz'
+ end
+ end
+ end
+
+ describe 'update assignee from issue#show' do
+ let(:issue) { create(:issue, project: project, author: @user) }
+
+ context 'by autorized user' do
+
+ it 'with dropdown menu' do
+ visit project_issue_path(project, issue)
+
+ find('.edit-issue.inline-update').select(project.team.members.first.name, from: 'issue_assignee_id')
+ click_button 'Update Issue'
+
+ page.should have_content "currently assigned to"
+ page.has_select?('issue_assignee_id', :selected => project.team.members.first.name)
+ end
+ end
+
+ context 'by unauthorized user' do
+
+ let(:guest) { create(:user) }
+
+ before :each do
+ project.team << [[guest], :guest]
+ issue.assignee = @user
+ issue.save
+ end
+
+ it 'shows assignee text' do
+ logout
+ login_with guest
+
+ visit project_issue_path(project, issue)
+ page.should have_content "currently assigned to #{issue.assignee.name}"
+
+ end
+ end
+
+ end
+
+ describe 'update milestone from issue#show' do
+ let!(:issue) { create(:issue, project: project, author: @user) }
+ let!(:milestone) { create(:milestone, project: project) }
+
+ context 'by authorized user' do
+
+ it 'with dropdown menu' do
+ visit project_issue_path(project, issue)
+
+ p find('.edit-issue.inline-update').text
+
+ find('.edit-issue.inline-update').select(milestone.title, from: 'issue_milestone_id')
+ click_button 'Update Issue'
+
+ page.should have_content "Attached to milestone"
+ page.has_select?('issue_assignee_id', :selected => milestone.title)
+ end
+ end
+
+ context 'by unauthorized user' do
+
+ let(:guest) { create(:user) }
+
+ before :each do
+ project.team << [[guest], :guest]
+ issue.milestone = milestone
+ issue.save
+ end
+
+ it 'shows milestone text' do
+ logout
+ login_with guest
+
+ visit project_issue_path(project, issue)
+
+ page.should have_content "Attached to milestone #{milestone.title}"
+ end
+ end
+ end
+
+ def first_issue
+ all("ul.issues-list li").first.text
+ end
+
+ def last_issue
+ all("ul.issues-list li").last.text
+ end
end
diff --git a/spec/features/notes_on_merge_requests_spec.rb b/spec/features/notes_on_merge_requests_spec.rb
index d29bed4dea7..b534548a122 100644
--- a/spec/features/notes_on_merge_requests_spec.rb
+++ b/spec/features/notes_on_merge_requests_spec.rb
@@ -108,7 +108,7 @@ describe "On a merge request", js: true do
within("#note_#{note.id}") do
should have_css(".note-last-update small")
- find(".note-last-update small").text.should match(/Edited just now/)
+ find(".note-last-update small").text.should match(/Edited less than a minute ago/)
end
end
end
@@ -159,13 +159,14 @@ describe "On a merge request diff", js: true, focus: true do
end
describe "the note form" do
- it 'should be valid' do
- within(".js-temp-notes-holder") { find("#note_noteable_type").value.should == "MergeRequest" }
- within(".js-temp-notes-holder") { find("#note_noteable_id").value.should == merge_request.id.to_s }
- within(".js-temp-notes-holder") { find("#note_commit_id").value.should == "" }
- within(".js-temp-notes-holder") { find("#note_line_code").value.should == "4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185" }
- should have_css(".js-close-discussion-note-form", text: "Cancel")
- end
+ # FIXME
+ #it 'should be valid' do
+ #within(".js-temp-notes-holder") { find("#note_noteable_type").value.should == "MergeRequest" }
+ #within(".js-temp-notes-holder") { find("#note_noteable_id").value.should == merge_request.id.to_s }
+ #within(".js-temp-notes-holder") { find("#note_commit_id").value.should == "" }
+ #within(".js-temp-notes-holder") { find("#note_line_code").value.should == "4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185" }
+ #should have_css(".js-close-discussion-note-form", text: "Cancel")
+ #end
it "shouldn't add a second form for same row" do
find('a[data-line-code="4735dfc552ad7bf15ca468adc3cad9d05b624490_185_185"]').click
diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb
new file mode 100644
index 00000000000..5abccd259d4
--- /dev/null
+++ b/spec/features/security/project/internal_access_spec.rb
@@ -0,0 +1,251 @@
+require 'spec_helper'
+
+describe "Internal Project Access" do
+ let(:project) { create(:project_with_code) }
+
+ let(:master) { create(:user) }
+ let(:guest) { create(:user) }
+ let(:reporter) { create(:user) }
+
+ before do
+ # internal project
+ project.visibility_level = Gitlab::VisibilityLevel::INTERNAL
+ project.save!
+
+ # full access
+ project.team << [master, :master]
+
+ # readonly
+ project.team << [reporter, :reporter]
+
+ end
+
+ describe "Project should be internal" do
+ subject { project }
+
+ its(:internal?) { should be_true }
+ end
+
+ describe "GET /:project_path" do
+ subject { project_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for guest }
+ it { should be_allowed_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/tree/master" do
+ subject { project_tree_path(project, project.repository.root_ref) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for guest }
+ it { should be_allowed_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/commits/master" do
+ subject { project_commits_path(project, project.repository.root_ref, limit: 1) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for guest }
+ it { should be_allowed_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/commit/:sha" do
+ subject { project_commit_path(project, project.repository.commit) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for guest }
+ it { should be_allowed_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/compare" do
+ subject { project_compare_index_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for guest }
+ it { should be_allowed_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/team" do
+ subject { project_team_index_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_denied_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_denied_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/wall" do
+ subject { project_wall_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for guest }
+ it { should be_allowed_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/blob" do
+ before do
+ commit = project.repository.commit
+ path = commit.tree.contents.select { |i| i.is_a?(Grit::Blob) }.first.name
+ @blob_path = project_blob_path(project, File.join(commit.id, path))
+ end
+
+ it { @blob_path.should be_allowed_for master }
+ it { @blob_path.should be_allowed_for reporter }
+ it { @blob_path.should be_allowed_for :admin }
+ it { @blob_path.should be_allowed_for guest }
+ it { @blob_path.should be_allowed_for :user }
+ it { @blob_path.should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/edit" do
+ subject { edit_project_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_denied_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_denied_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/deploy_keys" do
+ subject { project_deploy_keys_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_denied_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_denied_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/issues" do
+ subject { project_issues_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for guest }
+ it { should be_allowed_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/snippets" do
+ subject { project_snippets_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for guest }
+ it { should be_allowed_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/snippets/new" do
+ subject { new_project_snippet_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_denied_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/merge_requests" do
+ subject { project_merge_requests_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for guest }
+ it { should be_allowed_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/merge_requests/new" do
+ subject { new_project_merge_request_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_denied_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_denied_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/branches/recent" do
+ subject { recent_project_branches_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for guest }
+ it { should be_allowed_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/branches" do
+ subject { project_branches_path(project) }
+
+ before do
+ # Speed increase
+ Project.any_instance.stub(:branches).and_return([])
+ end
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for guest }
+ it { should be_allowed_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/tags" do
+ subject { project_tags_path(project) }
+
+ before do
+ # Speed increase
+ Project.any_instance.stub(:tags).and_return([])
+ end
+
+ it { should be_allowed_for master }
+ it { should be_allowed_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_allowed_for guest }
+ it { should be_allowed_for :user }
+ it { should be_denied_for :visitor }
+ end
+
+ describe "GET /:project_path/hooks" do
+ subject { project_hooks_path(project) }
+
+ it { should be_allowed_for master }
+ it { should be_denied_for reporter }
+ it { should be_allowed_for :admin }
+ it { should be_denied_for guest }
+ it { should be_denied_for :user }
+ it { should be_denied_for :visitor }
+ end
+end
diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb
index 7f3f8c50f02..481d8cec416 100644
--- a/spec/features/security/project/private_access_spec.rb
+++ b/spec/features/security/project/private_access_spec.rb
@@ -15,6 +15,12 @@ describe "Private Project Access" do
project.team << [reporter, :reporter]
end
+ describe "Project should be private" do
+ subject { project }
+
+ its(:private?) { should be_true }
+ end
+
describe "GET /:project_path" do
subject { project_path(project) }
diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb
index 267643fd8ef..3f1016473f5 100644
--- a/spec/features/security/project/public_access_spec.rb
+++ b/spec/features/security/project/public_access_spec.rb
@@ -9,7 +9,7 @@ describe "Public Project Access" do
before do
# public project
- project.public = true
+ project.visibility_level = Gitlab::VisibilityLevel::PUBLIC
project.save!
# full access
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index d63a2de8806..3644410e46d 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe ApplicationHelper do
describe 'current_controller?' do
before do
- controller.stub!(:controller_name).and_return('foo')
+ controller.stub(:controller_name).and_return('foo')
end
it "returns true when controller matches argument" do
@@ -22,7 +22,7 @@ describe ApplicationHelper do
describe 'current_action?' do
before do
- stub!(:action_name).and_return('foo')
+ allow(self).to receive(:action_name).and_return('foo')
end
it "returns true when action matches argument" do
@@ -38,7 +38,7 @@ describe ApplicationHelper do
current_action?(:baz, :bar, :foo).should be_true
end
end
-
+
describe "avatar_icon" do
avatar_file_path = File.join(Rails.root, 'public', 'gitlab_logo.png')
@@ -52,7 +52,7 @@ describe ApplicationHelper do
it "should call gravatar_icon when no avatar is present" do
user = create(:user)
user.save!
- stub!(:gravatar_icon).and_return('gravatar_method_called')
+ allow(self).to receive(:gravatar_icon).and_return('gravatar_method_called')
avatar_icon(user.email).to_s.should == "gravatar_method_called"
end
end
@@ -62,41 +62,41 @@ describe ApplicationHelper do
it "should return a generic avatar path when Gravatar is disabled" do
Gitlab.config.gravatar.stub(:enabled).and_return(false)
- gravatar_icon(user_email).should == 'no_avatar.png'
+ gravatar_icon(user_email).should == '/assets/no_avatar.png'
end
it "should return a generic avatar path when email is blank" do
- gravatar_icon('').should == 'no_avatar.png'
+ gravatar_icon('').should == '/assets/no_avatar.png'
end
it "should return default gravatar url" do
- stub!(:request).and_return(double(:ssl? => false))
+ allow(self).to receive(:request).and_return(double(:ssl? => false))
gravatar_icon(user_email).should match('http://www.gravatar.com/avatar/b58c6f14d292556214bd64909bcdb118')
end
it "should use SSL when appropriate" do
- stub!(:request).and_return(double(:ssl? => true))
+ allow(self).to receive(:request).and_return(double(:ssl? => true))
gravatar_icon(user_email).should match('https://secure.gravatar.com')
end
it "should return custom gravatar path when gravatar_url is set" do
- stub!(:request).and_return(double(:ssl? => false))
+ allow(self).to receive(:request).and_return(double(:ssl? => false))
Gitlab.config.gravatar.stub(:plain_url).and_return('http://example.local/?s=%{size}&hash=%{hash}')
gravatar_icon(user_email, 20).should == 'http://example.local/?s=20&hash=b58c6f14d292556214bd64909bcdb118'
end
it "should accept a custom size" do
- stub!(:request).and_return(double(:ssl? => false))
+ allow(self).to receive(:request).and_return(double(:ssl? => false))
gravatar_icon(user_email, 64).should match(/\?s=64/)
end
it "should use default size when size is wrong" do
- stub!(:request).and_return(double(:ssl? => false))
+ allow(self).to receive(:request).and_return(double(:ssl? => false))
gravatar_icon(user_email, nil).should match(/\?s=40/)
end
it "should be case insensitive" do
- stub!(:request).and_return(double(:ssl? => false))
+ allow(self).to receive(:request).and_return(double(:ssl? => false))
gravatar_icon(user_email).should == gravatar_icon(user_email.upcase + " ")
end
@@ -105,7 +105,7 @@ describe ApplicationHelper do
describe "user_color_scheme_class" do
context "with current_user is nil" do
it "should return a string" do
- stub!(:current_user).and_return(nil)
+ allow(self).to receive(:current_user).and_return(nil)
user_color_scheme_class.should be_kind_of(String)
end
end
@@ -115,7 +115,7 @@ describe ApplicationHelper do
context "with color_scheme_id == #{color_scheme_id}" do
it "should return a string" do
current_user = double(:color_scheme_id => color_scheme_id)
- stub!(:current_user).and_return(current_user)
+ allow(self).to receive(:current_user).and_return(current_user)
user_color_scheme_class.should be_kind_of(String)
end
end
diff --git a/spec/helpers/broadcast_messages_helper_spec.rb b/spec/helpers/broadcast_messages_helper_spec.rb
new file mode 100644
index 00000000000..1338ce4873d
--- /dev/null
+++ b/spec/helpers/broadcast_messages_helper_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+describe BroadcastMessagesHelper do
+ describe 'broadcast_styling' do
+ let(:broadcast_message) { double(color: "", font: "") }
+
+ context "default style" do
+ it "should have no style" do
+ broadcast_styling(broadcast_message).should match('')
+ end
+ end
+
+ context "customiezd style" do
+ before { broadcast_message.stub(color: "#f2dede", font: "#b94a48") }
+
+ it "should have a customized style" do
+ broadcast_styling(broadcast_message).should match('background-color:#f2dede;color:#b94a48')
+ end
+ end
+ end
+end
diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb
index a0bbc026421..33e69d4326c 100644
--- a/spec/helpers/gitlab_markdown_helper_spec.rb
+++ b/spec/helpers/gitlab_markdown_helper_spec.rb
@@ -378,9 +378,10 @@ describe GitlabMarkdownHelper do
it "should leave code blocks untouched" do
helper.stub(:user_color_scheme_class).and_return(:white)
- helper.markdown("\n some code from $#{snippet.id}\n here too\n").should include("<div class=\"white\"><div class=\"highlight\"><pre><span class=\"n\">some</span> <span class=\"n\">code</span> <span class=\"n\">from</span> $#{snippet.id}\n<span class=\"n\">here</span> <span class=\"n\">too</span>\n</pre></div></div>")
+ target_html = "<div class=\"white\"><div class=\"highlight\"><pre><span class=\"n\">some</span> <span class=\"n\">code</span> <span class=\"n\">from</span> <span class=\"err\">$</span><span class=\"mi\">#{snippet.id}</span>"
- helper.markdown("\n```\nsome code from $#{snippet.id}\nhere too\n```\n").should include("<div class=\"white\"><div class=\"highlight\"><pre><span class=\"n\">some</span> <span class=\"n\">code</span> <span class=\"n\">from</span> $#{snippet.id}\n<span class=\"n\">here</span> <span class=\"n\">too</span>\n</pre></div></div>")
+ helper.markdown("\n some code from $#{snippet.id}\n here too\n").should include(target_html)
+ helper.markdown("\n```\nsome code from $#{snippet.id}\nhere too\n```\n").should include(target_html)
end
it "should leave inline code untouched" do
@@ -392,7 +393,7 @@ describe GitlabMarkdownHelper do
end
it "should leave ref-like href of 'manual' links untouched" do
- markdown("why not [inspect !#{merge_request.iid}](http://example.tld/#!#{merge_request.iid})").should == "<p>why not <a href=\"http://example.tld/#!#{merge_request.iid}\">inspect </a><a href=\"#{project_merge_request_url(project, merge_request)}\" class=\"gfm gfm-merge_request \" title=\"Merge Request: #{merge_request.title}\">!#{merge_request.iid}</a><a href=\"http://example.tld/#!#{merge_request.iid}\"></a></p>\n"
+ markdown("why not [inspect !#{merge_request.iid}](http://example.tld/#!#{merge_request.iid})").should == "<p>why not <a href=\"http://example.tld/#!#{merge_request.iid}\">inspect </a><a class=\"gfm gfm-merge_request \" href=\"#{project_merge_request_url(project, merge_request)}\" title=\"Merge Request: #{merge_request.title}\">!#{merge_request.iid}</a><a href=\"http://example.tld/#!#{merge_request.iid}\"></a></p>\n"
end
it "should leave ref-like src of images untouched" do
@@ -434,7 +435,7 @@ describe GitlabMarkdownHelper do
describe "#render_wiki_content" do
before do
- @wiki = stub('WikiPage')
+ @wiki = double('WikiPage')
@wiki.stub(:content).and_return('wiki content')
end
@@ -448,7 +449,7 @@ describe GitlabMarkdownHelper do
it "should use the Gollum renderer for all other file types" do
@wiki.stub(:format).and_return(:rdoc)
- formatted_content_stub = stub('formatted_content')
+ formatted_content_stub = double('formatted_content')
formatted_content_stub.should_receive(:html_safe)
@wiki.stub(:formatted_content).and_return(formatted_content_stub)
diff --git a/spec/helpers/notifications_helper_spec.rb b/spec/helpers/notifications_helper_spec.rb
index 328f66237c3..c1efc1fb2a0 100644
--- a/spec/helpers/notifications_helper_spec.rb
+++ b/spec/helpers/notifications_helper_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe NotificationsHelper do
describe 'notification_icon' do
- let(:notification) { stub(disabled?: false, participating?: false, watch?: false) }
+ let(:notification) { double(disabled?: false, participating?: false, watch?: false) }
context "disabled notification" do
before { notification.stub(disabled?: true) }
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index 62f88dd522b..114058e3095 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -7,5 +7,17 @@ describe ProjectsHelper do
"<option value=\"redmine\">Redmine</option>\n" \
"<option value=\"gitlab\">GitLab</option>"
end
+
+ it "returns the correct issues trackers available with current tracker 'gitlab' selected" do
+ project_issues_trackers('gitlab').should ==
+ "<option value=\"redmine\">Redmine</option>\n" \
+ "<option selected=\"selected\" value=\"gitlab\">GitLab</option>"
+ end
+
+ it "returns the correct issues trackers available with current tracker 'redmine' selected" do
+ project_issues_trackers('redmine').should ==
+ "<option selected=\"selected\" value=\"redmine\">Redmine</option>\n" \
+ "<option value=\"gitlab\">GitLab</option>"
+ end
end
end
diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb
index 55b6b6b4dad..33ecb980202 100644
--- a/spec/helpers/search_helper_spec.rb
+++ b/spec/helpers/search_helper_spec.rb
@@ -8,7 +8,9 @@ describe SearchHelper do
describe 'search_autocomplete_source' do
context "with no current user" do
- before { stub!(:current_user).and_return(nil) }
+ before do
+ allow(self).to receive(:current_user).and_return(nil)
+ end
it "it returns nil" do
search_autocomplete_source.should be_nil
@@ -20,7 +22,7 @@ describe SearchHelper do
let(:result) { JSON.parse(search_autocomplete_source) }
before do
- stub!(:current_user).and_return(user)
+ allow(self).to receive(:current_user).and_return(user)
end
it "includes Help sections" do
diff --git a/spec/helpers/tab_helper_spec.rb b/spec/helpers/tab_helper_spec.rb
index ef8e4cf6375..fa8a3f554f7 100644
--- a/spec/helpers/tab_helper_spec.rb
+++ b/spec/helpers/tab_helper_spec.rb
@@ -5,8 +5,8 @@ describe TabHelper do
describe 'nav_link' do
before do
- controller.stub!(:controller_name).and_return('foo')
- stub!(:action_name).and_return('foo')
+ controller.stub(:controller_name).and_return('foo')
+ allow(self).to receive(:action_name).and_return('foo')
end
it "captures block output" do
diff --git a/spec/javascripts/stat_graph_contributors_graph_spec.js b/spec/javascripts/stat_graph_contributors_graph_spec.js
index 8d2e2038a55..1090cb7f620 100644
--- a/spec/javascripts/stat_graph_contributors_graph_spec.js
+++ b/spec/javascripts/stat_graph_contributors_graph_spec.js
@@ -88,19 +88,20 @@ describe("ContributorsGraph", function () {
describe("ContributorsMasterGraph", function () {
- describe("#process_dates", function () {
- it("gets and parses dates", function () {
- var graph = new ContributorsMasterGraph()
- var data = 'random data here'
- spyOn(graph, 'parse_dates')
- spyOn(graph, 'get_dates').andReturn("get")
- spyOn(ContributorsGraph,'set_dates').andCallThrough()
- graph.process_dates(data)
- expect(graph.parse_dates).toHaveBeenCalledWith(data)
- expect(graph.get_dates).toHaveBeenCalledWith(data)
- expect(ContributorsGraph.set_dates).toHaveBeenCalledWith("get")
- })
- })
+ // TODO: fix or remove
+ //describe("#process_dates", function () {
+ //it("gets and parses dates", function () {
+ //var graph = new ContributorsMasterGraph()
+ //var data = 'random data here'
+ //spyOn(graph, 'parse_dates')
+ //spyOn(graph, 'get_dates').andReturn("get")
+ //spyOn(ContributorsGraph,'set_dates').andCallThrough()
+ //graph.process_dates(data)
+ //expect(graph.parse_dates).toHaveBeenCalledWith(data)
+ //expect(graph.get_dates).toHaveBeenCalledWith(data)
+ //expect(ContributorsGraph.set_dates).toHaveBeenCalledWith("get")
+ //})
+ //})
describe("#get_dates", function () {
it("plucks the date field from data collection", function () {
diff --git a/spec/javascripts/stat_graph_contributors_util_spec.js b/spec/javascripts/stat_graph_contributors_util_spec.js
index 2e52479ccbb..9c1b588861d 100644
--- a/spec/javascripts/stat_graph_contributors_util_spec.js
+++ b/spec/javascripts/stat_graph_contributors_util_spec.js
@@ -54,16 +54,17 @@ describe("ContributorsStatGraphUtil", function () {
})
- describe("#store_commits", function () {
- var fake_total = "fake_total"
- var fake_by_author = "fake_by_author"
-
- it("calls #add twice with arguments fake_total and fake_by_author respectively", function () {
- spyOn(ContributorsStatGraphUtil, 'add')
- ContributorsStatGraphUtil.store_commits(fake_total, fake_by_author)
- expect(ContributorsStatGraphUtil.add.argsForCall).toEqual([["fake_total", "commits", 1], ["fake_by_author", "commits", 1]])
- })
- })
+ // TODO: fix or remove
+ //describe("#store_commits", function () {
+ //var fake_total = "fake_total"
+ //var fake_by_author = "fake_by_author"
+
+ //it("calls #add twice with arguments fake_total and fake_by_author respectively", function () {
+ //spyOn(ContributorsStatGraphUtil, 'add')
+ //ContributorsStatGraphUtil.store_commits(fake_total, fake_by_author)
+ //expect(ContributorsStatGraphUtil.add.argsForCall).toEqual([["fake_total", "commits", 1], ["fake_by_author", "commits", 1]])
+ //})
+ //})
describe("#add", function () {
it("adds 1 to current test_field in collection", function () {
@@ -79,27 +80,29 @@ describe("ContributorsStatGraphUtil", function () {
})
})
- describe("#store_additions", function () {
- var fake_entry = {additions: 10}
- var fake_total= "fake_total"
- var fake_by_author = "fake_by_author"
- it("calls #add twice with arguments fake_total and fake_by_author respectively", function () {
- spyOn(ContributorsStatGraphUtil, 'add')
- ContributorsStatGraphUtil.store_additions(fake_entry, fake_total, fake_by_author)
- expect(ContributorsStatGraphUtil.add.argsForCall).toEqual([["fake_total", "additions", 10], ["fake_by_author", "additions", 10]])
- })
- })
-
- describe("#store_deletions", function () {
- var fake_entry = {deletions: 10}
- var fake_total= "fake_total"
- var fake_by_author = "fake_by_author"
- it("calls #add twice with arguments fake_total and fake_by_author respectively", function () {
- spyOn(ContributorsStatGraphUtil, 'add')
- ContributorsStatGraphUtil.store_deletions(fake_entry, fake_total, fake_by_author)
- expect(ContributorsStatGraphUtil.add.argsForCall).toEqual([["fake_total", "deletions", 10], ["fake_by_author", "deletions", 10]])
- })
- })
+ // TODO: fix or remove
+ //describe("#store_additions", function () {
+ //var fake_entry = {additions: 10}
+ //var fake_total= "fake_total"
+ //var fake_by_author = "fake_by_author"
+ //it("calls #add twice with arguments fake_total and fake_by_author respectively", function () {
+ //spyOn(ContributorsStatGraphUtil, 'add')
+ //ContributorsStatGraphUtil.store_additions(fake_entry, fake_total, fake_by_author)
+ //expect(ContributorsStatGraphUtil.add.argsForCall).toEqual([["fake_total", "additions", 10], ["fake_by_author", "additions", 10]])
+ //})
+ //})
+
+ // TODO: fix or remove
+ //describe("#store_deletions", function () {
+ //var fake_entry = {deletions: 10}
+ //var fake_total= "fake_total"
+ //var fake_by_author = "fake_by_author"
+ //it("calls #add twice with arguments fake_total and fake_by_author respectively", function () {
+ //spyOn(ContributorsStatGraphUtil, 'add')
+ //ContributorsStatGraphUtil.store_deletions(fake_entry, fake_total, fake_by_author)
+ //expect(ContributorsStatGraphUtil.add.argsForCall).toEqual([["fake_total", "deletions", 10], ["fake_by_author", "deletions", 10]])
+ //})
+ //})
describe("#add_date", function () {
it("adds a date field to the collection", function () {
diff --git a/spec/javascripts/support/jasmine_helper.rb b/spec/javascripts/support/jasmine_helper.rb
index 34b418a9ca3..b4919802afe 100644
--- a/spec/javascripts/support/jasmine_helper.rb
+++ b/spec/javascripts/support/jasmine_helper.rb
@@ -1,5 +1,11 @@
-WebMock.allow_net_connect!
+#Use this file to set/override Jasmine configuration options
+#You can remove it if you don't need it.
+#This file is loaded *after* jasmine.yml is interpreted.
+#
+#Example: using a different boot file.
+#Jasmine.configure do |config|
+# config.boot_dir = '/absolute/path/to/boot_dir'
+# config.boot_files = lambda { ['/absolute/path/to/boot_dir/file.js'] }
+#end
+#
-Jasmine.configure do |config|
- config.browser = :phantomjs
-end
diff --git a/spec/lib/auth_spec.rb b/spec/lib/auth_spec.rb
index e05fde95731..073b811c3fb 100644
--- a/spec/lib/auth_spec.rb
+++ b/spec/lib/auth_spec.rb
@@ -8,21 +8,21 @@ describe Gitlab::Auth do
@user = create(
:user,
username: 'john',
- password: '888777',
- password_confirmation: '888777'
+ password: '88877711',
+ password_confirmation: '88877711'
)
end
it "should find user by valid login/password" do
- gl_auth.find('john', '888777').should == @user
+ gl_auth.find('john', '88877711').should == @user
end
it "should not find user with invalid password" do
- gl_auth.find('john', 'invalid').should_not == @user
+ gl_auth.find('john', 'invalid11').should_not == @user
end
it "should not find user with invalid login and password" do
- gl_auth.find('jon', 'invalid').should_not == @user
+ gl_auth.find('jon', 'invalid11').should_not == @user
end
end
end
diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb
index aac72c63ea5..7b3818ea5c8 100644
--- a/spec/lib/extracts_path_spec.rb
+++ b/spec/lib/extracts_path_spec.rb
@@ -7,7 +7,7 @@ describe ExtractsPath do
before do
@project = project
- project.stub(repository: stub(ref_names: ['master', 'foo/bar/baz', 'v1.0.0', 'v2.0.0']))
+ project.stub(repository: double(ref_names: ['master', 'foo/bar/baz', 'v1.0.0', 'v2.0.0']))
project.stub(path_with_namespace: 'gitlab/gitlab-ci')
end
diff --git a/spec/lib/gitlab/ldap/ldap_user_auth_spec.rb b/spec/lib/gitlab/ldap/ldap_user_auth_spec.rb
index b1c583c0476..b7d7bbaad2e 100644
--- a/spec/lib/gitlab/ldap/ldap_user_auth_spec.rb
+++ b/spec/lib/gitlab/ldap/ldap_user_auth_spec.rb
@@ -6,7 +6,7 @@ describe Gitlab::LDAP do
before do
Gitlab.config.stub(omniauth: {})
- @info = mock(
+ @info = double(
uid: '12djsak321',
name: 'John',
email: 'john@mail.com'
@@ -15,7 +15,7 @@ describe Gitlab::LDAP do
describe :find_for_ldap_auth do
before do
- @auth = mock(
+ @auth = double(
uid: '12djsak321',
info: @info,
provider: 'ldap'
diff --git a/spec/lib/gitlab/upgrader_spec.rb b/spec/lib/gitlab/upgrader_spec.rb
new file mode 100644
index 00000000000..2b254d6b3a6
--- /dev/null
+++ b/spec/lib/gitlab/upgrader_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+describe Gitlab::Upgrader do
+ let(:upgrader) { Gitlab::Upgrader.new }
+ let(:current_version) { Gitlab::VERSION }
+
+ describe 'current_version_raw' do
+ it { upgrader.current_version_raw.should == current_version }
+ end
+
+ describe 'latest_version?' do
+ it 'should be true if newest version' do
+ upgrader.stub(latest_version_raw: current_version)
+ upgrader.latest_version?.should be_true
+ end
+ end
+
+ describe 'latest_version_raw' do
+ it 'should be latest version for GitLab 5' do
+ upgrader.stub(current_version_raw: "5.3.0")
+ upgrader.latest_version_raw.should == "v5.4.2"
+ end
+ end
+end
diff --git a/spec/lib/oauth_spec.rb b/spec/lib/oauth_spec.rb
index e21074554b6..3dfe95a8e38 100644
--- a/spec/lib/oauth_spec.rb
+++ b/spec/lib/oauth_spec.rb
@@ -6,7 +6,7 @@ describe Gitlab::OAuth::User do
before do
Gitlab.config.stub(omniauth: {})
- @info = mock(
+ @info = double(
uid: '12djsak321',
name: 'John',
email: 'john@mail.com'
@@ -15,7 +15,7 @@ describe Gitlab::OAuth::User do
describe :create do
it "should create user from LDAP" do
- @auth = mock(info: @info, provider: 'ldap')
+ @auth = double(info: @info, provider: 'ldap')
user = gl_auth.create(@auth)
user.should be_valid
@@ -24,7 +24,7 @@ describe Gitlab::OAuth::User do
end
it "should create user from Omniauth" do
- @auth = mock(info: @info, provider: 'twitter')
+ @auth = double(info: @info, provider: 'twitter')
user = gl_auth.create(@auth)
user.should be_valid
@@ -33,7 +33,7 @@ describe Gitlab::OAuth::User do
end
it "should apply defaults to user" do
- @auth = mock(info: @info, provider: 'ldap')
+ @auth = double(info: @info, provider: 'ldap')
user = gl_auth.create(@auth)
user.should be_valid
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 666c6ccefff..b1e53486816 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -110,7 +110,7 @@ describe Notify do
it_behaves_like 'an assignee email'
it 'has the correct subject' do
- should have_subject /#{project.name} \| new issue ##{issue.iid} \| #{issue.title}/
+ should have_subject /#{project.name} \| New issue ##{issue.iid} \| #{issue.title}/
end
it 'contains a link to the new issue' do
@@ -126,7 +126,7 @@ describe Notify do
it_behaves_like 'a multiple recipients email'
it 'has the correct subject' do
- should have_subject /changed issue ##{issue.iid} \| #{issue.title}/
+ should have_subject /Changed issue ##{issue.iid} \| #{issue.title}/
end
it 'contains the name of the previous assignee' do
@@ -148,7 +148,7 @@ describe Notify do
subject { Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user) }
it 'has the correct subject' do
- should have_subject /changed issue ##{issue.iid} \| #{issue.title}/i
+ should have_subject /Changed issue ##{issue.iid} \| #{issue.title}/i
end
it 'contains the new status' do
@@ -175,7 +175,7 @@ describe Notify do
it_behaves_like 'an assignee email'
it 'has the correct subject' do
- should have_subject /new merge request !#{merge_request.iid}/
+ should have_subject /New merge request ##{merge_request.iid}/
end
it 'contains a link to the new merge request' do
@@ -199,7 +199,7 @@ describe Notify do
it_behaves_like 'a multiple recipients email'
it 'has the correct subject' do
- should have_subject /changed merge request !#{merge_request.iid}/
+ should have_subject /Changed merge request ##{merge_request.iid}/
end
it 'contains the name of the previous assignee' do
@@ -224,7 +224,7 @@ describe Notify do
subject { Notify.project_was_moved_email(project.id, user.id) }
it 'has the correct subject' do
- should have_subject /project was moved/
+ should have_subject /Project was moved/
end
it 'contains name of project' do
@@ -244,7 +244,7 @@ describe Notify do
user: user) }
subject { Notify.project_access_granted_email(users_project.id) }
it 'has the correct subject' do
- should have_subject /access to project was granted/
+ should have_subject /Access to project was granted/
end
it 'contains name of project' do
should have_body_text /#{project.name}/
@@ -302,7 +302,7 @@ describe Notify do
it_behaves_like 'a note email'
it 'has the correct subject' do
- should have_subject /note for commit #{commit.short_id}/
+ should have_subject /Note for commit #{commit.short_id}/
end
it 'contains a link to the commit' do
@@ -320,7 +320,7 @@ describe Notify do
it_behaves_like 'a note email'
it 'has the correct subject' do
- should have_subject /note for merge request ##{merge_request.iid}/
+ should have_subject /Note for merge request ##{merge_request.iid}/
end
it 'contains a link to the merge request note' do
@@ -338,7 +338,7 @@ describe Notify do
it_behaves_like 'a note email'
it 'has the correct subject' do
- should have_subject /note for issue ##{issue.iid}/
+ should have_subject /Note for issue ##{issue.iid}/
end
it 'contains a link to the issue note' do
@@ -356,7 +356,7 @@ describe Notify do
subject { Notify.group_access_granted_email(membership.id) }
it 'has the correct subject' do
- should have_subject /access to group was granted/
+ should have_subject /Access to group was granted/
end
it 'contains name of project' do
@@ -367,4 +367,52 @@ describe Notify do
should have_body_text /#{membership.human_access}/
end
end
+
+ describe 'confirmation if email changed' do
+ let(:example_site_path) { root_path }
+ let(:user) { create(:user, email: 'old-email@mail.com') }
+
+ before do
+ user.email = "new-email@mail.com"
+ user.save
+ end
+
+ subject { ActionMailer::Base.deliveries.last }
+
+ it 'is sent to the new user' do
+ should deliver_to 'new-email@mail.com'
+ end
+
+ it 'has the correct subject' do
+ should have_subject "Confirmation instructions"
+ end
+
+ it 'includes a link to the site' do
+ should have_body_text /#{example_site_path}/
+ end
+ end
+
+ describe 'email on push' do
+ let(:example_site_path) { root_path }
+ let(:user) { create(:user) }
+ let(:compare) { Gitlab::Git::Compare.new(project.repository.raw_repository, 'cd5c4bac', 'b1e6a9db') }
+
+ subject { Notify.repository_push_email(project.id, 'devs@company.name', user.id, 'master', compare) }
+
+ it 'is sent to recipient' do
+ should deliver_to 'devs@company.name'
+ end
+
+ it 'has the correct subject' do
+ should have_subject /New push to repository/
+ end
+
+ it 'includes commits list' do
+ should have_body_text /tree css fixes/
+ end
+
+ it 'includes diffs' do
+ should have_body_text /Checkout wiki pages for installation information/
+ end
+ end
end
diff --git a/spec/models/assembla_service_spec.rb b/spec/models/assembla_service_spec.rb
new file mode 100644
index 00000000000..0b961c81ac1
--- /dev/null
+++ b/spec/models/assembla_service_spec.rb
@@ -0,0 +1,50 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# token :string(255)
+# project_id :integer not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# active :boolean default(FALSE), not null
+# project_url :string(255)
+# subdomain :string(255)
+# room :string(255)
+#
+
+require 'spec_helper'
+
+describe AssemblaService do
+ describe "Associations" do
+ it { should belong_to :project }
+ it { should have_one :service_hook }
+ end
+
+ describe "Execute" do
+ let(:user) { create(:user) }
+ let(:project) { create(:project_with_code) }
+
+ before do
+ @assembla_service = AssemblaService.new
+ @assembla_service.stub(
+ project_id: project.id,
+ project: project,
+ service_hook: true,
+ token: 'verySecret'
+ )
+ @sample_data = GitPushService.new.sample_data(project, user)
+ @api_url = 'https://atlas.assembla.com/spaces/ouposp/github_tool?secret_key=verySecret'
+ WebMock.stub_request(:post, @api_url)
+ end
+
+ it "should call FlowDock API" do
+ @assembla_service.execute(@sample_data)
+ WebMock.should have_requested(:post, @api_url).with(
+ body: /#{@sample_data[:before]}.*#{@sample_data[:after]}.*#{project.path}/
+ ).once
+ end
+ end
+end
diff --git a/spec/models/broadcast_message_spec.rb b/spec/models/broadcast_message_spec.rb
new file mode 100644
index 00000000000..cf0b36a2830
--- /dev/null
+++ b/spec/models/broadcast_message_spec.rb
@@ -0,0 +1,39 @@
+# == Schema Information
+#
+# Table name: broadcast_messages
+#
+# id :integer not null, primary key
+# message :text default(""), not null
+# starts_at :datetime
+# ends_at :datetime
+# alert_type :integer
+# created_at :datetime not null
+# updated_at :datetime not null
+# color :string(255)
+# font :string(255)
+#
+
+require 'spec_helper'
+
+describe BroadcastMessage do
+ subject { create(:broadcast_message) }
+
+ it { should be_valid }
+
+ describe :current do
+ it "should return last message if time match" do
+ broadcast_message = create(:broadcast_message, starts_at: Time.now.yesterday, ends_at: Time.now.tomorrow)
+ BroadcastMessage.current.should == broadcast_message
+ end
+
+ it "should return nil if time not come" do
+ broadcast_message = create(:broadcast_message, starts_at: Time.now.tomorrow, ends_at: Time.now + 2.days)
+ BroadcastMessage.current.should be_nil
+ end
+
+ it "should return nil if time has passed" do
+ broadcast_message = create(:broadcast_message, starts_at: Time.now - 2.days, ends_at: Time.now.yesterday)
+ BroadcastMessage.current.should be_nil
+ end
+ end
+end
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index 852146ebaec..0827e4f162b 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -34,7 +34,7 @@ describe Issue, "Issuable" do
let!(:searchable_issue) { create(:issue, title: "Searchable issue") }
it "matches by title" do
- described_class.search('able').all.should == [searchable_issue]
+ described_class.search('able').should == [searchable_issue]
end
end
diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb
index 85bdf08ae64..53ede0d5ee9 100644
--- a/spec/models/event_spec.rb
+++ b/spec/models/event_spec.rb
@@ -67,12 +67,12 @@ describe Event do
end
describe 'Team events' do
- let(:user_project) { stub.as_null_object }
+ let(:user_project) { double.as_null_object }
let(:observer) { UsersProjectObserver.instance }
before {
Event.should_receive :create
- observer.stub(notification: stub.as_null_object)
+ observer.stub(notification: double.as_null_object)
}
describe "Joined project team" do
diff --git a/spec/models/flowdock_service_spec.rb b/spec/models/flowdock_service_spec.rb
index b22193c9e93..636aba2f012 100644
--- a/spec/models/flowdock_service_spec.rb
+++ b/spec/models/flowdock_service_spec.rb
@@ -11,6 +11,8 @@
# updated_at :datetime not null
# active :boolean default(FALSE), not null
# project_url :string(255)
+# subdomain :string(255)
+# room :string(255)
#
require 'spec_helper'
diff --git a/spec/models/forked_project_link_spec.rb b/spec/models/forked_project_link_spec.rb
index 44b8c6155be..472ddf1b59d 100644
--- a/spec/models/forked_project_link_spec.rb
+++ b/spec/models/forked_project_link_spec.rb
@@ -59,7 +59,7 @@ end
def fork_project(from_project, user)
context = Projects::ForkContext.new(from_project, user)
- shell = mock("gitlab_shell")
+ shell = double("gitlab_shell")
shell.stub(fork_repository: true)
context.stub(gitlab_shell: shell)
context.execute
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index b17183a281c..039a0c087ba 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -107,22 +107,22 @@ describe MergeRequest do
describe 'detection of issues to be closed' do
let(:issue0) { create :issue, project: subject.project }
let(:issue1) { create :issue, project: subject.project }
- let(:commit0) { mock('commit0', closes_issues: [issue0]) }
- let(:commit1) { mock('commit1', closes_issues: [issue0]) }
- let(:commit2) { mock('commit2', closes_issues: [issue1]) }
+ let(:commit0) { double('commit0', closes_issues: [issue0]) }
+ let(:commit1) { double('commit1', closes_issues: [issue0]) }
+ let(:commit2) { double('commit2', closes_issues: [issue1]) }
before do
- subject.stub(unmerged_commits: [commit0, commit1, commit2])
+ subject.stub(commits: [commit0, commit1, commit2])
end
it 'accesses the set of issues that will be closed on acceptance' do
- subject.project.default_branch = subject.target_branch
+ subject.project.stub(default_branch: subject.target_branch)
subject.closes_issues.should == [issue0, issue1].sort_by(&:id)
end
it 'only lists issues as to be closed if it targets the default branch' do
- subject.project.default_branch = 'master'
+ subject.project.stub(default_branch: 'master')
subject.target_branch = 'something-else'
subject.closes_issues.should be_empty
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index 42c405d8e50..55b264ce8cf 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -61,6 +61,11 @@ describe Note do
note.should be_upvote
end
+ it "recognizes a thumbsup emoji as a vote" do
+ note = build(:votable_note, note: ":thumbsup: for this")
+ note.should be_upvote
+ end
+
it "recognizes a -1 note" do
note = create(:votable_note, note: "-1 for this")
note.should be_downvote
@@ -70,6 +75,11 @@ describe Note do
note = build(:votable_note, note: ":-1: for this")
note.should be_downvote
end
+
+ it "recognizes a thumbsdown emoji as a vote" do
+ note = build(:votable_note, note: ":thumbsdown: for this")
+ note.should be_downvote
+ end
end
let(:project) { create(:project) }
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 88ea6926790..8aa4c7fed1a 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -9,19 +9,19 @@
# created_at :datetime not null
# updated_at :datetime not null
# creator_id :integer
-# default_branch :string(255)
# issues_enabled :boolean default(TRUE), not null
# wall_enabled :boolean default(TRUE), not null
# merge_requests_enabled :boolean default(TRUE), not null
# wiki_enabled :boolean default(TRUE), not null
# namespace_id :integer
-# public :boolean default(FALSE), not null
# issues_tracker :string(255) default("gitlab"), not null
# issues_tracker_id :string(255)
# snippets_enabled :boolean default(TRUE), not null
# last_activity_at :datetime
# imported :boolean default(FALSE), not null
# import_url :string(255)
+# visibility_level :integer default(0), not null
+# archived :boolean default(FALSE), not null
#
require 'spec_helper'
@@ -71,7 +71,7 @@ describe Project do
it "should not allow new projects beyond user limits" do
project2 = build(:project)
- project2.stub(:creator).and_return(double(can_create_project?: false, projects_limit: 0))
+ project2.stub(:creator).and_return(double(can_create_project?: false, projects_limit: 0).as_null_object)
project2.should_not be_valid
project2.errors[:limit_reached].first.should match(/Your own projects limit is 0/)
end
diff --git a/spec/models/service_hook_spec.rb b/spec/models/service_hook_spec.rb
index 0b0262c97f1..40a5fbc71d9 100644
--- a/spec/models/service_hook_spec.rb
+++ b/spec/models/service_hook_spec.rb
@@ -2,13 +2,16 @@
#
# Table name: web_hooks
#
-# id :integer not null, primary key
-# url :string(255)
-# project_id :integer
-# created_at :datetime not null
-# updated_at :datetime not null
-# type :string(255) default("ProjectHook")
-# service_id :integer
+# id :integer not null, primary key
+# url :string(255)
+# project_id :integer
+# created_at :datetime not null
+# updated_at :datetime not null
+# type :string(255) default("ProjectHook")
+# service_id :integer
+# push_events :boolean default(TRUE), not null
+# issues_events :boolean default(FALSE), not null
+# merge_requests_events :boolean default(FALSE), not null
#
require "spec_helper"
diff --git a/spec/models/system_hook_spec.rb b/spec/models/system_hook_spec.rb
index a9ed6a5fa7c..6a0d99dcc53 100644
--- a/spec/models/system_hook_spec.rb
+++ b/spec/models/system_hook_spec.rb
@@ -2,13 +2,16 @@
#
# Table name: web_hooks
#
-# id :integer not null, primary key
-# url :string(255)
-# project_id :integer
-# created_at :datetime not null
-# updated_at :datetime not null
-# type :string(255) default("ProjectHook")
-# service_id :integer
+# id :integer not null, primary key
+# url :string(255)
+# project_id :integer
+# created_at :datetime not null
+# updated_at :datetime not null
+# type :string(255) default("ProjectHook")
+# service_id :integer
+# push_events :boolean default(TRUE), not null
+# issues_events :boolean default(FALSE), not null
+# merge_requests_events :boolean default(FALSE), not null
#
require "spec_helper"
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index f6c9f82c4ee..94bd19f5900 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -36,6 +36,11 @@
# notification_level :integer default(1), not null
# password_expires_at :datetime
# created_by_id :integer
+# avatar :string(255)
+# confirmation_token :string(255)
+# confirmed_at :datetime
+# confirmation_sent_at :datetime
+# unconfirmed_email :string(255)
#
require 'spec_helper'
@@ -85,8 +90,8 @@ describe User do
end
it "should not generate password by default" do
- user = create(:user, password: 'abcdefg')
- user.password.should == 'abcdefg'
+ user = create(:user, password: 'abcdefghe')
+ user.password.should == 'abcdefghe'
end
it "should generate password when forcing random password" do
@@ -135,7 +140,6 @@ describe User do
end
it { @user.several_namespaces?.should be_true }
- it { @user.namespaces.should include(@user.namespace) }
it { @user.authorized_groups.should == [@group] }
it { @user.owned_groups.should == [@group] }
end
@@ -162,7 +166,6 @@ describe User do
end
it { @user.several_namespaces?.should be_false }
- it { @user.namespaces.should == [@user.namespace] }
end
describe 'blocking user' do
@@ -276,4 +279,18 @@ describe User do
User.by_username_or_id('bar').should be_nil
end
end
+
+ describe :avatar_type do
+ let(:user) { create(:user) }
+
+ it "should be true if avatar is image" do
+ user.update_attribute(:avatar, 'uploads/avatar.png')
+ user.avatar_type.should be_true
+ end
+
+ it "should be false if avatar is html page" do
+ user.update_attribute(:avatar, 'uploads/avatar.html')
+ user.avatar_type.should == ["only images allowed"]
+ end
+ end
end
diff --git a/spec/models/web_hook_spec.rb b/spec/models/web_hook_spec.rb
index 2d9301732dd..d6034081018 100644
--- a/spec/models/web_hook_spec.rb
+++ b/spec/models/web_hook_spec.rb
@@ -2,13 +2,16 @@
#
# Table name: web_hooks
#
-# id :integer not null, primary key
-# url :string(255)
-# project_id :integer
-# created_at :datetime not null
-# updated_at :datetime not null
-# type :string(255) default("ProjectHook")
-# service_id :integer
+# id :integer not null, primary key
+# url :string(255)
+# project_id :integer
+# created_at :datetime not null
+# updated_at :datetime not null
+# type :string(255) default("ProjectHook")
+# service_id :integer
+# push_events :boolean default(TRUE), not null
+# issues_events :boolean default(FALSE), not null
+# merge_requests_events :boolean default(FALSE), not null
#
require 'spec_helper'
diff --git a/spec/observers/issue_observer_spec.rb b/spec/observers/issue_observer_spec.rb
index 4155ff31193..9a0a2c4329c 100644
--- a/spec/observers/issue_observer_spec.rb
+++ b/spec/observers/issue_observer_spec.rb
@@ -9,7 +9,7 @@ describe IssueObserver do
before { subject.stub(:current_user).and_return(some_user) }
before { subject.stub(:current_commit).and_return(nil) }
- before { subject.stub(notification: mock('NotificationService').as_null_object) }
+ before { subject.stub(notification: double('NotificationService').as_null_object) }
before { mock_issue.project.stub_chain(:repository, :commit).and_return(nil) }
subject { IssueObserver.instance }
diff --git a/spec/observers/merge_request_observer_spec.rb b/spec/observers/merge_request_observer_spec.rb
index 3f5250a0040..a450b4d518e 100644
--- a/spec/observers/merge_request_observer_spec.rb
+++ b/spec/observers/merge_request_observer_spec.rb
@@ -4,14 +4,14 @@ describe MergeRequestObserver do
let(:some_user) { create :user }
let(:assignee) { create :user }
let(:author) { create :user }
- let(:mr_mock) { double(:merge_request, id: 42, assignee: assignee, author: author) }
+ let(:mr_mock) { double(:merge_request, id: 42, assignee: assignee, author: author).as_null_object }
let(:assigned_mr) { create(:merge_request, assignee: assignee, author: author, target_project: create(:project)) }
let(:unassigned_mr) { create(:merge_request, author: author, target_project: create(:project)) }
let(:closed_assigned_mr) { create(:closed_merge_request, assignee: assignee, author: author, target_project: create(:project)) }
let(:closed_unassigned_mr) { create(:closed_merge_request, author: author, target_project: create(:project)) }
before { subject.stub(:current_user).and_return(some_user) }
- before { subject.stub(notification: mock('NotificationService').as_null_object) }
+ before { subject.stub(notification: double('NotificationService').as_null_object) }
before { mr_mock.stub(:author_id) }
before { mr_mock.stub(:target_project) }
before { mr_mock.stub(:source_project) }
diff --git a/spec/observers/note_observer_spec.rb b/spec/observers/note_observer_spec.rb
index f9b96c255c1..f8693355b23 100644
--- a/spec/observers/note_observer_spec.rb
+++ b/spec/observers/note_observer_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe NoteObserver do
subject { NoteObserver.instance }
- before { subject.stub(notification: mock('NotificationService').as_null_object) }
+ before { subject.stub(notification: double('NotificationService').as_null_object) }
let(:team_without_author) { (1..2).map { |n| double :user, id: n } }
let(:note) { double(:note).as_null_object }
diff --git a/spec/observers/user_observer_spec.rb b/spec/observers/user_observer_spec.rb
index b74fceb98b1..9aeade535f9 100644
--- a/spec/observers/user_observer_spec.rb
+++ b/spec/observers/user_observer_spec.rb
@@ -4,7 +4,7 @@ describe UserObserver do
before(:each) { enable_observers }
after(:each) {disable_observers}
subject { UserObserver.instance }
- before { subject.stub(notification: mock('NotificationService').as_null_object) }
+ before { subject.stub(notification: double('NotificationService').as_null_object) }
it 'calls #after_create when new users are created' do
new_user = build(:user)
diff --git a/spec/observers/users_group_observer_spec.rb b/spec/observers/users_group_observer_spec.rb
index 3bf562edbb7..2ab99c33b78 100644
--- a/spec/observers/users_group_observer_spec.rb
+++ b/spec/observers/users_group_observer_spec.rb
@@ -5,7 +5,7 @@ describe UsersGroupObserver do
after(:each) { disable_observers }
subject { UsersGroupObserver.instance }
- before { subject.stub(notification: mock('NotificationService').as_null_object) }
+ before { subject.stub(notification: double('NotificationService').as_null_object) }
describe "#after_create" do
it "should send email to user" do
@@ -23,5 +23,10 @@ describe UsersGroupObserver do
subject.should_receive(:notification)
@membership.update_attribute(:group_access, UsersGroup::MASTER)
end
+
+ it "does not send an email when the access level has not changed" do
+ subject.should_not_receive(:notification)
+ @membership.update_attribute(:group_access, UsersGroup::OWNER)
+ end
end
end
diff --git a/spec/observers/users_project_observer_spec.rb b/spec/observers/users_project_observer_spec.rb
index e7c624fce59..dea90b2bfa7 100644
--- a/spec/observers/users_project_observer_spec.rb
+++ b/spec/observers/users_project_observer_spec.rb
@@ -7,27 +7,7 @@ describe UsersProjectObserver do
let(:user) { create(:user) }
let(:project) { create(:project) }
subject { UsersProjectObserver.instance }
- before { subject.stub(notification: mock('NotificationService').as_null_object) }
-
- describe "#after_commit" do
- it "should called when UsersProject created" do
- subject.should_receive(:after_commit)
- create(:users_project)
- end
-
- it "should send email to user" do
- subject.should_receive(:notification)
- Event.stub(create: true)
-
- create(:users_project)
- end
-
- it "should create new event" do
- Event.should_receive(:create)
-
- create(:users_project)
- end
- end
+ before { subject.stub(notification: double('NotificationService').as_null_object) }
describe "#after_update" do
before do
@@ -35,7 +15,7 @@ describe UsersProjectObserver do
end
it "should called when UsersProject updated" do
- subject.should_receive(:after_commit)
+ subject.should_receive(:after_update)
@users_project.update_attribute(:project_access, UsersProject::MASTER)
end
@@ -45,7 +25,7 @@ describe UsersProjectObserver do
end
it "should not called after UsersProject destroyed" do
- subject.should_not_receive(:after_commit)
+ subject.should_not_receive(:after_update)
@users_project.destroy
end
end
@@ -90,5 +70,18 @@ describe UsersProjectObserver do
it { File.exists?(@path).should be_false }
end
end
+
+ it "should send email to user" do
+ subject.should_receive(:notification)
+ Event.stub(create: true)
+
+ create(:users_project)
+ end
+
+ it "should create new event" do
+ Event.should_receive(:create)
+
+ create(:users_project)
+ end
end
-end \ No newline at end of file
+end
diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb
new file mode 100644
index 00000000000..2d1f8df47dd
--- /dev/null
+++ b/spec/requests/api/files_spec.rb
@@ -0,0 +1,115 @@
+require 'spec_helper'
+
+describe API::API do
+ include ApiHelpers
+ before(:each) { ActiveRecord::Base.observers.enable(:user_observer) }
+ after(:each) { ActiveRecord::Base.observers.disable(:user_observer) }
+
+ let(:user) { create(:user) }
+ let!(:project) { create(:project_with_code, namespace: user.namespace ) }
+ before { project.team << [user, :developer] }
+
+ describe "POST /projects/:id/repository/files" do
+ let(:valid_params) {
+ {
+ file_path: 'newfile.rb',
+ branch_name: 'master',
+ content: 'puts 8',
+ commit_message: 'Added newfile'
+ }
+ }
+
+ it "should create a new file in project repo" do
+ Gitlab::Satellite::NewFileAction.any_instance.stub(
+ commit!: true,
+ )
+
+ post api("/projects/#{project.id}/repository/files", user), valid_params
+ response.status.should == 201
+ json_response['file_path'].should == 'newfile.rb'
+ end
+
+ it "should return a 400 bad request if no params given" do
+ post api("/projects/#{project.id}/repository/files", user)
+ response.status.should == 400
+ end
+
+ it "should return a 400 if satellite fails to create file" do
+ Gitlab::Satellite::NewFileAction.any_instance.stub(
+ commit!: false,
+ )
+
+ post api("/projects/#{project.id}/repository/files", user), valid_params
+ response.status.should == 400
+ end
+ end
+
+ describe "PUT /projects/:id/repository/files" do
+ let(:valid_params) {
+ {
+ file_path: 'spec/spec_helper.rb',
+ branch_name: 'master',
+ content: 'puts 8',
+ commit_message: 'Changed file'
+ }
+ }
+
+ it "should update existing file in project repo" do
+ Gitlab::Satellite::EditFileAction.any_instance.stub(
+ commit!: true,
+ )
+
+ put api("/projects/#{project.id}/repository/files", user), valid_params
+ response.status.should == 200
+ json_response['file_path'].should == 'spec/spec_helper.rb'
+ end
+
+ it "should return a 400 bad request if no params given" do
+ put api("/projects/#{project.id}/repository/files", user)
+ response.status.should == 400
+ end
+
+ it "should return a 400 if satellite fails to create file" do
+ Gitlab::Satellite::EditFileAction.any_instance.stub(
+ commit!: false,
+ )
+
+ put api("/projects/#{project.id}/repository/files", user), valid_params
+ response.status.should == 400
+ end
+ end
+
+ describe "DELETE /projects/:id/repository/files" do
+ let(:valid_params) {
+ {
+ file_path: 'spec/spec_helper.rb',
+ branch_name: 'master',
+ commit_message: 'Changed file'
+ }
+ }
+
+ it "should delete existing file in project repo" do
+ Gitlab::Satellite::DeleteFileAction.any_instance.stub(
+ commit!: true,
+ )
+
+ delete api("/projects/#{project.id}/repository/files", user), valid_params
+ response.status.should == 200
+ json_response['file_path'].should == 'spec/spec_helper.rb'
+ end
+
+ it "should return a 400 bad request if no params given" do
+ delete api("/projects/#{project.id}/repository/files", user)
+ response.status.should == 400
+ end
+
+ it "should return a 400 if satellite fails to create file" do
+ Gitlab::Satellite::DeleteFileAction.any_instance.stub(
+ commit!: false,
+ )
+
+ delete api("/projects/#{project.id}/repository/files", user), valid_params
+ response.status.should == 400
+ end
+ end
+end
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index 25b9a10bd8c..1a5f11038b7 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -147,7 +147,7 @@ describe API::API do
describe "POST /groups/:id/projects/:project_id" do
let(:project) { create(:project) }
before(:each) do
- project.stub!(:transfer).and_return(true)
+ project.stub(:transfer).and_return(true)
Project.stub(:find).and_return(project)
end
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index e8870f4d5d8..5f6dff92c0a 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -103,6 +103,33 @@ describe API::API do
end
end
+ context "archived project" do
+ let(:personal_project) { create(:project, namespace: user.namespace) }
+
+ before do
+ project.team << [user, :developer]
+ project.archive!
+ end
+
+ context "git pull" do
+ it do
+ pull(key, project)
+
+ response.status.should == 200
+ response.body.should == 'true'
+ end
+ end
+
+ context "git push" do
+ it do
+ push(key, project)
+
+ response.status.should == 200
+ response.body.should == 'false'
+ end
+ end
+ end
+
context "deploy key" do
let(:key) { create(:deploy_key) }
diff --git a/spec/requests/api/namespaces_spec.rb b/spec/requests/api/namespaces_spec.rb
new file mode 100644
index 00000000000..2b1a4bf6ec8
--- /dev/null
+++ b/spec/requests/api/namespaces_spec.rb
@@ -0,0 +1,31 @@
+require 'spec_helper'
+
+describe API::API do
+ include ApiHelpers
+ before(:each) { ActiveRecord::Base.observers.enable(:user_observer) }
+ after(:each) { ActiveRecord::Base.observers.disable(:user_observer) }
+
+ let(:admin) { create(:admin) }
+ let!(:group1) { create(:group) }
+ let!(:group2) { create(:group) }
+
+ describe "GET /namespaces" do
+ context "when unauthenticated" do
+ it "should return authentication error" do
+ get api("/namespaces")
+ response.status.should == 401
+ end
+ end
+
+ context "when authenticated as admin" do
+ it "admin: should return an array of all namespaces" do
+ get api("/namespaces", admin)
+ response.status.should == 200
+ json_response.should be_an Array
+
+ # Admin namespace + 2 group namespaces
+ json_response.length.should == 3
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/project_hooks_spec.rb b/spec/requests/api/project_hooks_spec.rb
new file mode 100644
index 00000000000..beccd61866e
--- /dev/null
+++ b/spec/requests/api/project_hooks_spec.rb
@@ -0,0 +1,132 @@
+require 'spec_helper'
+
+describe API::API, 'ProjectHooks' do
+ include ApiHelpers
+ before(:each) { enable_observers }
+ after(:each) { disable_observers }
+
+ let(:user) { create(:user) }
+ let(:user3) { create(:user) }
+ let!(:project) { create(:project_with_code, creator_id: user.id, namespace: user.namespace) }
+ let!(:hook) { create(:project_hook, project: project, url: "http://example.com") }
+
+ before do
+ project.team << [user, :master]
+ project.team << [user3, :developer]
+ end
+
+ describe "GET /projects/:id/hooks" do
+ context "authorized user" do
+ it "should return project hooks" do
+ get api("/projects/#{project.id}/hooks", user)
+ response.status.should == 200
+
+ json_response.should be_an Array
+ json_response.count.should == 1
+ json_response.first['url'].should == "http://example.com"
+ end
+ end
+
+ context "unauthorized user" do
+ it "should not access project hooks" do
+ get api("/projects/#{project.id}/hooks", user3)
+ response.status.should == 403
+ end
+ end
+ end
+
+ describe "GET /projects/:id/hooks/:hook_id" do
+ context "authorized user" do
+ it "should return a project hook" do
+ get api("/projects/#{project.id}/hooks/#{hook.id}", user)
+ response.status.should == 200
+ json_response['url'].should == hook.url
+ end
+
+ it "should return a 404 error if hook id is not available" do
+ get api("/projects/#{project.id}/hooks/1234", user)
+ response.status.should == 404
+ end
+ end
+
+ context "unauthorized user" do
+ it "should not access an existing hook" do
+ get api("/projects/#{project.id}/hooks/#{hook.id}", user3)
+ response.status.should == 403
+ end
+ end
+
+ it "should return a 404 error if hook id is not available" do
+ get api("/projects/#{project.id}/hooks/1234", user)
+ response.status.should == 404
+ end
+ end
+
+ describe "POST /projects/:id/hooks" do
+ it "should add hook to project" do
+ expect {
+ post api("/projects/#{project.id}/hooks", user),
+ url: "http://example.com", issues_events: true
+ }.to change {project.hooks.count}.by(1)
+ response.status.should == 201
+ end
+
+ it "should return a 400 error if url not given" do
+ post api("/projects/#{project.id}/hooks", user)
+ response.status.should == 400
+ end
+
+ it "should return a 422 error if url not valid" do
+ post api("/projects/#{project.id}/hooks", user), "url" => "ftp://example.com"
+ response.status.should == 422
+ end
+ end
+
+ describe "PUT /projects/:id/hooks/:hook_id" do
+ it "should update an existing project hook" do
+ put api("/projects/#{project.id}/hooks/#{hook.id}", user),
+ url: 'http://example.org', push_events: false
+ response.status.should == 200
+ json_response['url'].should == 'http://example.org'
+ end
+
+ it "should return 404 error if hook id not found" do
+ put api("/projects/#{project.id}/hooks/1234", user), url: 'http://example.org'
+ response.status.should == 404
+ end
+
+ it "should return 400 error if url is not given" do
+ put api("/projects/#{project.id}/hooks/#{hook.id}", user)
+ response.status.should == 400
+ end
+
+ it "should return a 422 error if url is not valid" do
+ put api("/projects/#{project.id}/hooks/#{hook.id}", user), url: 'ftp://example.com'
+ response.status.should == 422
+ end
+ end
+
+ describe "DELETE /projects/:id/hooks/:hook_id" do
+ it "should delete hook from project" do
+ expect {
+ delete api("/projects/#{project.id}/hooks/#{hook.id}", user)
+ }.to change {project.hooks.count}.by(-1)
+ response.status.should == 200
+ end
+
+ it "should return success when deleting hook" do
+ delete api("/projects/#{project.id}/hooks/#{hook.id}", user)
+ response.status.should == 200
+ end
+
+ it "should return success when deleting non existent hook" do
+ delete api("/projects/#{project.id}/hooks/42", user)
+ response.status.should == 200
+ end
+
+ it "should return a 405 error if hook id not given" do
+ delete api("/projects/#{project.id}/hooks", user)
+ response.status.should == 405
+ end
+ end
+end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index bf4a1749418..8e0b9067672 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -10,7 +10,6 @@ describe API::API do
let(:user3) { create(:user) }
let(:admin) { create(:admin) }
let!(:project) { create(:project_with_code, creator_id: user.id, namespace: user.namespace) }
- let!(:hook) { create(:project_hook, project: project, url: "http://example.com") }
let!(:snippet) { create(:project_snippet, author: user, project: project, title: 'example') }
let!(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) }
let!(:users_project2) { create(:users_project, user: user3, project: project, project_access: UsersProject::DEVELOPER) }
@@ -36,6 +35,32 @@ describe API::API do
end
end
+ describe "GET /projects/all" do
+ context "when unauthenticated" do
+ it "should return authentication error" do
+ get api("/projects/all")
+ response.status.should == 401
+ end
+ end
+
+ context "when authenticated as regular user" do
+ it "should return authentication error" do
+ get api("/projects/all", user)
+ response.status.should == 403
+ end
+ end
+
+ context "when authenticated as admin" do
+ it "should return an array of all projects" do
+ get api("/projects/all", admin)
+ response.status.should == 200
+ json_response.should be_an Array
+ json_response.first['name'].should == project.name
+ json_response.first['owner']['email'].should == user.email
+ end
+ end
+ end
+
describe "POST /projects" do
context "maximum number of projects reached" do
before do
@@ -91,7 +116,6 @@ describe API::API do
it "should assign attributes to project" do
project = attributes_for(:project, {
description: Faker::Lorem.sentence,
- default_branch: 'stable',
issues_enabled: false,
wall_enabled: false,
merge_requests_enabled: false,
@@ -107,19 +131,46 @@ describe API::API do
end
it "should set a project as public" do
+ project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::PUBLIC })
+ post api("/projects", user), project
+ json_response['public'].should be_true
+ json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC
+ end
+
+ it "should set a project as public using :public" do
project = attributes_for(:project, { public: true })
post api("/projects", user), project
json_response['public'].should be_true
+ json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC
+ end
+ it "should set a project as internal" do
+ project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::INTERNAL })
+ post api("/projects", user), project
+ json_response['public'].should be_false
+ json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
end
- it "should set a project as private" do
- project = attributes_for(:project, { public: false })
+ it "should set a project as internal overriding :public" do
+ project = attributes_for(:project, { public: true, visibility_level: Gitlab::VisibilityLevel::INTERNAL })
post api("/projects", user), project
json_response['public'].should be_false
+ json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
+ end
+ it "should set a project as private" do
+ project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::PRIVATE })
+ post api("/projects", user), project
+ json_response['public'].should be_false
+ json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE
end
+ it "should set a project as private using :public" do
+ project = attributes_for(:project, { public: false })
+ post api("/projects", user), project
+ json_response['public'].should be_false
+ json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE
+ end
end
describe "POST /projects/user/:id" do
@@ -146,7 +197,6 @@ describe API::API do
it "should assign attributes to project" do
project = attributes_for(:project, {
description: Faker::Lorem.sentence,
- default_branch: 'stable',
issues_enabled: false,
wall_enabled: false,
merge_requests_enabled: false,
@@ -162,19 +212,46 @@ describe API::API do
end
it "should set a project as public" do
+ project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::PUBLIC })
+ post api("/projects/user/#{user.id}", admin), project
+ json_response['public'].should be_true
+ json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC
+ end
+
+ it "should set a project as public using :public" do
project = attributes_for(:project, { public: true })
post api("/projects/user/#{user.id}", admin), project
json_response['public'].should be_true
+ json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC
+ end
+ it "should set a project as internal" do
+ project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::INTERNAL })
+ post api("/projects/user/#{user.id}", admin), project
+ json_response['public'].should be_false
+ json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
end
- it "should set a project as private" do
- project = attributes_for(:project, { public: false })
+ it "should set a project as internal overriding :public" do
+ project = attributes_for(:project, { public: true, visibility_level: Gitlab::VisibilityLevel::INTERNAL })
post api("/projects/user/#{user.id}", admin), project
json_response['public'].should be_false
+ json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL
+ end
+ it "should set a project as private" do
+ project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::PRIVATE })
+ post api("/projects/user/#{user.id}", admin), project
+ json_response['public'].should be_false
+ json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE
end
+ it "should set a project as private using :public" do
+ project = attributes_for(:project, { public: false })
+ post api("/projects/user/#{user.id}", admin), project
+ json_response['public'].should be_false
+ json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE
+ end
end
describe "GET /projects/:id" do
@@ -361,121 +438,6 @@ describe API::API do
end
end
- describe "GET /projects/:id/hooks" do
- context "authorized user" do
- it "should return project hooks" do
- get api("/projects/#{project.id}/hooks", user)
- response.status.should == 200
-
- json_response.should be_an Array
- json_response.count.should == 1
- json_response.first['url'].should == "http://example.com"
- end
- end
-
- context "unauthorized user" do
- it "should not access project hooks" do
- get api("/projects/#{project.id}/hooks", user3)
- response.status.should == 403
- end
- end
- end
-
- describe "GET /projects/:id/hooks/:hook_id" do
- context "authorized user" do
- it "should return a project hook" do
- get api("/projects/#{project.id}/hooks/#{hook.id}", user)
- response.status.should == 200
- json_response['url'].should == hook.url
- end
-
- it "should return a 404 error if hook id is not available" do
- get api("/projects/#{project.id}/hooks/1234", user)
- response.status.should == 404
- end
- end
-
- context "unauthorized user" do
- it "should not access an existing hook" do
- get api("/projects/#{project.id}/hooks/#{hook.id}", user3)
- response.status.should == 403
- end
- end
-
- it "should return a 404 error if hook id is not available" do
- get api("/projects/#{project.id}/hooks/1234", user)
- response.status.should == 404
- end
- end
-
- describe "POST /projects/:id/hooks" do
- it "should add hook to project" do
- expect {
- post api("/projects/#{project.id}/hooks", user),
- url: "http://example.com"
- }.to change {project.hooks.count}.by(1)
- response.status.should == 201
- end
-
- it "should return a 400 error if url not given" do
- post api("/projects/#{project.id}/hooks", user)
- response.status.should == 400
- end
-
- it "should return a 422 error if url not valid" do
- post api("/projects/#{project.id}/hooks", user), "url" => "ftp://example.com"
- response.status.should == 422
- end
- end
-
- describe "PUT /projects/:id/hooks/:hook_id" do
- it "should update an existing project hook" do
- put api("/projects/#{project.id}/hooks/#{hook.id}", user),
- url: 'http://example.org'
- response.status.should == 200
- json_response['url'].should == 'http://example.org'
- end
-
- it "should return 404 error if hook id not found" do
- put api("/projects/#{project.id}/hooks/1234", user), url: 'http://example.org'
- response.status.should == 404
- end
-
- it "should return 400 error if url is not given" do
- put api("/projects/#{project.id}/hooks/#{hook.id}", user)
- response.status.should == 400
- end
-
- it "should return a 422 error if url is not valid" do
- put api("/projects/#{project.id}/hooks/#{hook.id}", user), url: 'ftp://example.com'
- response.status.should == 422
- end
- end
-
- describe "DELETE /projects/:id/hooks/:hook_id" do
- it "should delete hook from project" do
- expect {
- delete api("/projects/#{project.id}/hooks/#{hook.id}", user)
- }.to change {project.hooks.count}.by(-1)
- response.status.should == 200
- end
-
- it "should return success when deleting hook" do
- delete api("/projects/#{project.id}/hooks/#{hook.id}", user)
- response.status.should == 200
- end
-
- it "should return success when deleting non existent hook" do
- delete api("/projects/#{project.id}/hooks/42", user)
- response.status.should == 200
- end
-
- it "should return a 405 error if hook id not given" do
- delete api("/projects/#{project.id}/hooks", user)
- response.status.should == 405
- end
- end
-
describe "GET /projects/:id/snippets" do
it "should return an array of project snippets" do
get api("/projects/#{project.id}/snippets", user)
@@ -628,10 +590,10 @@ describe API::API do
describe :fork_admin do
let(:project_fork_target) { create(:project) }
- let(:project_fork_source) { create(:project, public: true) }
+ let(:project_fork_source) { create(:project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
describe "POST /projects/:id/fork/:forked_from_id" do
- let(:new_project_fork_source) { create(:project, public: true) }
+ let(:new_project_fork_source) { create(:project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
it "shouldn't available for non admin users" do
post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user)
@@ -700,8 +662,10 @@ describe API::API do
let!(:post) { create(:project, name: "#{query}_post", creator_id: user.id, namespace: user.namespace) }
let!(:pre_post) { create(:project, name: "pre_#{query}_post", creator_id: user.id, namespace: user.namespace) }
let!(:unfound) { create(:project, name: 'unfound', creator_id: user.id, namespace: user.namespace) }
- let!(:public) { create(:project, name: "another #{query}",public: true) }
- let!(:unfound_public) { create(:project, name: 'unfound public', public: true) }
+ let!(:internal) { create(:project, name: "internal #{query}", visibility_level: Gitlab::VisibilityLevel::INTERNAL) }
+ let!(:unfound_internal) { create(:project, name: 'unfound internal', visibility_level: Gitlab::VisibilityLevel::INTERNAL) }
+ let!(:public) { create(:project, name: "public #{query}", visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
+ let!(:unfound_public) { create(:project, name: 'unfound public', visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
context "when unauthenticated" do
it "should return authentication error" do
@@ -715,7 +679,7 @@ describe API::API do
get api("/projects/search/#{query}",user)
response.status.should == 200
json_response.should be_an Array
- json_response.size.should == 5
+ json_response.size.should == 6
json_response.each {|project| project['name'].should =~ /.*query.*/}
end
end
@@ -725,8 +689,8 @@ describe API::API do
get api("/projects/search/#{query}", user2)
response.status.should == 200
json_response.should be_an Array
- json_response.size.should == 1
- json_response.first['name'].should == "another #{query}"
+ json_response.size.should == 2
+ json_response.each {|project| project['name'].should =~ /(internal|public) query/}
end
end
end
diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb
index 9649c4d09c8..f73ac4372b2 100644
--- a/spec/requests/api/repositories_spec.rb
+++ b/spec/requests/api/repositories_spec.rb
@@ -1,4 +1,5 @@
require 'spec_helper'
+require 'mime/types'
describe API::API do
include ApiHelpers
@@ -225,11 +226,36 @@ describe API::API do
end
end
- describe "GET /projects/:id/repository/archive/:sha" do
+ describe "GET /projects/:id/repository/raw_blobs/:sha" do
+ it "should get the raw file contents" do
+ get api("/projects/#{project.id}/repository/raw_blobs/d1aff2896d99d7acc4d9780fbb716b113c45ecf7", user)
+ response.status.should == 200
+ end
+ end
+
+ describe "GET /projects/:id/repository/archive(.:format)?:sha" do
it "should get the archive" do
get api("/projects/#{project.id}/repository/archive", user)
+ repo_name = project.repository.name.gsub("\.git", "")
+ response.status.should == 200
+ response.headers['Content-Disposition'].should =~ /filename\=\"#{repo_name}\-[^\.]+\.tar.gz\"/
+ response.content_type.should == MIME::Types.type_for('file.tar.gz').first.content_type
+ end
+
+ it "should get the archive.zip" do
+ get api("/projects/#{project.id}/repository/archive.zip", user)
+ repo_name = project.repository.name.gsub("\.git", "")
+ response.status.should == 200
+ response.headers['Content-Disposition'].should =~ /filename\=\"#{repo_name}\-[^\.]+\.zip\"/
+ response.content_type.should == MIME::Types.type_for('file.zip').first.content_type
+ end
+
+ it "should get the archive.tar.bz2" do
+ get api("/projects/#{project.id}/repository/archive.tar.bz2", user)
+ repo_name = project.repository.name.gsub("\.git", "")
response.status.should == 200
- response.content_type.should == 'application/x-gzip'
+ response.headers['Content-Disposition'].should =~ /filename\=\"#{repo_name}\-[^\.]+\.tar.bz2\"/
+ response.content_type.should == MIME::Types.type_for('file.tar.bz2').first.content_type
end
it "should return 404 for invalid sha" do
diff --git a/spec/requests/api/session_spec.rb b/spec/requests/api/session_spec.rb
index 0fd90c567e0..668007dc29f 100644
--- a/spec/requests/api/session_spec.rb
+++ b/spec/requests/api/session_spec.rb
@@ -8,7 +8,7 @@ describe API::API do
describe "POST /session" do
context "when valid password" do
it "should return private token" do
- post api("/session"), email: user.email, password: '123456'
+ post api("/session"), email: user.email, password: '12345678'
response.status.should == 201
json_response['email'].should == user.email
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index 5597f08d186..97f7392e50a 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -130,6 +130,14 @@ describe Projects::RepositoriesController, "routing" do
get("/gitlab/gitlabhq/repository/archive").should route_to('projects/repositories#archive', project_id: 'gitlab/gitlabhq')
end
+ it "to #archive format:zip" do
+ get("/gitlab/gitlabhq/repository/archive.zip").should route_to('projects/repositories#archive', project_id: 'gitlab/gitlabhq', format: 'zip')
+ end
+
+ it "to #archive format:tar.bz2" do
+ get("/gitlab/gitlabhq/repository/archive.tar.bz2").should route_to('projects/repositories#archive', project_id: 'gitlab/gitlabhq', format: 'tar.bz2')
+ end
+
it "to #show" do
get("/gitlab/gitlabhq/repository").should route_to('projects/repositories#show', project_id: 'gitlab/gitlabhq')
end
diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb
index 1b1d19d26b1..1af052d8739 100644
--- a/spec/routing/routing_spec.rb
+++ b/spec/routing/routing_spec.rb
@@ -185,6 +185,13 @@ describe Profiles::KeysController, "routing" do
end
end
+# profile_avatar DELETE /profile/avatar(.:format) profiles/avatars#destroy
+describe Profiles::AvatarsController, "routing" do
+ it "to #destroy" do
+ delete("/profile/avatar").should route_to('profiles/avatars#destroy')
+ end
+end
+
# dashboard GET /dashboard(.:format) dashboard#show
# dashboard_issues GET /dashboard/issues(.:format) dashboard#issues
# dashboard_merge_requests GET /dashboard/merge_requests(.:format) dashboard#merge_requests
diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb
index 2870f59195a..b46022fb2da 100644
--- a/spec/services/git_push_service_spec.rb
+++ b/spec/services/git_push_service_spec.rb
@@ -74,38 +74,19 @@ describe GitPushService do
end
describe "Web Hooks" do
- context "with web hooks" do
- before do
- @project_hook = create(:project_hook)
- @project_hook_2 = create(:project_hook)
- project.hooks << [@project_hook, @project_hook_2]
-
- stub_request(:post, @project_hook.url)
- stub_request(:post, @project_hook_2.url)
- end
-
- it "executes multiple web hook" do
- @project_hook.should_receive(:async_execute).once
- @project_hook_2.should_receive(:async_execute).once
-
- service.execute(project, user, @oldrev, @newrev, @ref)
- end
- end
-
context "execute web hooks" do
- before do
- @project_hook = create(:project_hook)
- project.hooks << [@project_hook]
- stub_request(:post, @project_hook.url)
- end
-
it "when pushing a branch for the first time" do
- @project_hook.should_receive(:async_execute)
+ project.should_receive(:execute_hooks)
service.execute(project, user, @blankrev, 'newrev', 'refs/heads/master')
end
+ it "when pushing new commits to existing branch" do
+ project.should_receive(:execute_hooks)
+ service.execute(project, user, 'oldrev', 'newrev', 'refs/heads/master')
+ end
+
it "when pushing tags" do
- @project_hook.should_not_receive(:async_execute)
+ project.should_not_receive(:execute_hooks)
service.execute(project, user, 'newrev', 'newrev', 'refs/tags/v1.0.0')
end
end
diff --git a/spec/support/login_helpers.rb b/spec/support/login_helpers.rb
index 025534a900d..cc0ec2f4e3d 100644
--- a/spec/support/login_helpers.rb
+++ b/spec/support/login_helpers.rb
@@ -16,7 +16,7 @@ module LoginHelpers
def login_with(user)
visit new_user_session_path
fill_in "user_login", with: user.email
- fill_in "user_password", with: "123456"
+ fill_in "user_password", with: "12345678"
click_button "Sign in"
Thread.current[:current_user] = user
end
diff --git a/spec/support/mentionable_shared_examples.rb b/spec/support/mentionable_shared_examples.rb
index a7f189777c8..948fff27b89 100644
--- a/spec/support/mentionable_shared_examples.rb
+++ b/spec/support/mentionable_shared_examples.rb
@@ -12,7 +12,7 @@ def common_mentionable_setup
let(:mentioned_issue) { create :issue, project: mproject }
let(:other_issue) { create :issue, project: mproject }
let(:mentioned_mr) { create :merge_request, target_project: mproject, source_branch: 'different' }
- let(:mentioned_commit) { mock('commit', sha: '1234567890abcdef').as_null_object }
+ let(:mentioned_commit) { double('commit', sha: '1234567890abcdef').as_null_object }
# Override to add known commits to the repository stub.
let(:extra_commits) { [] }
@@ -30,7 +30,7 @@ def common_mentionable_setup
commitmap = { '123456' => mentioned_commit }
extra_commits.each { |c| commitmap[c.sha[0..5]] = c }
- repo = mock('repository')
+ repo = double('repository')
repo.stub(:commit) { |sha| commitmap[sha] }
mproject.stub(repository: repo)
diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb
index 16e10b1a62b..e2bc2a5d7dd 100644
--- a/spec/support/test_env.rb
+++ b/spec/support/test_env.rb
@@ -45,6 +45,7 @@ module TestEnv
def disable_mailer
NotificationService.any_instance.stub(mailer: double.as_null_object)
end
+
def enable_mailer
NotificationService.any_instance.unstub(:mailer)
end
@@ -68,7 +69,8 @@ module TestEnv
remove_repository: true,
update_repository_head: true,
add_key: true,
- remove_key: true
+ remove_key: true,
+ version: '6.3.0'
)
Gitlab::Satellite::Satellite.any_instance.stub(
@@ -96,6 +98,15 @@ module TestEnv
FileUtils.rm_rf File.join(testing_path(), "#{name}.wiki.git")
end
+ def reset_satellite_dir
+ setup_stubs
+ FileUtils.cd(seed_satellite_path) do
+ `git reset --hard --quiet`
+ `git clean -fx`
+ `git checkout --quiet origin/master`
+ end
+ end
+
# Create a repo and it's satellite
def create_repo(namespace, name)
setup_stubs
diff --git a/vendor/assets/javascripts/ace-src-noconflict/mode-diff.js b/vendor/assets/javascripts/ace-src-noconflict/mode-diff.js
index 75e26cc7056..8d1e7cee096 100644
--- a/vendor/assets/javascripts/ace-src-noconflict/mode-diff.js
+++ b/vendor/assets/javascripts/ace-src-noconflict/mode-diff.js
@@ -66,7 +66,7 @@ var DiffHighlightRules = function() {
"regex": "^(?:\\*{15}|={67}|-{3}|\\+{3})$",
"token": "punctuation.definition.separator.diff",
"name": "keyword"
- }, { //diff.range.unified
+ }, { //diff.range.inline
"regex": "^(@@)(\\s*.+?\\s*)(@@)(.*)$",
"token": [
"constant",